diff --git a/lib/android.js b/lib/android.js index 128cbf6..cea9adc 100644 --- a/lib/android.js +++ b/lib/android.js @@ -405,16 +405,15 @@ function _getApi () { if (procSelfCmdlineAddr !== null) { procSelfCmdlineAddr = procSelfCmdlineAddr.add(1); - exp = findAdrpAddReferenceToString( - 'libart.so', - '?0 ?? ff ?0 00 ?? ?? 91', + exp = findPcRelativeReferenceToString( + 'libart.so', '?0 ?? ff ?0 00 ?? ?? 91', 0, procSelfCmdlineAddr, adrpAddAddr => { for (let off = 0; off !== 40; off += 4) { - const dis = Instruction.parse(adrpAddAddr.sub(off)); - const mnemonic = dis.mnemonic; + const insn = Instruction.parse(adrpAddAddr.sub(off)); + const mnemonic = insn.mnemonic; if (mnemonic === 'b' || mnemonic === 'bl') { - const branchAddr = ptr(dis.operands[0].value); + const branchAddr = ptr(insn.operands[0].value); const nextDis = Instruction.parse(branchAddr); if (nextDis.mnemonic === 'stp') { return { @@ -585,24 +584,33 @@ function tryGetEnvJvmti (vm, runtime) { vm.perform(() => { let ensurePluginLoadedAddr = Module.findExportByName('libart.so', '_ZN3art7Runtime18EnsurePluginLoadedEPKcPNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE'); - if (ensurePluginLoadedAddr === null && Process.arch === 'arm64') { + console.log('arch:', Process.arch, 'ensurePluginLoadedAddr:', ensurePluginLoadedAddr); + if (ensurePluginLoadedAddr === null) { 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); + if (Process.arch === 'arm64') { + ensurePluginLoadedAddr = findPcRelativeReferenceToString( + 'libart.so', '?1 ?? ff ?0 21 ?? ?? 91', 0, + libopenjdkjvmtiSoAddr, + adrpAddAddr => { + for (let off = 0; ; off += 4) { + const insn = Instruction.parse(adrpAddAddr.add(off)); + const mnemonic = insn.mnemonic; + if (mnemonic === 'b' || mnemonic === 'bl') { + return ptr(insn.operands[0].value); + } } } - } - ); + ); + } else { + ensurePluginLoadedAddr = findPcRelativeReferenceToString( + 'libart.so', '49 0d f1 ?? 0a', -1, + libopenjdkjvmtiSoAddr, + match => scanForward(match.add.next, 4, insn => (insn.mnemonic === 'bl') ? ptr(insn.operands[0].value).or(1) : null) + ); + } + console.log('ensurePluginLoadedAddr:', ensurePluginLoadedAddr?.sub(Module.getBaseAddress('libart.so'))); } } if (ensurePluginLoadedAddr === null) { @@ -1945,19 +1953,18 @@ function instrumentArtMethodInvocationFromInterpreter () { const invokingPercentSAddr = findStringInRodata('libart.so', invokingPercentSString); if (invokingPercentSAddr !== null) { const foundFuncs = []; - const doCallFuncAddrs = findAdrpAddReferenceToString( - 'libart.so', - '?2 e? ff ?0 42 ?? ?? 91', + const doCallFuncAddrs = findPcRelativeReferenceToString( + 'libart.so', '?2 e? ff ?0 42 ?? ?? 91', 0, invokingPercentSAddr, adrpAddAddr => { for (let off = 0; ; off += 4) { - 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)); + let insn = Instruction.parse(adrpAddAddr.sub(off)); + if (insn.mnemonic === 'str') { + insn = Instruction.parse(insn.next); + if (insn.mnemonic === 'stp') { + insn = Instruction.parse(insn.next); + if (insn.mnemonic === 'stp') { + foundFuncs.push(insn.address.sub(8)); break; } } @@ -2007,17 +2014,16 @@ function ensureArtKnowsHowToHandleReplacementMethods (vm) { 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', + copyingPhase = findPcRelativeReferenceToString( + 'libart.so', '?1 ?? ff ?0 21 ?? ?? 91', 0, copyingPhaseAddr, adrpAddAddr => { for (let off = 0; ; off += 4) { - 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); + let insn = Instruction.parse(adrpAddAddr.sub(off)); + if (insn.mnemonic === 'sub') { + insn = Instruction.parse(insn.next); + if (insn.mnemonic === 'stp') { + return insn.address.sub(4); } } } @@ -4062,9 +4068,8 @@ function makeArtThreadStateTransitionImpl (vm, env, callback) { if (jniFatalErrorCalledAddr !== null) { const occurrences = []; - if (findAdrpAddReferenceToString( - 'libart.so', - '?1 ?? ff ?0 21 ?? ?? 91', + if (findPcRelativeReferenceToString( + 'libart.so', '?1 ?? ff ?0 21 ?? ?? 91', 0, jniFatalErrorCalledAddr, address => { occurrences.push(address); @@ -5282,45 +5287,107 @@ function findStringInRodata (modName, stringBytes) { return matches[0].address; } -function findAdrpAddReferenceToString (modName, pattern, stringAddr, callback) { +function findPcRelativeReferenceToString (modName, pattern, offset, stringAddr, predicate) { const arch = Process.arch; - let firstMnemonic; - if (arch === 'arm64') { - firstMnemonic = 'adrp'; - } else if (arch === 'arm') { - firstMnemonic = 'adr'; - } else { + if (!arch.startsWith('arm')) { return null; } const textSection = Module.enumerateSections(modName).find(s => s.name === '.text'); + const matches = Memory.scanSync(textSection.address, textSection.size, pattern); - 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; + if (arch === 'arm64') { + for (const match of matches) { + const candidate = match.address.add(offset); - dis = Instruction.parse(dis.next); - if (dis.mnemonic !== 'add') { - dis = Instruction.parse(dis.next); - if (dis.mnemonic !== 'add') { + const adrp = Instruction.parse(candidate); + if (adrp.mnemonic !== 'adrp') { continue; } - } + const adrpOps = adrp.operands; + const destReg = adrpOps[0].value; + const baseVal = adrpOps[1].value; - const addOp = dis.operands[2]; - if (addOp.type !== 'imm') { - continue; + const offsetVal = scanForward(adrp.next, 2, insn => { + if (insn.mnemonic !== 'add') { + return null; + } + + const addOps = insn.operands; + if (addOps[0].value !== destReg || addOps[1].value !== destReg) { + return null; + } + + const immOp = addOps[2]; + if (immOp.type !== 'imm') { + return null; + } + + return immOp.value; + }); + if (offsetVal === null) { + continue; + } + + if (ptr(baseVal).add(offsetVal).equals(stringAddr)) { + const result = predicate(candidate); + if (result !== null) { + return result; + } + } } - const offsetVal = addOp.value; + } else { + const insnAlignMask = ptr(3).not(); - if (ptr(baseVal).add(offsetVal).equals(stringAddr)) { - const result = callback(match.address); - if (result !== null) { - return result; + for (const match of matches) { + const candidate = match.address.add(offset); + + const ldr = Instruction.parse(candidate.or(1)); + if (ldr.mnemonic !== 'ldr') { + continue; + } + + const ldrOps = ldr.operands; + const ldrSrc = ldrOps[1]; + if (ldrSrc.type !== 'mem') { + continue; + } + const ldrSrcVal = ldrSrc.value; + if (ldrSrcVal.base !== 'pc') { + continue; + } + + const destReg = ldrOps[0].value; + + const add = scanForward(ldr.next, 4, insn => { + if (insn.mnemonic !== 'add') { + return null; + } + + const ops = insn.operands; + if (ops[0].value !== destReg) { + return null; + } + + if (ops[1].value !== 'pc') { + return null; + } + + return insn; + }); + if (add === null) { + continue; + } + + const baseVal = ldr.address.and(insnAlignMask).add(4 + ldrSrcVal.disp).readPointer(); + const offsetVal = add.address.add(4); + + if (baseVal.add(offsetVal).equals(stringAddr)) { + const result = predicate({ ldr, add }); + if (result !== null) { + return result; + } } } } @@ -5328,11 +5395,24 @@ function findAdrpAddReferenceToString (modName, pattern, stringAddr, callback) { return null; } +function scanForward (startAddress, limit, predicate) { + let cursor = startAddress; + for (let i = 0; i !== limit; i++) { + const insn = Instruction.parse(cursor); + + const result = predicate(insn); + if (result !== null) { return result; } + + cursor = insn.next; + } + 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); + const insn = Instruction.parse(startAddress.sub(off)); + if (insn.mnemonic === 'sub') { + const nextDis = Instruction.parse(insn.next); if (nextDis.mnemonic === 'stp') { return nextDis.address.sub(4); }