From b0baa489fa761be0bb3c96d1d4e2888322ed4c83 Mon Sep 17 00:00:00 2001 From: Frank Spierings Date: Fri, 30 Aug 2024 22:44:54 +0200 Subject: [PATCH] jvm: Add preliminary support for OpenJDK 17 on Windows (#328) This change will make the jvm.js code be able to bridge Java OpenJDK 17 on Windows. This was tested on Windows 10, using jdk-17.0.11+9 from https://learn.microsoft.com/en-gb/java/openjdk/download and the separately downloaded and extracted debug symbols. The code is not pretty, but I've tried to keep it as close to the original as possible. Note that I discovered a bug while performing the tests; https://github.com/frida/frida-gum/issues/811. This means that the target process needs to be restarted after detaching Frida, for the Java bridge (resolving of Symbols) to work. This code currently does not work with OpenJDK21! This code requires the 'manual' placement of the debug symbols, otherwise jvm.js will be unable to find the functions it requires. --- lib/jvm.js | 395 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 222 insertions(+), 173 deletions(-) diff --git a/lib/jvm.js b/lib/jvm.js index 6a2daa1..15cc47b 100644 --- a/lib/jvm.js +++ b/lib/jvm.js @@ -51,184 +51,226 @@ function _getApi () { flavor: 'jvm' }; - const pending = [{ - module: vmModule.path, - functions: { - JNI_GetCreatedJavaVMs: ['JNI_GetCreatedJavaVMs', 'int', ['pointer', 'int', 'pointer']], - - _ZN6Method4sizeEb: ['Method::size', 'int', ['int']], - _ZN6Method19set_native_functionEPhb: ['Method::set_native_function', 'void', ['pointer', 'pointer', 'int']], - _ZN6Method21clear_native_functionEv: ['Method::clear_native_function', 'void', ['pointer']], - // JDK >= 17 - _ZN6Method24restore_unshareable_infoEP10JavaThread: ['Method::restore_unshareable_info', 'void', ['pointer', 'pointer']], - // JDK < 17 - _ZN6Method24restore_unshareable_infoEP6Thread: ['Method::restore_unshareable_info', 'void', ['pointer', 'pointer']], - _ZN6Method10jmethod_idEv: ['Method::jmethod_id', 'pointer', ['pointer']], - _ZN6Method10clear_codeEv: function (address) { - const clearCode = new NativeFunction(address, 'void', ['pointer'], nativeFunctionOptions); - this['Method::clear_code'] = function (thisPtr) { - clearCode(thisPtr); - }; - }, - _ZN6Method10clear_codeEb: function (address) { - const clearCode = new NativeFunction(address, 'void', ['pointer', 'int'], nativeFunctionOptions); - const lock = 0; - this['Method::clear_code'] = function (thisPtr) { - clearCode(thisPtr, lock); - }; - }, - - // JDK >= 13 - _ZN18VM_RedefineClasses19mark_dependent_codeEP13InstanceKlass: ['VM_RedefineClasses::mark_dependent_code', 'void', ['pointer', 'pointer']], - _ZN18VM_RedefineClasses20flush_dependent_codeEv: ['VM_RedefineClasses::flush_dependent_code', 'void', []], - // JDK < 13 - _ZN18VM_RedefineClasses20flush_dependent_codeEP13InstanceKlassP6Thread: ['VM_RedefineClasses::flush_dependent_code', 'void', ['pointer', 'pointer', 'pointer']], - // JDK < 10 - _ZN18VM_RedefineClasses20flush_dependent_codeE19instanceKlassHandleP6Thread: ['VM_RedefineClasses::flush_dependent_code', 'void', ['pointer', 'pointer', 'pointer']], - - _ZN19ResolvedMethodTable21adjust_method_entriesEPb: ['ResolvedMethodTable::adjust_method_entries', 'void', ['pointer']], - // JDK < 10 - _ZN15MemberNameTable21adjust_method_entriesEP13InstanceKlassPb: ['MemberNameTable::adjust_method_entries', 'void', ['pointer', 'pointer', 'pointer']], - - _ZN17ConstantPoolCache21adjust_method_entriesEPb: function (address) { - const adjustMethod = new NativeFunction(address, 'void', ['pointer', 'pointer'], nativeFunctionOptions); - this['ConstantPoolCache::adjust_method_entries'] = function (thisPtr, holderPtr, tracePtr) { - adjustMethod(thisPtr, tracePtr); - }; - }, - // JDK < 13 - _ZN17ConstantPoolCache21adjust_method_entriesEP13InstanceKlassPb: function (address) { - const adjustMethod = new NativeFunction(address, 'void', ['pointer', 'pointer', 'pointer'], nativeFunctionOptions); - this['ConstantPoolCache::adjust_method_entries'] = function (thisPtr, holderPtr, tracePtr) { - adjustMethod(thisPtr, holderPtr, tracePtr); - }; - }, - - _ZN20ClassLoaderDataGraph10classes_doEP12KlassClosure: ['ClassLoaderDataGraph::classes_do', 'void', ['pointer']], - _ZN20ClassLoaderDataGraph22clean_deallocate_listsEb: ['ClassLoaderDataGraph::clean_deallocate_lists', 'void', ['int']], - - _ZN10JavaThread27thread_from_jni_environmentEP7JNIEnv_: ['JavaThread::thread_from_jni_environment', 'pointer', ['pointer']], - - _ZN8VMThread7executeEP12VM_Operation: ['VMThread::execute', 'void', ['pointer']], - - _ZN11OopMapCache22flush_obsolete_entriesEv: ['OopMapCache::flush_obsolete_entries', 'void', ['pointer']], - - _ZN14NMethodSweeper11force_sweepEv: ['NMethodSweeper::force_sweep', 'void', []], - _ZN14NMethodSweeper16sweep_code_cacheEv: ['NMethodSweeper::sweep_code_cache', 'void', []], - _ZN14NMethodSweeper17sweep_in_progressEv: ['NMethodSweeper::sweep_in_progress', 'bool', []], - - JVM_Sleep: ['JVM_Sleep', 'void', ['pointer', 'pointer', 'long']] - }, - variables: { - // JDK <= 9 - _ZN18VM_RedefineClasses14_the_class_oopE: function (address) { - this.redefineClass = address; - }, - // 9 < JDK < 13 - _ZN18VM_RedefineClasses10_the_classE: function (address) { - this.redefineClass = address; - }, - // JDK < 13 - _ZN18VM_RedefineClasses25AdjustCpoolCacheAndVtable8do_klassEP5Klass: function (address) { - this.doKlass = address; - }, - // JDK >= 13 - _ZN18VM_RedefineClasses22AdjustAndCleanMetadata8do_klassEP5Klass: function (address) { - this.doKlass = address; - }, - _ZTV18VM_RedefineClasses: function (address) { - this.vtableRedefineClasses = address; - }, - _ZN18VM_RedefineClasses4doitEv: function (address) { - this.redefineClassesDoIt = address; - }, - _ZN18VM_RedefineClasses13doit_prologueEv: function (address) { - this.redefineClassesDoItPrologue = address; - }, - _ZN18VM_RedefineClasses13doit_epilogueEv: function (address) { - this.redefineClassesDoItEpilogue = address; - }, - _ZN18VM_RedefineClassesD0Ev: function (address) { - this.redefineClassesDispose0 = address; - }, - _ZN18VM_RedefineClassesD1Ev: function (address) { - this.redefineClassesDispose1 = address; - }, - _ZNK18VM_RedefineClasses26allow_nested_vm_operationsEv: function (address) { - this.redefineClassesAllow = address; - }, - _ZNK18VM_RedefineClasses14print_on_errorEP12outputStream: function (address) { - this.redefineClassesOnError = address; - }, - - // JDK >= 17 - _ZN13InstanceKlass33create_new_default_vtable_indicesEiP10JavaThread: function (address) { - this.createNewDefaultVtableIndices = address; - }, - // JDK < 17 - _ZN13InstanceKlass33create_new_default_vtable_indicesEiP6Thread: function (address) { - this.createNewDefaultVtableIndices = address; - }, - - _ZN19Abstract_VM_Version19jre_release_versionEv: function (address) { - const getVersion = new NativeFunction(address, 'pointer', [], nativeFunctionOptions); - const versionS = getVersion().readCString(); - this.version = versionS.startsWith('1.8') - ? 8 - : versionS.startsWith('9.') - ? 9 - : parseInt(versionS.slice(0, 2), 10); - this.versionS = versionS; - }, - - _ZN14NMethodSweeper11_traversalsE: function (address) { - this.traversals = address; - }, - _ZN14NMethodSweeper21_sweep_fractions_leftE: function (address) { - this.fractions = address; - }, - _ZN14NMethodSweeper13_should_sweepE: function (address) { - this.shouldSweep = address; - } - }, - optionals: [ - '_ZN6Method24restore_unshareable_infoEP10JavaThread', - '_ZN6Method24restore_unshareable_infoEP6Thread', - '_ZN6Method10clear_codeEv', - '_ZN6Method10clear_codeEb', + const pending = Process.platform === 'windows' + ? [{ + module: vmModule.path, + functions: { + JNI_GetCreatedJavaVMs: ['JNI_GetCreatedJavaVMs', 'int', ['pointer', 'int', 'pointer']], + JVM_Sleep: ['JVM_Sleep', 'void', ['pointer', 'pointer', 'long']], + 'VMThread::execute': ['VMThread::execute', 'void', ['pointer']], + 'Method::size': ['Method::size', 'int', ['int']], + 'Method::set_native_function': ['Method::set_native_function', 'void', ['pointer', 'pointer', 'int']], + 'Method::clear_native_function': ['Method::clear_native_function', 'void', ['pointer']], + 'Method::jmethod_id': ['Method::jmethod_id', 'pointer', ['pointer']], + 'ClassLoaderDataGraph::classes_do': ['ClassLoaderDataGraph::classes_do', 'void', ['pointer']], + 'NMethodSweeper::sweep_code_cache': ['NMethodSweeper::sweep_code_cache', 'void', []], + 'OopMapCache::flush_obsolete_entries': ['OopMapCache::flush_obsolete_entries', 'void', ['pointer']] + }, + variables: { + 'VM_RedefineClasses::`vftable\'': function (address) { + this.vtableRedefineClasses = address; + }, + 'VM_RedefineClasses::doit': function (address) { + this.redefineClassesDoIt = address; + }, + 'VM_RedefineClasses::doit_prologue': function (address) { + this.redefineClassesDoItPrologue = address; + }, + 'VM_RedefineClasses::doit_epilogue': function (address) { + this.redefineClassesDoItEpilogue = address; + }, + 'VM_RedefineClasses::allow_nested_vm_operations': function (address) { + this.redefineClassesAllow = address; + }, + 'NMethodSweeper::_traversals': function (address) { + this.traversals = address; + }, + 'NMethodSweeper::_should_sweep': function (address) { + this.shouldSweep = address; + } + }, + optionals: [ + ] + }] + // If platform is not Windows + : [{ + module: vmModule.path, + functions: { + JNI_GetCreatedJavaVMs: ['JNI_GetCreatedJavaVMs', 'int', ['pointer', 'int', 'pointer']], + + _ZN6Method4sizeEb: ['Method::size', 'int', ['int']], + _ZN6Method19set_native_functionEPhb: ['Method::set_native_function', 'void', ['pointer', 'pointer', 'int']], + _ZN6Method21clear_native_functionEv: ['Method::clear_native_function', 'void', ['pointer']], + // JDK >= 17 + _ZN6Method24restore_unshareable_infoEP10JavaThread: ['Method::restore_unshareable_info', 'void', ['pointer', 'pointer']], + // JDK < 17 + _ZN6Method24restore_unshareable_infoEP6Thread: ['Method::restore_unshareable_info', 'void', ['pointer', 'pointer']], + _ZN6Method10jmethod_idEv: ['Method::jmethod_id', 'pointer', ['pointer']], + _ZN6Method10clear_codeEv: function (address) { + const clearCode = new NativeFunction(address, 'void', ['pointer'], nativeFunctionOptions); + this['Method::clear_code'] = function (thisPtr) { + clearCode(thisPtr); + }; + }, + _ZN6Method10clear_codeEb: function (address) { + const clearCode = new NativeFunction(address, 'void', ['pointer', 'int'], nativeFunctionOptions); + const lock = 0; + this['Method::clear_code'] = function (thisPtr) { + clearCode(thisPtr, lock); + }; + }, + + // JDK >= 13 + _ZN18VM_RedefineClasses19mark_dependent_codeEP13InstanceKlass: ['VM_RedefineClasses::mark_dependent_code', 'void', ['pointer', 'pointer']], + _ZN18VM_RedefineClasses20flush_dependent_codeEv: ['VM_RedefineClasses::flush_dependent_code', 'void', []], + // JDK < 13 + _ZN18VM_RedefineClasses20flush_dependent_codeEP13InstanceKlassP6Thread: ['VM_RedefineClasses::flush_dependent_code', 'void', ['pointer', 'pointer', 'pointer']], + // JDK < 10 + _ZN18VM_RedefineClasses20flush_dependent_codeE19instanceKlassHandleP6Thread: ['VM_RedefineClasses::flush_dependent_code', 'void', ['pointer', 'pointer', 'pointer']], + + _ZN19ResolvedMethodTable21adjust_method_entriesEPb: ['ResolvedMethodTable::adjust_method_entries', 'void', ['pointer']], + // JDK < 10 + _ZN15MemberNameTable21adjust_method_entriesEP13InstanceKlassPb: ['MemberNameTable::adjust_method_entries', 'void', ['pointer', 'pointer', 'pointer']], + + _ZN17ConstantPoolCache21adjust_method_entriesEPb: function (address) { + const adjustMethod = new NativeFunction(address, 'void', ['pointer', 'pointer'], nativeFunctionOptions); + this['ConstantPoolCache::adjust_method_entries'] = function (thisPtr, holderPtr, tracePtr) { + adjustMethod(thisPtr, tracePtr); + }; + }, + // JDK < 13 + _ZN17ConstantPoolCache21adjust_method_entriesEP13InstanceKlassPb: function (address) { + const adjustMethod = new NativeFunction(address, 'void', ['pointer', 'pointer', 'pointer'], nativeFunctionOptions); + this['ConstantPoolCache::adjust_method_entries'] = function (thisPtr, holderPtr, tracePtr) { + adjustMethod(thisPtr, holderPtr, tracePtr); + }; + }, + + _ZN20ClassLoaderDataGraph10classes_doEP12KlassClosure: ['ClassLoaderDataGraph::classes_do', 'void', ['pointer']], + _ZN20ClassLoaderDataGraph22clean_deallocate_listsEb: ['ClassLoaderDataGraph::clean_deallocate_lists', 'void', ['int']], + + _ZN10JavaThread27thread_from_jni_environmentEP7JNIEnv_: ['JavaThread::thread_from_jni_environment', 'pointer', ['pointer']], + + _ZN8VMThread7executeEP12VM_Operation: ['VMThread::execute', 'void', ['pointer']], + + _ZN11OopMapCache22flush_obsolete_entriesEv: ['OopMapCache::flush_obsolete_entries', 'void', ['pointer']], + + _ZN14NMethodSweeper11force_sweepEv: ['NMethodSweeper::force_sweep', 'void', []], + _ZN14NMethodSweeper16sweep_code_cacheEv: ['NMethodSweeper::sweep_code_cache', 'void', []], + _ZN14NMethodSweeper17sweep_in_progressEv: ['NMethodSweeper::sweep_in_progress', 'bool', []], + + JVM_Sleep: ['JVM_Sleep', 'void', ['pointer', 'pointer', 'long']] + }, + variables: { + // JDK <= 9 + _ZN18VM_RedefineClasses14_the_class_oopE: function (address) { + this.redefineClass = address; + }, + // 9 < JDK < 13 + _ZN18VM_RedefineClasses10_the_classE: function (address) { + this.redefineClass = address; + }, + // JDK < 13 + _ZN18VM_RedefineClasses25AdjustCpoolCacheAndVtable8do_klassEP5Klass: function (address) { + this.doKlass = address; + }, + // JDK >= 13 + _ZN18VM_RedefineClasses22AdjustAndCleanMetadata8do_klassEP5Klass: function (address) { + this.doKlass = address; + }, + _ZTV18VM_RedefineClasses: function (address) { + this.vtableRedefineClasses = address; + }, + _ZN18VM_RedefineClasses4doitEv: function (address) { + this.redefineClassesDoIt = address; + }, + _ZN18VM_RedefineClasses13doit_prologueEv: function (address) { + this.redefineClassesDoItPrologue = address; + }, + _ZN18VM_RedefineClasses13doit_epilogueEv: function (address) { + this.redefineClassesDoItEpilogue = address; + }, + _ZN18VM_RedefineClassesD0Ev: function (address) { + this.redefineClassesDispose0 = address; + }, + _ZN18VM_RedefineClassesD1Ev: function (address) { + this.redefineClassesDispose1 = address; + }, + _ZNK18VM_RedefineClasses26allow_nested_vm_operationsEv: function (address) { + this.redefineClassesAllow = address; + }, + _ZNK18VM_RedefineClasses14print_on_errorEP12outputStream: function (address) { + this.redefineClassesOnError = address; + }, + + // JDK >= 17 + _ZN13InstanceKlass33create_new_default_vtable_indicesEiP10JavaThread: function (address) { + this.createNewDefaultVtableIndices = address; + }, + // JDK < 17 + _ZN13InstanceKlass33create_new_default_vtable_indicesEiP6Thread: function (address) { + this.createNewDefaultVtableIndices = address; + }, + + _ZN19Abstract_VM_Version19jre_release_versionEv: function (address) { + const getVersion = new NativeFunction(address, 'pointer', [], nativeFunctionOptions); + const versionS = getVersion().readCString(); + this.version = versionS.startsWith('1.8') + ? 8 + : versionS.startsWith('9.') + ? 9 + : parseInt(versionS.slice(0, 2), 10); + this.versionS = versionS; + }, + + _ZN14NMethodSweeper11_traversalsE: function (address) { + this.traversals = address; + }, + _ZN14NMethodSweeper21_sweep_fractions_leftE: function (address) { + this.fractions = address; + }, + _ZN14NMethodSweeper13_should_sweepE: function (address) { + this.shouldSweep = address; + } + }, + optionals: [ + '_ZN6Method24restore_unshareable_infoEP10JavaThread', + '_ZN6Method24restore_unshareable_infoEP6Thread', + '_ZN6Method10clear_codeEv', + '_ZN6Method10clear_codeEb', - '_ZN18VM_RedefineClasses19mark_dependent_codeEP13InstanceKlass', - '_ZN18VM_RedefineClasses20flush_dependent_codeEv', - '_ZN18VM_RedefineClasses20flush_dependent_codeEP13InstanceKlassP6Thread', - '_ZN18VM_RedefineClasses20flush_dependent_codeE19instanceKlassHandleP6Thread', + '_ZN18VM_RedefineClasses19mark_dependent_codeEP13InstanceKlass', + '_ZN18VM_RedefineClasses20flush_dependent_codeEv', + '_ZN18VM_RedefineClasses20flush_dependent_codeEP13InstanceKlassP6Thread', + '_ZN18VM_RedefineClasses20flush_dependent_codeE19instanceKlassHandleP6Thread', - '_ZN19ResolvedMethodTable21adjust_method_entriesEPb', - '_ZN15MemberNameTable21adjust_method_entriesEP13InstanceKlassPb', + '_ZN19ResolvedMethodTable21adjust_method_entriesEPb', + '_ZN15MemberNameTable21adjust_method_entriesEP13InstanceKlassPb', - '_ZN17ConstantPoolCache21adjust_method_entriesEPb', - '_ZN17ConstantPoolCache21adjust_method_entriesEP13InstanceKlassPb', + '_ZN17ConstantPoolCache21adjust_method_entriesEPb', + '_ZN17ConstantPoolCache21adjust_method_entriesEP13InstanceKlassPb', - '_ZN20ClassLoaderDataGraph22clean_deallocate_listsEb', + '_ZN20ClassLoaderDataGraph22clean_deallocate_listsEb', - '_ZN10JavaThread27thread_from_jni_environmentEP7JNIEnv_', + '_ZN10JavaThread27thread_from_jni_environmentEP7JNIEnv_', - '_ZN14NMethodSweeper11force_sweepEv', - '_ZN14NMethodSweeper17sweep_in_progressEv', + '_ZN14NMethodSweeper11force_sweepEv', + '_ZN14NMethodSweeper17sweep_in_progressEv', - '_ZN18VM_RedefineClasses14_the_class_oopE', - '_ZN18VM_RedefineClasses10_the_classE', - '_ZN18VM_RedefineClasses25AdjustCpoolCacheAndVtable8do_klassEP5Klass', - '_ZN18VM_RedefineClasses22AdjustAndCleanMetadata8do_klassEP5Klass', - '_ZN18VM_RedefineClassesD0Ev', - '_ZN18VM_RedefineClassesD1Ev', - '_ZNK18VM_RedefineClasses14print_on_errorEP12outputStream', + '_ZN18VM_RedefineClasses14_the_class_oopE', + '_ZN18VM_RedefineClasses10_the_classE', + '_ZN18VM_RedefineClasses25AdjustCpoolCacheAndVtable8do_klassEP5Klass', + '_ZN18VM_RedefineClasses22AdjustAndCleanMetadata8do_klassEP5Klass', + '_ZN18VM_RedefineClassesD0Ev', + '_ZN18VM_RedefineClassesD1Ev', + '_ZNK18VM_RedefineClasses14print_on_errorEP12outputStream', - '_ZN13InstanceKlass33create_new_default_vtable_indicesEiP10JavaThread', - '_ZN13InstanceKlass33create_new_default_vtable_indicesEiP6Thread', + '_ZN13InstanceKlass33create_new_default_vtable_indicesEiP10JavaThread', + '_ZN13InstanceKlass33create_new_default_vtable_indicesEiP6Thread', - '_ZN14NMethodSweeper21_sweep_fractions_leftE' - ] - }]; + '_ZN14NMethodSweeper21_sweep_fractions_leftE' + ] + }]; const missing = []; @@ -294,10 +336,17 @@ function _getApi () { } temporaryApi.vm = vms.readPointer(); - const allocatorFunctions = { - $new: ['_Znwm', 'pointer', ['ulong']], - $delete: ['_ZdlPv', 'void', ['pointer']] - }; + const allocatorFunctions = Process.platform === 'windows' + ? { + $new: ['??2@YAPEAX_K@Z', 'pointer', ['ulong']], + $delete: ['??3@YAXPEAX@Z', 'void', ['pointer']] + } + // If platform is not Windows + : { + $new: ['_Znwm', 'pointer', ['ulong']], + $delete: ['_ZdlPv', 'void', ['pointer']] + }; + for (const [name, [rawName, retType, argTypes]] of Object.entries(allocatorFunctions)) { let address = Module.findExportByName(null, rawName); if (address === null) {