// Locate the rekordbox .pdb serializer via known strings + magic-number constants. // Run headless: analyzeHeadless rbx -process rekordbox.exe -noanalysis \ // -scriptPath ~/ghidra_scripts -postScript FindPdb.java // Output goes to ~/ghidra_proj/findpdb.txt (and the log). import java.io.PrintWriter; import java.util.*; import ghidra.app.script.GhidraScript; import ghidra.program.model.address.Address; import ghidra.program.model.listing.*; import ghidra.program.model.scalar.Scalar; import ghidra.program.model.symbol.*; public class FindPdb extends GhidraScript { // Strings that mark the export code paths. static final String[] KEYWORDS = { "export.pdb", "exportExt.pdb", "USBANLZ", "PIONEER", "ANLZ", "HOT CUE BANK", "ORIGINAL ARTIST", "rekordbox/export" }; // Magic numbers maple currently guesses (various widths/combinations). static final long[] MAGICS = { 42343L, 54995L, // 0xA567 / 0xD6D3 per-track markers 0xD6D3A567L, 0xA567D6D3L, // combined 32-bit forms 788224L, // 0x000C0700 track bitmask 1004L, // 0x03EC "strange page" marker 0x64L, 0x44L, 0x24L, // page-flag bytes 19048L, 30967L // the schema author's original guesses }; PrintWriter out; void emit(String s) { println(s); if (out != null) out.println(s); } @Override public void run() throws Exception { out = new PrintWriter("/home/ntr/ghidra_proj/findpdb.txt"); Listing listing = currentProgram.getListing(); ReferenceManager rm = currentProgram.getReferenceManager(); FunctionManager fm = currentProgram.getFunctionManager(); emit("===== STRING XREFS ====="); DataIterator di = listing.getDefinedData(true); while (di.hasNext() && !monitor.isCancelled()) { Data d = di.next(); if (d == null || !d.hasStringValue()) continue; Object val = d.getValue(); if (!(val instanceof String)) continue; String s = (String) val; for (String kw : KEYWORDS) { if (s.contains(kw)) { emit("\nSTRING @ " + d.getAddress() + " : " + s.replace("\n", "\\n")); ReferenceIterator ri = rm.getReferencesTo(d.getAddress()); int n = 0; while (ri.hasNext() && n++ < 25) { Reference r = ri.next(); Function f = fm.getFunctionContaining(r.getFromAddress()); emit(" xref from " + r.getFromAddress() + (f != null ? " in " + f.getName() + " @ " + f.getEntryPoint() : " (no func)")); } break; } } } emit("\n===== CONSTANT XREFS ====="); Set targets = new HashSet<>(); for (long m : MAGICS) targets.add(m); Map counts = new TreeMap<>(); InstructionIterator ii = listing.getInstructions(true); while (ii.hasNext() && !monitor.isCancelled()) { Instruction insn = ii.next(); for (int op = 0; op < insn.getNumOperands(); op++) { for (Object o : insn.getOpObjects(op)) { if (o instanceof Scalar) { long v = ((Scalar) o).getUnsignedValue(); if (targets.contains(v)) { Function f = fm.getFunctionContaining(insn.getAddress()); emit(String.format("CONST 0x%X (%d) @ %s %s in %s", v, v, insn.getAddress(), insn.toString(), f != null ? f.getName() + " @ " + f.getEntryPoint() : "(no func)")); counts.merge(v, 1, Integer::sum); } } } } } emit("\n===== CONSTANT HIT COUNTS ====="); for (Map.Entry e : counts.entrySet()) emit(String.format("0x%X (%d): %d hits", e.getKey(), e.getKey(), e.getValue())); out.flush(); out.close(); emit("\nwrote /home/ntr/ghidra_proj/findpdb.txt"); } }