Skip to content

Instantly share code, notes, and snippets.

@oleavr
Last active January 30, 2024 15:03
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save oleavr/f5b6c2ed05c502e5ad53271ffdda5168 to your computer and use it in GitHub Desktop.
Save oleavr/f5b6c2ed05c502e5ad53271ffdda5168 to your computer and use it in GitHub Desktop.
ArtStackVisitor example
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