Last active
January 30, 2024 15:03
-
-
Save oleavr/f5b6c2ed05c502e5ad53271ffdda5168 to your computer and use it in GitHub Desktop.
ArtStackVisitor example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Java = require('frida-java-bridge'); | |
const { getApi, withRunnableArtThread, ArtStackVisitor, translateMethod } = require('frida-java-bridge/lib/android'); | |
Java.perform(() => { | |
const AccountManager = Java.use('android.accounts.AccountManager'); | |
const m = AccountManager.getAccounts; | |
m.implementation = function (...args) { | |
console.log('getAccounts() called from: ' + JSON.stringify(captureBacktrace(), null, 2)); | |
return m.apply(this, args); | |
}; | |
}); | |
const translateLocation = new NativeFunction( | |
Module.getExportByName('libart.so', '_ZN3art7Monitor17TranslateLocationEPNS_9ArtMethodEjPPKcPi'), | |
'void', | |
[ | |
'pointer', | |
'uint32', | |
'pointer', | |
'pointer', | |
], | |
{ | |
exceptions: 'propagate' | |
}); | |
class DebugStackVisitor extends ArtStackVisitor { | |
constructor(thread) { | |
super(thread, getApi()['art::Thread::GetLongJumpContext'](thread), 'include-inlined-frames'); | |
this.frames = []; | |
} | |
visitFrame() { | |
this._collectFrame(this.describeLocation()); | |
return true; | |
} | |
_collectFrame(location) { | |
if (location === 'upcall') | |
return; | |
const tokens = location.split('\'', 3); | |
const rawMethodSignature = tokens[1]; | |
if (rawMethodSignature.startsWith('<')) | |
return; | |
const details = tokens[2]; | |
const separatorIndex = rawMethodSignature.indexOf(' '); | |
const returnType = rawMethodSignature.substring(0, separatorIndex); | |
const rest = rawMethodSignature.substring(separatorIndex + 1); | |
const argsStartIndex = rest.indexOf('('); | |
const argsEndIndex = rest.indexOf(')', argsStartIndex + 1); | |
const argTypes = rest.substring(argsStartIndex + 1, argsEndIndex); | |
const classAndMethodName = rest.substring(0, argsStartIndex); | |
const methodNameStartIndex = classAndMethodName.lastIndexOf('.'); | |
const className = classAndMethodName.substring(0, methodNameStartIndex); | |
const methodName = classAndMethodName.substring(methodNameStartIndex + 1); | |
let dexPc = parseInt(details.substring(13), 16); | |
const methodSignature = `${returnType} ${methodName}(${argTypes})`; | |
const actualMethod = this.getMethod(); | |
const translatedMethod = translateMethod(actualMethod); | |
if (!translatedMethod.equals(actualMethod)) | |
dexPc = 0; | |
const fileNamePtr = Memory.alloc(16); | |
const lineNumberPtr = fileNamePtr.add(8); | |
translateLocation(translatedMethod, dexPc, fileNamePtr, lineNumberPtr); | |
const fileName = fileNamePtr.readPointer().readUtf8String(); | |
const lineNumber = lineNumberPtr.readS32(); | |
this.frames.push({ | |
'class': className, | |
fileName, | |
lineNumber, | |
methodSignature, | |
source: 'dynamic' | |
}); | |
} | |
} | |
function captureBacktrace() { | |
let frames = null; | |
const vm = Java.vm; | |
withRunnableArtThread(vm, vm.getEnv(), thread => { | |
const visitor = new DebugStackVisitor(thread); | |
visitor.walkStack(true); | |
frames = visitor.frames; | |
}); | |
return frames; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment