Files

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) });