From 5d47892ea653fea57d3dfff9e7f9aedfe7719a0e Mon Sep 17 00:00:00 2001 From: matbrik <6418737+matbrik@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:11:29 +0000 Subject: [PATCH 01/10] android: alternative offset to ExceptionClear in libart (https://github.com/frida/frida/issues/2958)(https://github.com/frida/frida-java-bridge/issues/336) --- lib/android.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/android.js b/lib/android.js index f5dcfbe..03ba488 100644 --- a/lib/android.js +++ b/lib/android.js @@ -44,6 +44,7 @@ const X86_JMP_MAX_DISTANCE = 0x7fffbfff; const ARM64_ADRP_MAX_DISTANCE = 0xfffff000; const ENV_VTABLE_OFFSET_EXCEPTION_CLEAR = 17 * pointerSize; +const ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT = 250 * pointerSize; const ENV_VTABLE_OFFSET_FATAL_ERROR = 18 * pointerSize; const DVM_JNI_ENV_OFFSET_SELF = 12; @@ -259,8 +260,8 @@ function _getApi () { // Android < 6 for cloneArtMethod() _ZN3art6Thread14CurrentFromGdbEv: ['art::Thread::CurrentFromGdb', 'pointer', []], - _ZN3art6mirror6Object5CloneEPNS_6ThreadE: function (address) { - this['art::mirror::Object::Clone'] = new NativeFunction(address, 'pointer', ['pointer', 'pointer'], nativeFunctionOptions); + _ZN3art6mirror6Object5CloneEPNS_6ThreadE: function (address) { + this['art::mirror::Object::Clone'] = new NativeFunction(address, 'pointer', ['pointer', 'pointer'], nativeFunctionOptions); }, _ZN3art6mirror6Object5CloneEPNS_6ThreadEm: function (address) { const clone = new NativeFunction(address, 'pointer', ['pointer', 'pointer', 'pointer'], nativeFunctionOptions); @@ -3926,9 +3927,17 @@ const threadStateTransitionRecompilers = { function makeArtThreadStateTransitionImpl (vm, env, callback) { const envVtable = env.handle.readPointer(); - const exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer(); + let exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer(); const nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer(); - + if (Process.arch === 'arm64') { + const base = exceptionClearImpl; + const size = 0x150; + const pattern = "c0 03 5f d6"; ///ret + var res = Memory.scanSync(base, size, pattern) + if(res.length > 0){ + exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT).readPointer(); + } + } const recompile = threadStateTransitionRecompilers[Process.arch]; if (recompile === undefined) { throw new Error('Not yet implemented for ' + Process.arch); From a44980e0a15f7c3172332027e3e6b0c333c28ce0 Mon Sep 17 00:00:00 2001 From: matbrik <6418737+matbrik@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:32:59 +0000 Subject: [PATCH 02/10] fix lint errors --- lib/android.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/android.js b/lib/android.js index 03ba488..6db17bd 100644 --- a/lib/android.js +++ b/lib/android.js @@ -260,8 +260,8 @@ function _getApi () { // Android < 6 for cloneArtMethod() _ZN3art6Thread14CurrentFromGdbEv: ['art::Thread::CurrentFromGdb', 'pointer', []], - _ZN3art6mirror6Object5CloneEPNS_6ThreadE: function (address) { - this['art::mirror::Object::Clone'] = new NativeFunction(address, 'pointer', ['pointer', 'pointer'], nativeFunctionOptions); + _ZN3art6mirror6Object5CloneEPNS_6ThreadE: function (address) { + this['art::mirror::Object::Clone'] = new NativeFunction(address, 'pointer', ['pointer', 'pointer'], nativeFunctionOptions); }, _ZN3art6mirror6Object5CloneEPNS_6ThreadEm: function (address) { const clone = new NativeFunction(address, 'pointer', ['pointer', 'pointer', 'pointer'], nativeFunctionOptions); @@ -3930,11 +3930,9 @@ function makeArtThreadStateTransitionImpl (vm, env, callback) { let exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer(); const nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer(); if (Process.arch === 'arm64') { - const base = exceptionClearImpl; const size = 0x150; - const pattern = "c0 03 5f d6"; ///ret - var res = Memory.scanSync(base, size, pattern) - if(res.length > 0){ + const pattern = 'c0 03 5f d6'; // ret + if (Memory.scanSync(exceptionClearImpl, size, pattern).length > 0) { exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT).readPointer(); } } From 96ee9c4c7e0e6b233c8434ac6a40f9dcebdba13d Mon Sep 17 00:00:00 2001 From: matbrik <6418737+matbrik@users.noreply.github.com> Date: Wed, 25 Sep 2024 09:52:37 +0000 Subject: [PATCH 03/10] android: handle change of signature of runFlip --- lib/android.js | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/lib/android.js b/lib/android.js index 6db17bd..1317e73 100644 --- a/lib/android.js +++ b/lib/android.js @@ -1894,29 +1894,23 @@ function ensureArtKnowsHowToHandleReplacementMethods (vm) { const apiLevel = getAndroidApiLevel(); - const mayUseCollector = (apiLevel > 28) - ? (type) => { - const impl = Module.findExportByName('libart.so', '_ZNK3art2gc4Heap15MayUseCollectorENS0_13CollectorTypeE'); - if (impl === null) { - return false; - } - return new NativeFunction(impl, 'bool', ['pointer', 'int'])(getApi().artHeap, type); - } - : () => false; - const kCollectorTypeCMC = 3; + let copyingPhase = null; + if (apiLevel > 28) { + copyingPhase = Module.findExportByName('libart.so', '_ZN3art2gc9collector17ConcurrentCopying12CopyingPhaseEv'); + } else if (apiLevel > 22) { + copyingPhase = Module.findExportByName('libart.so', '_ZN3art2gc9collector17ConcurrentCopying12MarkingPhaseEv'); + } + if (copyingPhase !== null) { + Interceptor.attach(copyingPhase, artController.hooks.Gc.copyingPhase); + } - if (mayUseCollector(kCollectorTypeCMC)) { - Interceptor.attach(Module.getExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_b'), artController.hooks.Gc.runFlip); - } else { - let copyingPhase = null; - if (apiLevel > 28) { - copyingPhase = Module.findExportByName('libart.so', '_ZN3art2gc9collector17ConcurrentCopying12CopyingPhaseEv'); - } else if (apiLevel > 22) { - copyingPhase = Module.findExportByName('libart.so', '_ZN3art2gc9collector17ConcurrentCopying12MarkingPhaseEv'); - } - if (copyingPhase !== null) { - Interceptor.attach(copyingPhase, artController.hooks.Gc.copyingPhase); - } + let runFlip = null; + runFlip = Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_b'); + if (runFlip === null) { + runFlip = Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0'); // api 35 + } + if (runFlip !== null) { + Interceptor.attach(runFlip, artController.hooks.Gc.runFlip); } } From 807c7f00689e4e506aa8711684e6ba431f719c66 Mon Sep 17 00:00:00 2001 From: matbrik <6418737+matbrik@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:08:42 +0000 Subject: [PATCH 04/10] fix stripped libart --- lib/android.js | 147 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 135 insertions(+), 12 deletions(-) diff --git a/lib/android.js b/lib/android.js index 1317e73..5483742 100644 --- a/lib/android.js +++ b/lib/android.js @@ -503,7 +503,9 @@ function _getApi () { if (temporaryApi['art::interpreter::GetNterpEntryPoint'] !== undefined) { temporaryApi.artNterpEntryPoint = temporaryApi['art::interpreter::GetNterpEntryPoint'](); } - + else { + temporaryApi.artNterpEntryPoint = findExecuteNterpImpl(); + } artController = makeArtController(vm); fixupArtQuickDeliverExceptionBug(temporaryApi); @@ -533,18 +535,116 @@ function _getApi () { return temporaryApi; } +function stringToBytesHex(str) { + let bytes = []; + for (let i = 0; i < str.length; i++) { + let byteHex = str.charCodeAt(i).toString(16).toUpperCase(); + if (byteHex.length === 1) { + byteHex = '0' + byteHex; + } + bytes.push(byteHex); + } + return bytes.join(' '); +} + +function findString(string, moduleName){ + const module = Process.findModuleByName(moduleName); + const ranges = module.enumerateRanges('r--'); + const pattern = stringToBytesHex(string); + return ranges.map((range) => { + const base = range.base; + const size = range.size; + const result = Memory.scanSync(base, size, pattern); + if (result.length>0) return result; + return; + }).filter((element => element != null)).flat(); +} + +function createAdd(address, register){ + return ((0x244<<12)+(address&0xfff)<<10)+(register<<5)+register; +} + +function getPattern(instr){ + return ptr(instr).toMatchPattern().replace('ff ff ff ff', ''); +} + +function findPatternInModule(pattern, moduleName){ + const module = Process.findModuleByName(moduleName); + const ranges = module.enumerateRanges('r-x'); + return ranges.map((range) => { + const base = range.base; + const size = range.size; + var result = Memory.scanSync(base, size, pattern); + if (result.length==0) return; + return result; + }).filter((element => element!=null)).flat(); +} + + +function findEnsurePlugIn(){ + const results = findString('libopenjdkjvmti.so', 'libart.so'); + const pattern = getPattern(createAdd(results[0].address,1)); + const pattern_results = findPatternInModule(pattern, 'libart.so'); + return pattern_results.map((match) => { + const possibleBranch = match.address.add(4); + const disasm = Instruction.parse(possibleBranch); + if (disasm.mnemonic=='b') + { + return new NativePointer(disasm.operands[0].value); + } + return; + }).filter((element => element!=null)).flat(); +} + +function findPrologue(address){ + for (let off = 0;; off += 4) { + let disasm = Instruction.parse(address.sub(0x4).sub(off)); + if (disasm.mnemonic === 'str') { + disasm = Instruction.parse(disasm.next); + if (disasm.mnemonic === 'stp') { + return disasm.address.sub(0x4); + } + } + } +} + +function findExecuteNterpImpl(){ + let artMethodCopyfrom = Module.findExportByName('libart.so', '_ZN3art9ArtMethod8CopyFromEPS0_NS_11PointerSizeE') + for (let off = 0; off<0x300; off+=4){ + let disasm = Instruction.parse(artMethodCopyfrom.add(off)); + let next_instr = Instruction.parse(artMethodCopyfrom.add(off).add(0x4)); + if (disasm.mnemonic === 'adrp' && next_instr.mnemonic === 'add'){ + const base = ptr(disasm.operands[1].value); + const offset = next_instr.operands[2].value; + const result = base.add(offset); + const dest = Instruction.parse(result); + if (dest.mnemonic === 'sub') + return result + } + } +} + function tryGetEnvJvmti (vm, runtime) { let env = null; vm.perform(() => { - const ensurePluginLoaded = new NativeFunction( - Module.getExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'), + let ensurePluginLoaded; + if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE')===null){ + // on some devices calling the ensurePluginLoaded make the app really slow so for is commented + return; + const candidates = findEnsurePlugIn(); + ensurePluginLoaded = new NativeFunction(candidates[0], + 'bool', + ['pointer', 'pointer', 'pointer']); + }else{ + ensurePluginLoaded = new NativeFunction(Module.getExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'), 'bool', ['pointer', 'pointer', 'pointer']); + } const errorPtr = Memory.alloc(pointerSize); const success = ensurePluginLoaded(runtime, Memory.allocUtf8String('libopenjdkjvmti.so'), errorPtr); if (!success) { - // FIXME: Avoid leaking error + // FIXME: Avoid leaking error return; } @@ -624,7 +724,7 @@ function _getArtRuntimeSpec (api) { const apiLevel = getAndroidApiLevel(); const codename = getAndroidCodename(); - const isApiLevel34OrApexEquivalent = Module.findExportByName('libart.so', '_ZN3art7AppInfo29GetPrimaryApkReferenceProfileEv') !== null; + const isApiLevel34OrApexEquivalent = Module.findExportByName('libart.so', '_ZN3art7AppInfo29GetPrimaryApkReferenceProfileEv') !== null || Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_') !== null; let spec = null; @@ -1853,6 +1953,25 @@ function instrumentArtQuickEntrypoints (vm) { }); } +function findDoCalls(){ + const results = findString('Invoking %s with bad arg %d', 'libart.so'); + const pattern = getPattern(createAdd(results[0].address,2)); + const pattern_results = findPatternInModule(pattern, 'libart.so'); + + return pattern_results.map((match) => { + const possibleBranch = match.address; + const instr = Instruction.parse(possibleBranch.sub(4)); + if (instr.mnemonic=='adrp') + { + if (new NativePointer(instr.operands[1].value).equals(results[0].address.and(0xfffffffffff000))) + { + return findPrologue(possibleBranch); + } + } + return + }).filter((element => element!=null)).flat(); +} + function instrumentArtMethodInvocationFromInterpreter () { const apiLevel = getAndroidApiLevel(); @@ -1865,9 +1984,17 @@ function instrumentArtMethodInvocationFromInterpreter () { artInterpreterDoCallExportRegex = /^_ZN3art11interpreter6DoCallILb[0-1]EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtbPNS_6JValueE$/; } + let need_docall = true; for (const exp of Module.enumerateExports('libart.so').filter(exp => artInterpreterDoCallExportRegex.test(exp.name))) { + need_docall = false; Interceptor.attach(exp.address, artController.hooks.Interpreter.doCall); } + if(need_docall & Process.arch === 'arm64'){ + const doCallsCandidates = findDoCalls(); + for(const address of doCallsCandidates){ + Interceptor.attach(address, artController.hooks.Interpreter.doCall); + } + } } function ensureArtKnowsHowToHandleReplacementMethods (vm) { @@ -1907,7 +2034,7 @@ function ensureArtKnowsHowToHandleReplacementMethods (vm) { let runFlip = null; runFlip = Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_b'); if (runFlip === null) { - runFlip = Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0'); // api 35 + runFlip = Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_'); // api 35 } if (runFlip !== null) { Interceptor.attach(runFlip, artController.hooks.Gc.runFlip); @@ -3923,12 +4050,8 @@ function makeArtThreadStateTransitionImpl (vm, env, callback) { const envVtable = env.handle.readPointer(); let exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer(); const nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer(); - if (Process.arch === 'arm64') { - const size = 0x150; - const pattern = 'c0 03 5f d6'; // ret - if (Memory.scanSync(exceptionClearImpl, size, pattern).length > 0) { - exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT).readPointer(); - } + if (Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_')){ + exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT).readPointer(); } const recompile = threadStateTransitionRecompilers[Process.arch]; if (recompile === undefined) { From f610262d6766b69763198d78c63b1df3a6a9e374 Mon Sep 17 00:00:00 2001 From: matbrik <6418737+matbrik@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:36:58 +0000 Subject: [PATCH 05/10] lint errors --- lib/android.js | 148 +++++++++++++++++++++++-------------------------- 1 file changed, 70 insertions(+), 78 deletions(-) diff --git a/lib/android.js b/lib/android.js index 5483742..fc51b64 100644 --- a/lib/android.js +++ b/lib/android.js @@ -502,8 +502,7 @@ function _getApi () { } if (temporaryApi['art::interpreter::GetNterpEntryPoint'] !== undefined) { temporaryApi.artNterpEntryPoint = temporaryApi['art::interpreter::GetNterpEntryPoint'](); - } - else { + } else { temporaryApi.artNterpEntryPoint = findExecuteNterpImpl(); } artController = makeArtController(vm); @@ -535,69 +534,64 @@ function _getApi () { return temporaryApi; } -function stringToBytesHex(str) { - let bytes = []; +function stringToBytesHex (str) { + const bytes = []; for (let i = 0; i < str.length; i++) { - let byteHex = str.charCodeAt(i).toString(16).toUpperCase(); - if (byteHex.length === 1) { - byteHex = '0' + byteHex; - } - bytes.push(byteHex); + let byteHex = str.charCodeAt(i).toString(16).toUpperCase(); + if (byteHex.length === 1) { + byteHex = '0' + byteHex; + } + bytes.push(byteHex); } return bytes.join(' '); } -function findString(string, moduleName){ +function findString (string, moduleName) { const module = Process.findModuleByName(moduleName); const ranges = module.enumerateRanges('r--'); const pattern = stringToBytesHex(string); return ranges.map((range) => { - const base = range.base; - const size = range.size; - const result = Memory.scanSync(base, size, pattern); - if (result.length>0) return result; - return; + const base = range.base; + const size = range.size; + const result = Memory.scanSync(base, size, pattern); + if (result.length > 0) return result; }).filter((element => element != null)).flat(); } -function createAdd(address, register){ - return ((0x244<<12)+(address&0xfff)<<10)+(register<<5)+register; +function createAdd (address, register) { + return (0x244 << 12) + (address & 0xfff) << 10 + register << 5 + register; } -function getPattern(instr){ - return ptr(instr).toMatchPattern().replace('ff ff ff ff', ''); +function getPattern (instr) { + return ptr(instr).toMatchPattern().replace('ff ff ff ff', ''); } -function findPatternInModule(pattern, moduleName){ +function findPatternInModule (pattern, moduleName) { const module = Process.findModuleByName(moduleName); const ranges = module.enumerateRanges('r-x'); return ranges.map((range) => { - const base = range.base; - const size = range.size; - var result = Memory.scanSync(base, size, pattern); - if (result.length==0) return; - return result; - }).filter((element => element!=null)).flat(); + const base = range.base; + const size = range.size; + const result = Memory.scanSync(base, size, pattern); + if (result.length != 0) return result; + }).filter(element => element != null).flat(); } - -function findEnsurePlugIn(){ +function findEnsurePlugIn () { const results = findString('libopenjdkjvmti.so', 'libart.so'); - const pattern = getPattern(createAdd(results[0].address,1)); - const pattern_results = findPatternInModule(pattern, 'libart.so'); - return pattern_results.map((match) => { - const possibleBranch = match.address.add(4); - const disasm = Instruction.parse(possibleBranch); - if (disasm.mnemonic=='b') - { - return new NativePointer(disasm.operands[0].value); - } - return; - }).filter((element => element!=null)).flat(); + const pattern = getPattern(createAdd(results[0].address, 1)); + const patternResults = findPatternInModule(pattern, 'libart.so'); + return patternResults.map((match) => { + const possibleBranch = match.address.add(4); + const disasm = Instruction.parse(possibleBranch); + if (disasm.mnemonic === 'b') { + return new NativePointer(disasm.operands[0].value); + } + }).filter((element => element != null)).flat(); } -function findPrologue(address){ - for (let off = 0;; off += 4) { +function findPrologue (address) { + for (let off = 0; ; off += 4) { let disasm = Instruction.parse(address.sub(0x4).sub(off)); if (disasm.mnemonic === 'str') { disasm = Instruction.parse(disasm.next); @@ -608,18 +602,19 @@ function findPrologue(address){ } } -function findExecuteNterpImpl(){ - let artMethodCopyfrom = Module.findExportByName('libart.so', '_ZN3art9ArtMethod8CopyFromEPS0_NS_11PointerSizeE') - for (let off = 0; off<0x300; off+=4){ - let disasm = Instruction.parse(artMethodCopyfrom.add(off)); - let next_instr = Instruction.parse(artMethodCopyfrom.add(off).add(0x4)); - if (disasm.mnemonic === 'adrp' && next_instr.mnemonic === 'add'){ +function findExecuteNterpImpl () { + const artMethodCopyfrom = Module.findExportByName('libart.so', '_ZN3art9ArtMethod8CopyFromEPS0_NS_11PointerSizeE'); + for (let off = 0; off < 0x300; off += 4) { + const disasm = Instruction.parse(artMethodCopyfrom.add(off)); + const nextInstr = Instruction.parse(artMethodCopyfrom.add(off).add(0x4)); + if (disasm.mnemonic === 'adrp' && nextInstr.mnemonic === 'add') { const base = ptr(disasm.operands[1].value); - const offset = next_instr.operands[2].value; + const offset = nextInstr.operands[2].value; const result = base.add(offset); const dest = Instruction.parse(result); - if (dest.mnemonic === 'sub') - return result + if (dest.mnemonic === 'sub') { + return result; + } } } } @@ -629,17 +624,17 @@ function tryGetEnvJvmti (vm, runtime) { vm.perform(() => { let ensurePluginLoaded; - if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE')===null){ + if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE') === null) { // on some devices calling the ensurePluginLoaded make the app really slow so for is commented return; - const candidates = findEnsurePlugIn(); - ensurePluginLoaded = new NativeFunction(candidates[0], - 'bool', - ['pointer', 'pointer', 'pointer']); - }else{ + // const candidates = findEnsurePlugIn(); + // ensurePluginLoaded = new NativeFunction(candidates[0], + // 'bool', + // ['pointer', 'pointer', 'pointer']); + } else { ensurePluginLoaded = new NativeFunction(Module.getExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'), - 'bool', - ['pointer', 'pointer', 'pointer']); + 'bool', + ['pointer', 'pointer', 'pointer']); } const errorPtr = Memory.alloc(pointerSize); const success = ensurePluginLoaded(runtime, Memory.allocUtf8String('libopenjdkjvmti.so'), errorPtr); @@ -1953,23 +1948,20 @@ function instrumentArtQuickEntrypoints (vm) { }); } -function findDoCalls(){ +function findDoCalls () { const results = findString('Invoking %s with bad arg %d', 'libart.so'); - const pattern = getPattern(createAdd(results[0].address,2)); - const pattern_results = findPatternInModule(pattern, 'libart.so'); - - return pattern_results.map((match) => { - const possibleBranch = match.address; - const instr = Instruction.parse(possibleBranch.sub(4)); - if (instr.mnemonic=='adrp') - { - if (new NativePointer(instr.operands[1].value).equals(results[0].address.and(0xfffffffffff000))) - { - return findPrologue(possibleBranch); - } + const pattern = getPattern(createAdd(results[0].address, 2)); + const patternResults = findPatternInModule(pattern, 'libart.so'); + + return patternResults.map((match) => { + const possibleBranch = match.address; + const instr = Instruction.parse(possibleBranch.sub(4)); + if (instr.mnemonic === 'adrp') { + if (new NativePointer(instr.operands[1].value).equals(results[0].address.and(0xfffffffffff000))) { + return findPrologue(possibleBranch); } - return - }).filter((element => element!=null)).flat(); + } + }).filter(element => element!=null).flat(); } function instrumentArtMethodInvocationFromInterpreter () { @@ -1984,14 +1976,14 @@ function instrumentArtMethodInvocationFromInterpreter () { artInterpreterDoCallExportRegex = /^_ZN3art11interpreter6DoCallILb[0-1]EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtbPNS_6JValueE$/; } - let need_docall = true; + let docallFound = true; for (const exp of Module.enumerateExports('libart.so').filter(exp => artInterpreterDoCallExportRegex.test(exp.name))) { - need_docall = false; + docallFound = false; Interceptor.attach(exp.address, artController.hooks.Interpreter.doCall); } - if(need_docall & Process.arch === 'arm64'){ + if (docallFound & Process.arch === 'arm64') { const doCallsCandidates = findDoCalls(); - for(const address of doCallsCandidates){ + for (const address of doCallsCandidates) { Interceptor.attach(address, artController.hooks.Interpreter.doCall); } } @@ -4050,7 +4042,7 @@ function makeArtThreadStateTransitionImpl (vm, env, callback) { const envVtable = env.handle.readPointer(); let exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer(); const nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer(); - if (Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_')){ + if (Module.findExportByName('libart.so', '_ZN3art6Thread15RunFlipFunctionEPS0_')) { exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR_ALT).readPointer(); } const recompile = threadStateTransitionRecompilers[Process.arch]; From 8e0c6c5933e55d83aa8ce6ff259475cc67214e91 Mon Sep 17 00:00:00 2001 From: matbrik <6418737+matbrik@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:41:44 +0000 Subject: [PATCH 06/10] fix lint error --- lib/android.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/android.js b/lib/android.js index fc51b64..a2f404f 100644 --- a/lib/android.js +++ b/lib/android.js @@ -555,7 +555,8 @@ function findString (string, moduleName) { const size = range.size; const result = Memory.scanSync(base, size, pattern); if (result.length > 0) return result; - }).filter((element => element != null)).flat(); + return null; + }).filter(element => element !== null).flat(); } function createAdd (address, register) { @@ -574,6 +575,7 @@ function findPatternInModule (pattern, moduleName) { const size = range.size; const result = Memory.scanSync(base, size, pattern); if (result.length != 0) return result; + return null; }).filter(element => element != null).flat(); } @@ -587,7 +589,7 @@ function findEnsurePlugIn () { if (disasm.mnemonic === 'b') { return new NativePointer(disasm.operands[0].value); } - }).filter((element => element != null)).flat(); + }).filter(element => element !== null).flat(); } function findPrologue (address) { @@ -626,11 +628,13 @@ function tryGetEnvJvmti (vm, runtime) { let ensurePluginLoaded; if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE') === null) { // on some devices calling the ensurePluginLoaded make the app really slow so for is commented + if (false) { + const candidates = findEnsurePlugIn(); + ensurePluginLoaded = new NativeFunction(candidates[0], + 'bool', + ['pointer', 'pointer', 'pointer']); + } return; - // const candidates = findEnsurePlugIn(); - // ensurePluginLoaded = new NativeFunction(candidates[0], - // 'bool', - // ['pointer', 'pointer', 'pointer']); } else { ensurePluginLoaded = new NativeFunction(Module.getExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'), 'bool', @@ -1961,7 +1965,8 @@ function findDoCalls () { return findPrologue(possibleBranch); } } - }).filter(element => element!=null).flat(); + return null; + }).filter(element => element !== null).flat(); } function instrumentArtMethodInvocationFromInterpreter () { From 513b09ffca6e17a9877c319006e57b5a011ea663 Mon Sep 17 00:00:00 2001 From: matbrik <6418737+matbrik@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:49:31 +0000 Subject: [PATCH 07/10] fix lint error --- lib/android.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/android.js b/lib/android.js index a2f404f..01c30ee 100644 --- a/lib/android.js +++ b/lib/android.js @@ -574,9 +574,9 @@ function findPatternInModule (pattern, moduleName) { const base = range.base; const size = range.size; const result = Memory.scanSync(base, size, pattern); - if (result.length != 0) return result; + if (result.length !== 0) return result; return null; - }).filter(element => element != null).flat(); + }).filter(element => element !== null).flat(); } function findEnsurePlugIn () { @@ -589,6 +589,7 @@ function findEnsurePlugIn () { if (disasm.mnemonic === 'b') { return new NativePointer(disasm.operands[0].value); } + return null; }).filter(element => element !== null).flat(); } @@ -628,11 +629,11 @@ function tryGetEnvJvmti (vm, runtime) { let ensurePluginLoaded; if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE') === null) { // on some devices calling the ensurePluginLoaded make the app really slow so for is commented - if (false) { - const candidates = findEnsurePlugIn(); - ensurePluginLoaded = new NativeFunction(candidates[0], - 'bool', - ['pointer', 'pointer', 'pointer']); + if (env !== null) { + const candidates = findEnsurePlugIn(); + ensurePluginLoaded = new NativeFunction(candidates[0], + 'bool', + ['pointer', 'pointer', 'pointer']); } return; } else { From 395f2ac4a3dec61dee6b0be758302925f6b06dad Mon Sep 17 00:00:00 2001 From: matbrik <6418737+matbrik@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:11:20 +0000 Subject: [PATCH 08/10] narrow fix to arm64 --- lib/android.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/android.js b/lib/android.js index 01c30ee..2a69a92 100644 --- a/lib/android.js +++ b/lib/android.js @@ -503,7 +503,8 @@ function _getApi () { if (temporaryApi['art::interpreter::GetNterpEntryPoint'] !== undefined) { temporaryApi.artNterpEntryPoint = temporaryApi['art::interpreter::GetNterpEntryPoint'](); } else { - temporaryApi.artNterpEntryPoint = findExecuteNterpImpl(); + if (Process.arch === 'arm64') + temporaryApi.artNterpEntryPoint = findExecuteNterpImpl(); } artController = makeArtController(vm); @@ -629,7 +630,7 @@ function tryGetEnvJvmti (vm, runtime) { let ensurePluginLoaded; if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE') === null) { // on some devices calling the ensurePluginLoaded make the app really slow so for is commented - if (env !== null) { + if (env !== null & Process.arch === 'arm64') { const candidates = findEnsurePlugIn(); ensurePluginLoaded = new NativeFunction(candidates[0], 'bool', From ce9e0ee694aaeb12ab08517fc5f5ce55dcf58029 Mon Sep 17 00:00:00 2001 From: matbrik <6418737+matbrik@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:13:16 +0000 Subject: [PATCH 09/10] fix lint error --- lib/android.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/android.js b/lib/android.js index 2a69a92..cac8002 100644 --- a/lib/android.js +++ b/lib/android.js @@ -503,8 +503,9 @@ function _getApi () { if (temporaryApi['art::interpreter::GetNterpEntryPoint'] !== undefined) { temporaryApi.artNterpEntryPoint = temporaryApi['art::interpreter::GetNterpEntryPoint'](); } else { - if (Process.arch === 'arm64') + if (Process.arch === 'arm64') { temporaryApi.artNterpEntryPoint = findExecuteNterpImpl(); + } } artController = makeArtController(vm); From 9636722e508b247a5509d9fa69ba6c87bf64bdd8 Mon Sep 17 00:00:00 2001 From: matbrik <6418737+matbrik@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:39:12 +0000 Subject: [PATCH 10/10] nterp from android 11 --- lib/android.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/android.js b/lib/android.js index cac8002..910c873 100644 --- a/lib/android.js +++ b/lib/android.js @@ -503,7 +503,7 @@ function _getApi () { if (temporaryApi['art::interpreter::GetNterpEntryPoint'] !== undefined) { temporaryApi.artNterpEntryPoint = temporaryApi['art::interpreter::GetNterpEntryPoint'](); } else { - if (Process.arch === 'arm64') { + if (Process.arch === 'arm64' && getAndroidApiLevel() >= 30) { temporaryApi.artNterpEntryPoint = findExecuteNterpImpl(); } } @@ -631,7 +631,7 @@ function tryGetEnvJvmti (vm, runtime) { let ensurePluginLoaded; if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE') === null) { // on some devices calling the ensurePluginLoaded make the app really slow so for is commented - if (env !== null & Process.arch === 'arm64') { + if (env !== null && Process.arch === 'arm64') { const candidates = findEnsurePlugIn(); ensurePluginLoaded = new NativeFunction(candidates[0], 'bool', @@ -1989,7 +1989,7 @@ function instrumentArtMethodInvocationFromInterpreter () { docallFound = false; Interceptor.attach(exp.address, artController.hooks.Interpreter.doCall); } - if (docallFound & Process.arch === 'arm64') { + if (docallFound && Process.arch === 'arm64') { const doCallsCandidates = findDoCalls(); for (const address of doCallsCandidates) { Interceptor.attach(address, artController.hooks.Interpreter.doCall); @@ -3536,7 +3536,7 @@ class ArtMethodMangler { // Replace Nterp quick entrypoints with art_quick_to_interpreter_bridge to force stepping out // of ART's next-generation interpreter and use the quick stub instead. - if (artNterpEntryPoint !== undefined && quickCode.equals(artNterpEntryPoint)) { + if (artNterpEntryPoint !== undefined && artNterpEntryPoint !== 0 && quickCode.equals(artNterpEntryPoint)) { patchArtMethod(hookedMethodId, { quickCode: api.artQuickToInterpreterBridge }, vm);