Designs and generates Frida JavaScript/TypeScript single-file scripts or full agents for dynamic reverse engineering on multiple platforms...
///<reference path='index.d.ts'/> at the top for IDE type hints.index.d.ts exists in the workspace (prefer rg --files -g "index.d.ts").curl https://github.com/DefinitelyTyped/DefinitelyTyped/raw/refs/heads/master/types/frida-gum/index.d.ts -o index.d.ts.[frida] tag: detail, JSON.stringify for complex data).ptr(...).Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress) to capture native call stack; prefer Backtracer.FUZZY for stripped binaries.module+offset via DebugSymbol.fromAddress(addr) or manual calculation addr.sub(module.base) for reproducible offsets.this.returnAddress in onEnter to identify immediate caller; combine with backtrace for full context.Process.getCurrentThreadId()), and timestamp for correlation.Java.use('android.util.Log').getStackTraceString(Java.use('java.lang.Exception').$new()) for Java-layer call chains.hexdump(ptr, {length: N, ansi: true}) to inspect memory regions with visual formatting./// <reference path="index.d.ts" />
// Structured logging with timestamp and thread ID
const log = (tag, ...args) => {
const tid = Process.getCurrentThreadId();
console.log(`[frida][t:${tid}] ${tag}:`, ...args);
};
// Backtrace helper: returns formatted call stack with module+offset
function bt(ctx, limit = 8) {
return Thread.backtrace(ctx, Backtracer.ACCURATE)
.slice(0, limit)
.map(addr => {
const sym = DebugSymbol.fromAddress(addr);
const mod = Process.findModuleByAddress(addr);
if (mod) {
const offset = addr.sub(mod.base);
return sym.name
? `${mod.name}!${sym.name}+0x${offset.toString(16)}`
: `${mod.name}+0x${offset.toString(16)}`;
}
return addr.toString();
});
}
// Address resolver: returns { module, offset, symbol }
function addrInfo(addr) {
const sym = DebugSymbol.fromAddress(addr);
const mod = Process.findModuleByAddress(addr);
return {
module: mod?.name || 'unknown',
offset: mod ? addr.sub(mod.base).toString(16) : '0',
symbol: sym.name || null
};
}
function hookExport(moduleName, exportName) {
const mod = Process.getModuleByName(moduleName);
const addr = mod.getExportByName(exportName);
if (!addr) { log('miss', moduleName, exportName); return; }
const offset = addr.sub(mod.base);
log('hook', `${moduleName}!${exportName} @ ${addr} (${moduleName}+0x${offset.toString(16)})`);
Interceptor.attach(addr, {
onEnter(args) {
this.caller = addrInfo(this.returnAddress);
this.fileDescriptor = args[0].toInt32();
log('enter', exportName,
`fd=${this.fileDescriptor}`,
`caller=${this.caller.module}+0x${this.caller.offset}`);
// Full backtrace (uncomment when needed):
// log('backtrace', exportName, '\n' + bt(this.context).join('\n <- '));
},
onLeave(retval) {
const ret = retval.toInt32();
if (ret > 0) {
log('leave', exportName, `ret=${ret}`);
}
}
});
}
function main() {
// hookExport('libc.so', 'read');
}
git clone https://github.com/oleavr/frida-agent-example then cd frida-agent-example.npm install (assumes Node toolchain present).npm run build.frida -l _agent.js -U -f com.example.android.agent/index.ts, keep the same logging; export minimal API surface. Map helpers (address resolvers, Java helpers) as needed.Interceptor for hooking functions, NativeFunction for calling exports/pointers, Module.enumerateExports/Imports for discovery, Memory.read/write/patch for data capture or inline patching, Stalker for instruction-level tracing, Java/ObjC runtime APIs when on Android/iOS.frida -l script.js -U -f <pkg>).frida --runtime=v8 --debug -l script.js -U -f <pkg> to enable the Node.js-compatible script debugger and attach via Chrome DevTools.frida, frida-trace, frida-discover, etc.).