diff --git a/lib/android.js b/lib/android.js index 8c253e0..b6b0ba3 100644 --- a/lib/android.js +++ b/lib/android.js @@ -399,64 +399,38 @@ function _getApi () { Object.keys(functions) .forEach(function (name) { let exp = exportByName[name]; - if (exp === undefined && name === '_ZN3art7Monitor17TranslateLocationEPNS_9ArtMethodEjPPKcPi') { - let proc_self_cmdline_string_found_addr; - let proc_self_cmdline_string = '00 2f 70 72 6f 63 2f 73 65 6c 66 2f 63 6d 64 6c 69 6e 65'; - const rodata_seciton = Module.enumerateSectionsSync('libart.so').filter(s => s.name == '.rodata')[0]; - for (const match of Memory.scanSync(rodata_seciton.address, rodata_seciton.size, proc_self_cmdline_string)) { - if (match) { - proc_self_cmdline_string_found_addr = match.address.add(0x1).toString(); - break; - } - } - // adrp, add sample - // 00 CB FF F0 00 00 07 91 - // E0 EF FF D0 00 80 38 91 - // 40 D3 FF D0 00 24 11 91 - // 00 DC FF 90 00 F0 10 91 - // 00 DC FF 90 00 F0 10 91 - let adrp, add; - let adrp_add_pattern = '?0 ?? FF ?0 00 ?? ?? 91'; - let adrp_add_pattern_found_addr; - let translate_location_func_addr; - const text_section = Module.enumerateSectionsSync('libart.so').filter(s => s.name == '.text')[0]; - for (const match of Memory.scanSync(text_section.address, text_section.size, adrp_add_pattern)) { - let disasm = Instruction.parse(match.address); - if (disasm.mnemonic === "adrp") { - adrp = disasm.operands.find(op => op.type === 'imm')?.value; - disasm = Instruction.parse(disasm.next); - if (disasm.mnemonic !== "add") { - disasm = Instruction.parse(disasm.next); - } - add = disasm.operands.find(op => op.type === 'imm')?.value; - if (adrp !== undefined && add !== undefined && ptr(adrp).add(add).toString() === proc_self_cmdline_string_found_addr.toString()) { - if (adrp_add_pattern_found_addr === undefined) { - adrp_add_pattern_found_addr = match.address; - } - for (let off = 0; off < 40; off += 4) { - disasm = Instruction.parse(adrp_add_pattern_found_addr.sub(off)); - if (disasm.mnemonic === "b" || disasm.mnemonic === "bl") { - let branch_addr = ptr(disasm.operands.find(op => op.type === 'imm')?.value); - disasm = Instruction.parse(branch_addr); - if (disasm.mnemonic === 'stp') { - translate_location_func_addr = disasm.address; - exp = { - address: translate_location_func_addr, - name: "_ZN3art7Monitor17TranslateLocationEPNS_9ArtMethodEjPPKcPi", - type: "function" - } - break; - } else { - continue; + if (exp === undefined && name === '_ZN3art7Monitor17TranslateLocationEPNS_9ArtMethodEjPPKcPi' && Process.arch === 'arm64') { + const procSelfCmdlineString = '00 2f 70 72 6f 63 2f 73 65 6c 66 2f 63 6d 64 6c 69 6e 65'; + let procSelfCmdlineAddr = findStringInRodata('libart.so', procSelfCmdlineString); + if (procSelfCmdlineAddr !== null) { + procSelfCmdlineAddr = procSelfCmdlineAddr.add(1); + + exp = findAdrpAddReferenceToString( + 'libart.so', + '?0 ?? ff ?0 00 ?? ?? 91', + procSelfCmdlineAddr, + adrpAddAddr => { + for (let off = 0; off !== 40; off += 4) { + const dis = Instruction.parse(adrpAddAddr.sub(off)); + const mnemonic = dis.mnemonic; + if (mnemonic === "b" || mnemonic === "bl") { + const branchAddr = ptr(dis.operands[0].value); + const nextDis = Instruction.parse(branchAddr); + if (nextDis.mnemonic === 'stp') { + return { + type: "function", + name, + address: nextDis.address + }; } } } - break; + return null; } - } + ); } } - if (exp !== undefined && exp.type === 'function') { + if (exp?.type === 'function') { const signature = functions[name]; if (typeof signature === 'function') { signature.call(temporaryApi, exp.address); @@ -610,65 +584,33 @@ function tryGetEnvJvmti (vm, runtime) { let env = null; vm.perform(() => { - let ensurePluginLoaded; - if (Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE') === null) { - if (Process.arch === 'arm64') { - let libopenjdkjvmti_so_string_found_addr; - let libopenjdkjvmti_so_string = '6c 69 62 6f 70 65 6e 6a 64 6b 6a 76 6d 74 69 2e 73 6f'; - const rodata_seciton = Module.enumerateSectionsSync('libart.so').filter(s => s.name == '.rodata')[0]; - for (const match of Memory.scanSync(rodata_seciton.address, rodata_seciton.size, libopenjdkjvmti_so_string)) { - if (match) { - libopenjdkjvmti_so_string_found_addr = match.address.toString(); - break; - } - } - // adrp, add sample - // 61 D4 FF B0 21 80 38 91 - // 41 B9 FF D0 21 78 0E 91 - // 41 CD FF B0 21 F0 09 91 - // 41 B9 FF B0 21 78 0E 91 - let adrp, add; - let adrp_add_pattern = '?1 ?? FF ?0 21 ?? ?? 91'; - let adrp_add_pattern_found_addr; - let ensurePluginLoaded_func_addr; - const text_section = Module.enumerateSectionsSync('libart.so').filter(s => s.name == '.text')[0]; - for (const match of Memory.scanSync(text_section.address, text_section.size, adrp_add_pattern)) { - let disasm = Instruction.parse(match.address); - if (disasm.mnemonic === "adrp") { - adrp = disasm.operands.find(op => op.type === 'imm')?.value; - disasm = Instruction.parse(disasm.next); - if (disasm.mnemonic !== "add") { - disasm = Instruction.parse(disasm.next); - } - add = disasm.operands.find(op => op.type === 'imm')?.value; - if (adrp !== undefined && add !== undefined && ptr(adrp).add(add).toString() === libopenjdkjvmti_so_string_found_addr.toString()) { - if (adrp_add_pattern_found_addr === undefined) { - adrp_add_pattern_found_addr = match.address; - } - for (let off = 0;; off += 4) { - disasm = Instruction.parse(adrp_add_pattern_found_addr.add(off)); - if (disasm.mnemonic === "b" || disasm.mnemonic === "bl") { - ensurePluginLoaded_func_addr = ptr(disasm.operands.find(op => op.type === 'imm')?.value); - break; - } + let ensurePluginLoadedAddr = Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'); + if (ensurePluginLoadedAddr === null && Process.arch === 'arm64') { + const libopenjdkjvmtiSoString = '6c 69 62 6f 70 65 6e 6a 64 6b 6a 76 6d 74 69 2e 73 6f'; + const libopenjdkjvmtiSoAddr = findStringInRodata('libart.so', libopenjdkjvmtiSoString); + if (libopenjdkjvmtiSoAddr !== null) { + ensurePluginLoadedAddr = findAdrpAddReferenceToString( + 'libart.so', + '?1 ?? ff ?0 21 ?? ?? 91', + libopenjdkjvmtiSoAddr, + adrpAddAddr => { + for (let off = 0;; off += 4) { + const dis = Instruction.parse(adrpAddAddr.add(off)); + const mnemonic = dis.mnemonic; + if (mnemonic === "b" || mnemonic === "bl") { + return ptr(dis.operands[0].value); } - break; } } - } - if (ensurePluginLoaded_func_addr !== undefined) { - ensurePluginLoaded = new NativeFunction(ensurePluginLoaded_func_addr, - 'bool', - ['pointer', 'pointer', 'pointer']); - } else { - return; - } + ); } - } else { - ensurePluginLoaded = new NativeFunction(Module.getExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'), - 'bool', - ['pointer', 'pointer', 'pointer']); } + if (ensurePluginLoadedAddr === null) { + return; + } + const ensurePluginLoaded = new NativeFunction(ensurePluginLoadedAddr, + 'bool', + ['pointer', 'pointer', 'pointer']); const errorPtr = Memory.alloc(pointerSize); const success = ensurePluginLoaded(runtime, Memory.allocUtf8String('libopenjdkjvmti.so'), errorPtr); if (!success) { @@ -1993,66 +1935,41 @@ function instrumentArtMethodInvocationFromInterpreter () { artInterpreterDoCallExportRegex = /^_ZN3art11interpreter6DoCallILb[0-1]EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtbPNS_6JValueE$/; } - let docallFound = false; + let doCallFound = false; for (const exp of Module.enumerateExports('libart.so').filter(exp => artInterpreterDoCallExportRegex.test(exp.name))) { - docallFound = true; + doCallFound = true; Interceptor.attach(exp.address, artController.hooks.Interpreter.doCall); } - if (!docallFound && Process.arch === 'arm64') { - let Invoking_percent_s_string_found_addr; - let Invoking_percent_s_string = '49 6e 76 6f 6b 69 6e 67 20 25 73'; - const rodata_seciton = Module.enumerateSectionsSync('libart.so').filter(s => s.name == '.rodata')[0]; - for (const match of Memory.scanSync(rodata_seciton.address, rodata_seciton.size, Invoking_percent_s_string)) { - if (match) { - Invoking_percent_s_string_found_addr = match.address.toString(); - break; - } - } - // adrp, add sample - // E2 E8 FF F0 42 20 1C 91 || 62 E8 FF 90 42 20 1C 91 - // 42 E9 FF F0 42 38 10 91 || 62 E2 FF F0 42 38 10 91 - // E2 E8 FF F0 42 20 1C 91 || 62 E8 FF 90 42 20 1C 91 - // 02 E7 FF 90 42 F4 0F 91 || E2 E6 FF F0 42 F4 0F 91 - let adrp, add; - let adrp_add_pattern = '?2 E? FF ?0 42 ?? ?? 91'; - let adrp_add_pattern_found_addr; - let doCall_func_addr = []; - let doCall_func_found_count = 0; - const text_section = Module.enumerateSectionsSync('libart.so').filter(s => s.name == '.text')[0]; - for (const match of Memory.scanSync(text_section.address, text_section.size, adrp_add_pattern)) { - let disasm = Instruction.parse(match.address); - if (disasm.mnemonic === "adrp") { - adrp = disasm.operands.find(op => op.type === 'imm')?.value; - disasm = Instruction.parse(disasm.next); - if (disasm.mnemonic !== "add") { - disasm = Instruction.parse(disasm.next); - } - add = disasm.operands.find(op => op.type === 'imm')?.value; - if (adrp !== undefined && add !== undefined && ptr(adrp).add(add).toString() === Invoking_percent_s_string_found_addr.toString()) { - adrp_add_pattern_found_addr = match.address; + if (!doCallFound && Process.arch === 'arm64') { + const invokingPercentSString = '49 6e 76 6f 6b 69 6e 67 20 25 73'; + const invokingPercentSAddr = findStringInRodata('libart.so', invokingPercentSString); + if (invokingPercentSAddr !== null) { + const foundFuncs = []; + const doCallFuncAddrs = findAdrpAddReferenceToString( + 'libart.so', + '?2 e? ff ?0 42 ?? ?? 91', + invokingPercentSAddr, + adrpAddAddr => { for (let off = 0;; off += 4) { - disasm = Instruction.parse(adrp_add_pattern_found_addr.sub(off)); - if (disasm.mnemonic === "str") { - disasm = Instruction.parse(disasm.next); - if (disasm.mnemonic === "stp") { - disasm = Instruction.parse(disasm.next); - if (disasm.mnemonic === "stp") { - doCall_func_found_count++; - doCall_func_addr.push(disasm.address.sub(0x8)); + let dis = Instruction.parse(adrpAddAddr.sub(off)); + if (dis.mnemonic === "str") { + dis = Instruction.parse(dis.next); + if (dis.mnemonic === "stp") { + dis = Instruction.parse(dis.next); + if (dis.mnemonic === "stp") { + foundFuncs.push(dis.address.sub(8)); break; } } } } - if (doCall_func_found_count == 2) { - break; - } + return (foundFuncs.length === 2) ? foundFuncs : null; } + ) ?? []; + for (const address of doCallFuncAddrs) { + Interceptor.attach(address, artController.hooks.Interpreter.doCall); } } - for (const address of doCall_func_addr) { - Interceptor.attach(address, artController.hooks.Interpreter.doCall); - } } } @@ -2086,46 +2003,26 @@ function ensureArtKnowsHowToHandleReplacementMethods (vm) { } else if (apiLevel > 22) { copyingPhase = Module.findExportByName('libart.so', '_ZN3art2gc9collector17ConcurrentCopying12MarkingPhaseEv'); } - if (Process.arch === 'arm64' && copyingPhase === null) { - let CopyingPhase_string_found_addr; - let CopyingPhase_string = '43 6f 70 79 69 6e 67 50 68 61 73 65'; - const rodata_seciton = Module.enumerateSectionsSync('libart.so').filter(s => s.name == '.rodata')[0]; - for (const match of Memory.scanSync(rodata_seciton.address, rodata_seciton.size, CopyingPhase_string)) { - if (match) { - CopyingPhase_string_found_addr = match.address.toString(); - break; - } - } - let adrp, add; - let adrp_add_pattern = '?1 ?? FF ?0 21 ?? ?? 91'; - let adrp_add_in_CopyingPhase_func; - const text_section = Module.enumerateSectionsSync('libart.so').filter(s => s.name == '.text')[0]; - for (const match of Memory.scanSync(text_section.address, text_section.size, adrp_add_pattern)) { - let disasm = Instruction.parse(match.address); - if (disasm.mnemonic === "adrp") { - adrp = disasm.operands.find(op => op.type === 'imm')?.value; - disasm = Instruction.parse(disasm.next); - if (disasm.mnemonic !== "add") { - disasm = Instruction.parse(disasm.next); - } - add = disasm.operands.find(op => op.type === 'imm')?.value; - if (adrp !== undefined && add !== undefined && ptr(adrp).add(add).toString() === CopyingPhase_string_found_addr.toString()) { - if (adrp_add_in_CopyingPhase_func === undefined) { - adrp_add_in_CopyingPhase_func = match.address; - } + if (copyingPhase === null && Process.arch === 'arm64') { + const copyingPhaseString = '43 6f 70 79 69 6e 67 50 68 61 73 65'; + const copyingPhaseAddr = findStringInRodata('libart.so', copyingPhaseString); + if (copyingPhaseAddr !== null) { + copyingPhase = findAdrpAddReferenceToString( + 'libart.so', + '?1 ?? ff ?0 21 ?? ?? 91', + copyingPhaseAddr, + adrpAddAddr => { for (let off = 0;; off += 4) { - disasm = Instruction.parse(adrp_add_in_CopyingPhase_func.sub(off)); - if (disasm.mnemonic === "sub") { - disasm = Instruction.parse(disasm.next); - if (disasm.mnemonic === "stp") { - copyingPhase = disasm.address.sub(0x4); - break; + let dis = Instruction.parse(adrpAddAddr.sub(off)); + if (dis.mnemonic === "sub") { + dis = Instruction.parse(dis.next); + if (dis.mnemonic === "stp") { + return dis.address.sub(4); } } } - break; } - } + ); } } if (copyingPhase !== null) { @@ -4151,76 +4048,45 @@ function makeArtThreadStateTransitionImpl (vm, env, callback) { const envVtable = env.handle.readPointer(); let exceptionClearImpl = envVtable.add(ENV_VTABLE_OFFSET_EXCEPTION_CLEAR).readPointer(); let nextFuncImpl = envVtable.add(ENV_VTABLE_OFFSET_FATAL_ERROR).readPointer(); - // I think if we can find the JNI_FatalError function symbol and its address matches nextFuncImpl, then it should be fine. - let checkFatalError = Module.enumerateSymbolsSync('libart.so').filter(m => m.name.indexOf('art3JNI') >= 0 && - m.name.indexOf('FatalError') >=0 && - m.address.toString() === nextFuncImpl.toString())[0]; - - if (Process.arch === 'arm64' && checkFatalError === undefined) { - let JNI_FatalError_Called_string_found_addr; - let JNI_FatalError_Called_string = '4A 4E 49 20 46 61 74 61 6C 45 72 72 6F 72 20 63 61 6C'; - const rodata_seciton = Module.enumerateSectionsSync('libart.so').filter(s => s.name == '.rodata')[0]; - for (const match of Memory.scanSync(rodata_seciton.address, rodata_seciton.size, JNI_FatalError_Called_string)) { - if (match) { - JNI_FatalError_Called_string_found_addr = match.address.toString(); - break; - } - } - let adrp, add; - let adrp_add_pattern = '?1 ?? FF ?0 21 ?? ?? 91'; - let adrp_add_in_JNI_false_FatalError_func; - let adrp_add_in_JNI_true_FatalError_func; - let JNI_true_FatalError_func; - let JNI_ExceptionClear_func; - const text_section = Module.enumerateSectionsSync('libart.so').filter(s => s.name == '.text')[0]; - for (const match of Memory.scanSync(text_section.address, text_section.size, adrp_add_pattern)) { - let disasm = Instruction.parse(match.address); - if (disasm.mnemonic === "adrp") { - adrp = disasm.operands.find(op => op.type === 'imm')?.value; - disasm = Instruction.parse(disasm.next); - if (disasm.mnemonic !== "add") { - disasm = Instruction.parse(disasm.next); - } - add = disasm.operands.find(op => op.type === 'imm')?.value; - if (adrp !== undefined && add !== undefined && ptr(adrp).add(add).toString() === JNI_FatalError_Called_string_found_addr.toString()) { - if (adrp_add_in_JNI_false_FatalError_func === undefined) { - adrp_add_in_JNI_false_FatalError_func = match.address; - continue; - } - if (adrp_add_in_JNI_true_FatalError_func === undefined) { - adrp_add_in_JNI_true_FatalError_func = match.address; - } - for (let off = 0;; off += 4) { - disasm = Instruction.parse(adrp_add_in_JNI_true_FatalError_func.sub(off)); - if (disasm.mnemonic === "sub") { - disasm = Instruction.parse(disasm.next); - if (disasm.mnemonic === "stp") { - JNI_true_FatalError_func = disasm.address.sub(0x4); - break; - } - } - } - if (JNI_true_FatalError_func !== undefined) { - for (let off = 0;; off += 4) { - disasm = Instruction.parse(JNI_true_FatalError_func.sub(0x4).sub(off)); - if (disasm.mnemonic === "sub") { - disasm = Instruction.parse(disasm.next); - if (disasm.mnemonic === "stp") { - JNI_ExceptionClear_func = disasm.address.sub(0x4); - break; - } - } - } + const fatalErrorSymbol = Module.enumerateSymbols('libart.so').find(({ name, address }) => name.includes('art3JNI') && + name.includes('FatalError') && + address.equals(nextFuncImpl)); + if (fatalErrorSymbol === undefined && Process.arch === 'arm64') { + const jniFatalErrorCalledString = '4a 4e 49 20 46 61 74 61 6c 45 72 72 6f 72 20 63 61 6c'; + const jniFatalErrorCalledAddr = findStringInRodata('libart.so', jniFatalErrorCalledString); + + let adrpAddInJNIFalseFatalErrorFunc; + let adrpAddInJNITrueFatalErrorFunc; + let jniTrueFatalErrorFunc; + let jniExceptionClearFunc; + + if (jniFatalErrorCalledAddr !== null) { + const occurrences = []; + if (findAdrpAddReferenceToString( + 'libart.so', + '?1 ?? ff ?0 21 ?? ?? 91', + jniFatalErrorCalledAddr, + address => { + occurrences.push(address); + + if (occurrences.length === 1) { + adrpAddInJNIFalseFatalErrorFunc = address; + return null; } - break; + + adrpAddInJNITrueFatalErrorFunc = address; + + jniTrueFatalErrorFunc = scanBackwardForSubStp(adrpAddInJNITrueFatalErrorFunc); + jniExceptionClearFunc = scanBackwardForSubStp(jniTrueFatalErrorFunc.sub(4)); + + return occurrences; } + ) !== null) { + exceptionClearImpl = jniExceptionClearFunc; + nextFuncImpl = jniTrueFatalErrorFunc; } } - if (JNI_true_FatalError_func !== undefined && JNI_ExceptionClear_func !== undefined) { - exceptionClearImpl = JNI_ExceptionClear_func; - nextFuncImpl = JNI_true_FatalError_func; - } } const recompile = threadStateTransitionRecompilers[Process.arch]; if (recompile === undefined) { @@ -5409,6 +5275,73 @@ function alignPointerOffset (offset) { return offset; } +function findStringInRodata(modName, stringBytes) { + const rodataSection = Module.enumerateSections(modName).find(s => s.name === '.rodata'); + const matches = Memory.scanSync(rodataSection.address, rodataSection.size, stringBytes); + if (matches.length === 0) { + return null; + } + return matches[0].address; +} + +function findAdrpAddReferenceToString(modName, pattern, stringAddr, callback) { + const arch = Process.arch; + + let firstMnemonic; + if (arch === 'arm64') { + firstMnemonic = 'adrp'; + } else if (arch === 'arm') { + firstMnemonic = 'adr'; + } else { + return null; + } + + const textSection = Module.enumerateSections(modName).find(s => s.name === '.text'); + + for (const match of Memory.scanSync(textSection.address, textSection.size, pattern)) { + let dis = Instruction.parse(match.address); + if (dis.mnemonic !== firstMnemonic) { + continue; + } + const baseVal = dis.operands[1].value; + + dis = Instruction.parse(dis.next); + if (dis.mnemonic !== 'add') { + dis = Instruction.parse(dis.next); + if (dis.mnemonic !== 'add') { + continue; + } + } + + const addOp = dis.operands[2]; + if (addOp.type !== 'imm') { + continue; + } + const offsetVal = addOp.value; + + if (ptr(baseVal).add(offsetVal).equals(stringAddr)) { + const result = callback(match.address); + if (result !== null) { + return result; + } + } + } + + return null; +} + +function scanBackwardForSubStp(startAddress) { + for (let off = 0;; off += 4) { + const dis = Instruction.parse(startAddress.sub(off)); + if (dis.mnemonic === 'sub') { + const nextDis = Instruction.parse(dis.next); + if (nextDis.mnemonic === 'stp') { + return nextDis.address.sub(4); + } + } + } +} + module.exports = { getApi, ensureClassInitialized,