ghidra
This commit is contained in:
1 parent
6a6553077b
commit
f71a2a3512
23 files changed
+7773
-68
No files matched your search
@@ -0,0 +1,104 @@
|
||||
// Frida host (Node ESM) for the pdb-capture agent.
|
||||
// - Attaches the agent (rbx_pdb_capture.js) to rekordbox processes.
|
||||
// - Streams ALL their output to THIS console (agent lines are [pid]-tagged).
|
||||
// - STAYS ALIVE via the Node event loop -- no frida-CLI REPL/EOF problem.
|
||||
// - Uses CHILD-GATING (Windows-supported; spawn-gating is not) so children of a
|
||||
// gated process are instrumented AT BIRTH. This is how we reach the
|
||||
// rekordboxAgent that writes export.pdb: it's CIG-protected and refuses
|
||||
// post-hoc injection, but as a freshly-spawned child it can be hooked.
|
||||
// - Clears the captures/ folder on start.
|
||||
//
|
||||
// Setup (Windows, repo root, once): npm i frida
|
||||
// Run:
|
||||
// node tools\capture.mjs --spawn (RECOMMENDED: launch rekordbox under the
|
||||
// gate so every child incl. the DB agent is
|
||||
// hooked at birth. Close rekordbox first.)
|
||||
// node tools\capture.mjs (attach to running procs + gate their
|
||||
// future children; restart rekordbox to
|
||||
// respawn the agent as a gated child.)
|
||||
|
||||
import * as fridaNs from "frida";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const frida = fridaNs.default ?? fridaNs;
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const REKORDBOX = "C:\\Program Files\\rekordbox\\rekordbox 7.0.5\\rekordbox.exe";
|
||||
const AGENT = fs.readFileSync(path.join(__dirname, "rbx_pdb_capture.js"), "utf8");
|
||||
const CAP = path.join(__dirname, "..", "captures");
|
||||
const SPAWN = process.argv.includes("--spawn");
|
||||
|
||||
function clearCaptures() {
|
||||
fs.mkdirSync(CAP, { recursive: true });
|
||||
let n = 0;
|
||||
for (const f of fs.readdirSync(CAP)) {
|
||||
try { fs.rmSync(path.join(CAP, f), { force: true, recursive: true }); n++; } catch (e) {}
|
||||
}
|
||||
console.log(`[host] cleared ${n} file(s) from ${CAP}`);
|
||||
}
|
||||
|
||||
function isRbx(name) { return /rekordbox/i.test(name || ""); }
|
||||
|
||||
// Attach, load the agent, and gate this process's future children too.
|
||||
async function instrument(device, pid, label) {
|
||||
try {
|
||||
const session = await device.attach(pid);
|
||||
session.detached.connect(reason => console.log(`[host] detached ${label} (${pid}): ${reason}`));
|
||||
const script = await session.createScript(AGENT);
|
||||
script.logHandler = (level, text) => console.log(text);
|
||||
script.message.connect(msg => {
|
||||
if (msg.type === "error")
|
||||
console.error(`[host] ${label} (${pid}) script error:`, msg.stack || msg.description || msg);
|
||||
});
|
||||
await script.load();
|
||||
try { await session.enableChildGating(); } catch (e) { /* not always available */ }
|
||||
console.log(`[host] instrumented ${label} (${pid})`);
|
||||
return session;
|
||||
} catch (e) {
|
||||
console.error(`[host] FAILED to instrument ${label} (${pid}): ${e.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
clearCaptures();
|
||||
const device = await frida.getLocalDevice();
|
||||
|
||||
// Children of any gated process are suspended at birth -> instrument, resume.
|
||||
device.childAdded.connect(async child => {
|
||||
const name = child.identifier || child.path || String(child.pid);
|
||||
try {
|
||||
console.log(`[host] child spawned: ${name} (${child.pid})`);
|
||||
if (isRbx(name)) await instrument(device, child.pid, "child:" + name);
|
||||
} catch (e) {
|
||||
console.error(`[host] child handler error (${child.pid}): ${e.message}`);
|
||||
} finally {
|
||||
try { await device.resume(child.pid); } catch (_) {}
|
||||
}
|
||||
});
|
||||
|
||||
if (SPAWN) {
|
||||
console.log(`[host] spawning ${REKORDBOX}`);
|
||||
const pid = await device.spawn([REKORDBOX]);
|
||||
await instrument(device, pid, "main"); // enables child gating on main
|
||||
await device.resume(pid);
|
||||
console.log(`[host] resumed main rekordbox (${pid}); children caught at birth`);
|
||||
} else {
|
||||
for (const p of await device.enumerateProcesses()) {
|
||||
if (isRbx(p.name)) await instrument(device, p.pid, p.name);
|
||||
}
|
||||
console.log("[host] attached to running rekordbox processes (+child gating).");
|
||||
console.log("[host] >>> the export.pdb writer (rekordboxAgent) refuses live injection.");
|
||||
console.log("[host] >>> RESTART rekordbox -- the new agent spawns as a GATED CHILD and");
|
||||
console.log("[host] >>> gets hooked at birth. Then Export and watch for [SAVED] ...export.pdb");
|
||||
console.log("[host] >>> (or re-run with --spawn to launch rekordbox under the gate.)");
|
||||
}
|
||||
|
||||
console.log("[host] ready. Ctrl-C to stop.");
|
||||
process.stdin.resume();
|
||||
process.on("SIGINT", () => { console.log("\n[host] stopping."); process.exit(0); });
|
||||
}
|
||||
|
||||
main().catch(e => { console.error("[host] fatal:", e); process.exit(1); });
|
||||
Reference in new issue
Block a user