55 lines
2.5 KiB
Markdown
55 lines
2.5 KiB
Markdown
# re/ — rekordbox `export.pdb` reverse-engineering
|
|
|
|
Shared record of the Ghidra/Frida work behind maple's pdb writer. See the
|
|
top-level `README.md` for the high-level findings; this dir holds the raw
|
|
artifacts so others can reproduce and extend the analysis.
|
|
|
|
## What's here
|
|
|
|
`scripts/` — Ghidra headless scripts (run against an analyzed `rekordbox.exe`):
|
|
- `FindPdb.java` — locate the writer via string + magic-constant xrefs
|
|
- `DecPdb.java` — decompile an explicit list of addresses
|
|
- `CallTree.java` — walk a call tree, flag file-I/O / 4096-using functions
|
|
- `FindHash.java` — hunt the per-track hash: decompile `formatForSearch` + callers + content-string xrefs
|
|
|
|
`findings/` — script output (decompiled C + xref maps):
|
|
- `findpdb.txt` — string xrefs → the export functions; magic-constant hit counts
|
|
- `decpdb.txt` — decompiled `export.pdb` serializer call stack (DeviceSQL Page Manager)
|
|
- `calltree.txt` — call tree under the export orchestrator `FUN_14119d2d0`
|
|
- `hash.txt` — `formatForSearch` + callers + `djmdContent`/`ContentID` xrefs
|
|
|
|
## Reproduce (not committed: the binary + Ghidra project)
|
|
|
|
`rekordbox.exe` and the analyzed Ghidra project are **not** in the repo
|
|
(copyright + size). To rebuild from your own rekordbox install (Windows path
|
|
visible from WSL):
|
|
|
|
```bash
|
|
export JAVA_HOME=~/jdk/jdk-21.0.11+10 # Ghidra needs JDK 21
|
|
GH=/path/to/ghidra_12.1.2_PUBLIC
|
|
# one-time: import + analyze (~1h, 10G heap, 8 cores)
|
|
GHIDRA_HEADLESS_MAXMEM=10G "$GH/support/analyzeHeadless" ~/ghidra_proj rbx \
|
|
-import "/mnt/c/Program Files/rekordbox/rekordbox 7.0.5/rekordbox.exe" \
|
|
-overwrite -max-cpu 8 -analysisTimeoutPerFile 7200
|
|
# then run any script without re-analyzing:
|
|
"$GH/support/analyzeHeadless" ~/ghidra_proj rbx -process rekordbox.exe -noanalysis \
|
|
-scriptPath re/scripts -postScript FindPdb.java
|
|
```
|
|
|
|
Runtime→Ghidra address map: `static = 0x140000000 + module_offset` (Ghidra image
|
|
base `0x140000000`; ASLR runtime base varies — read `rekordbox.exe+0xOFFSET` from
|
|
the Frida agent, never hardcode it).
|
|
|
|
## Live capture (Frida, Windows)
|
|
|
|
`../tools/capture.mjs` (Node, `npm i frida`) + `../tools/rbx_pdb_capture.js`
|
|
(agent). Child-gates so the CIG-protected writer is hooked at spawn:
|
|
`node tools/capture.mjs --spawn`.
|
|
|
|
## Open questions
|
|
|
|
- per-track `0x14` (28-bit) — varies per track, not a plain crc32; likely a
|
|
stored `djmdContent` value. `0x18`/`0x1a` are a per-export constant.
|
|
- `index_shift` (row `0x02`) — positional (`~0x20`/row, resets per page) but the
|
|
exact formula isn't pinned. maple writes `0`; rekordbox doesn't.
|