80 lines
3.1 KiB
JavaScript
80 lines
3.1 KiB
JavaScript
// rpc_template.js — editable rpc.exports example for frida_rpc_serve.py.
|
|
//
|
|
// Three shapes every target needs, already wired:
|
|
// 1. add — a pure-JS export, proves the transport with zero target knowledge
|
|
// 2. getpackagename — a harmless Java probe (calls a framework method via JNI)
|
|
// 3. callnative — a native-call placeholder: wrap any exported symbol of any
|
|
// loaded module and call it with explicit types
|
|
//
|
|
// Adapt 3 to your target: the hardened .so that computes a signature or an
|
|
// encryption is usually either exported (Module.findExportByName) or bound at
|
|
// runtime via RegisterNatives (hook RegisterNatives first to learn the address,
|
|
// then pass the raw address to callnativeaddr).
|
|
|
|
'use strict';
|
|
|
|
function log(ev, data) {
|
|
try { send(Object.assign({ t: Date.now(), ev: ev }, data || {})); } catch (e) {}
|
|
}
|
|
|
|
// Export lookup that works across frida 16/17 (Module.findExportByName(null,..)
|
|
// was removed in 17; getGlobalExportByName is the replacement).
|
|
function resolveExport(name) {
|
|
try { if (Module.getGlobalExportByName) return Module.getGlobalExportByName(name); } catch (e) {}
|
|
try { if (Module.findExportByName) return Module.findExportByName(null, name); } catch (e) {}
|
|
try {
|
|
const libc = Process.findModuleByName('libc.so');
|
|
if (libc) return libc.getExportByName(name);
|
|
} catch (e) {}
|
|
return null;
|
|
}
|
|
|
|
rpc.exports = {
|
|
// 1) pure-JS demo — sanity-check the RPC transport itself.
|
|
add: function (a, b) {
|
|
return a + b;
|
|
},
|
|
|
|
ping: function () {
|
|
return 'pong';
|
|
},
|
|
|
|
// 2) harmless Java probe: the package name of the process we live in.
|
|
// Works on any app process with a live Java VM; needs no target knowledge.
|
|
getpackagename: function () {
|
|
let out = null;
|
|
Java.perform(function () {
|
|
const app = Java.use('android.app.ActivityThread').currentApplication();
|
|
out = app.getPackageName();
|
|
});
|
|
return out;
|
|
},
|
|
|
|
// 3) NATIVE CALL PLACEHOLDER — adapt module/name/ret/arg types to your target.
|
|
// moduleName null means "search every loaded module" (global export lookup).
|
|
callnative: function (moduleName, exportName, retType, argTypes, args) {
|
|
let addr = null;
|
|
if (moduleName) {
|
|
const base = Module.findBaseAddress(moduleName);
|
|
if (base === null) throw new Error('module not loaded: ' + moduleName);
|
|
addr = Module.findExportByName(moduleName, exportName);
|
|
} else {
|
|
addr = resolveExport(exportName);
|
|
}
|
|
if (addr === null) throw new Error('export not found: ' + exportName);
|
|
const fn = new NativeFunction(addr, retType, argTypes);
|
|
return fn.apply(null, args || []);
|
|
},
|
|
|
|
// Variant for symbols that never hit the export table (dynamically registered
|
|
// JNI, or an offset inside a stripped .so): pass module name + raw offset.
|
|
callnativeaddr: function (moduleName, offset, retType, argTypes, args) {
|
|
const base = Module.findBaseAddress(moduleName);
|
|
if (base === null) throw new Error('module not loaded: ' + moduleName);
|
|
const fn = new NativeFunction(base.add(offset), retType, argTypes);
|
|
return fn.apply(null, args || []);
|
|
}
|
|
};
|
|
|
|
log('RPC-READY', { exports: Object.keys(rpc.exports) });
|