The Ghidra project¶
The Ghidra project is where findings live. A discovery that only ever makes it into a report is one the next session will do again from scratch.
Where things are¶
| Project | firmware/iPodClassic.gpr, program osos-dec.dfu |
| Scripts | firmware/ghidra_scripts_project/ |
| ASM dump | firmware/claudedump/asm_current/ |
One file per function, asm_current/220cfc74.s, with an INDEX.txt and a functions.txt mapping
address to name. The dump annotates string arguments (PARAM -> s_Foo) and branch targets
(-> FUN_xxxx), so grep-by-string and grep-by-callee both work on the disassembly. Ghidra ships
prebuilt decompiler natives for Apple silicon, so nothing needs building.
The bulk C dump is gone¶
Deleted on 2026-08-16, deliberately. The decompilation reads like an answer without being one, and
what it hid here was substantial: nineteen separate *Hilited handlers inlined into a single switch
with the jump table swallowed, tail calls rendered as ninety-line bodies, adjustor thunks that
resolve to "no function" until the branch is decoded, a divisor dropped out of a scale, and a set of
colours that exist only as stack byte stores and never appear in the C at all.
What survives is one function's C, on demand, which is worth it for the shape the ARM makes expensive to read by hand - a forty-case property getter, say:
cd firmware && <ghidra>/support/analyzeHeadless . iPodClassic -process osos-dec.dfu -noanalysis \
-scriptPath ghidra_scripts_project -postScript DumpOneFunctionC 0x221ddf74
Anything it says about a number, a call target or a register-derived value still gets checked in the
.s before it reaches code.
Refresh the dump first¶
Not at the end of the session. First.
cd firmware && <ghidra>/support/analyzeHeadless . iPodClassic \
-process osos-dec.dfu -noanalysis -scriptPath ghidra_scripts_project \
-postScript DumpAllFunctionsAsm claudedump/asm_current
The dump is a snapshot, and it has sat at 192 named functions while the project held 457. Script
output arrives on stderr, prefixed INFO <Script>.java>.
Read the ARM whenever a call's target matters¶
The decompiler renders an indirect call as a plain one, which has caught this project out more than once:
- a display function's panel write is
ldr ip,[r0,#0xc] ; blx ipthrough a descriptor slot; - a gate dispatches through two vtable slots;
- a frame count comes from
TABLE + 4rather thanheader + 4, becauser1is reloaded midway.
In each case the C read as a settled answer while hiding the question.
Findings go back in through a script¶
Apply what was learned with an Apply*Findings.java script, then re-dump so the next read sees the
new names. Do it when a finding is established rather than at the end of a thread, because the end of
a thread is where findings get forgotten.
Before disassembling anything that looks like infrastructure¶
Grep the project's symbols and the reports first. At least one address has been "discovered" twice, having been named and written up the first time.
Two things worth knowing about this particular image¶
A method reached only through a vtable may have no function at all. It is missing from the dump
and reads as unreachable code. Dump the vtable first - the object's vptr, then the slot - and if the
target has no function, create it with an Apply*Findings script and re-dump. Creating one can
land a zero-length body when the bytes were never disassembled; the order that works is remove,
clear, disassemble, create. Hand-disassembling with objdump is the signal that this step was
skipped.
A name can be attached to the wrong function. getFunctionContaining will happily return an
unrelated function when the address you want has no function defined at it. Check getFunctionAt
first, or you leave a wrong name in the shared project, which is worse than no name.
Semihosting is instrumented and then thrown away. RetailOS instruments roughly 131 call sites
with SVC 0x123456 (ARM semihosting) and the retail handler is a stub (mov r0,#0 ; bx lr), which
discards all of it. Replacement firmware that implements that handler inherits the convention, and
OpenOCD semihosting, on day one.