Firmware reverse engineering
Reverse-engineering the Charmera keychain camera
with AI and radare2
SHA-256 d8797ced94fc2aaf9c72f3ffcff91af4ac584b77c36437daccc276dc0f422008
The object
Teardown: PCB, battery, sensor, speaker
Product line (7 styles)
SPI programmer + SOIC8 clip on the flash
Flash chip under the clip
Method
file only says "data". The byte pattern at 0x200 (ldr pc,[pc,#..], the exception vector table) identifies ARM32 little-endian — that is why you pass -a arm -b 32. r2 defaults to x86 and reports havecode=false.
The problem
Left: the function list from afl. Right: the raw ARM of one function (pd 38 @ 0x1d714). This is the scale no human holds in their head — which is why the method and the AI come next.
Method
the commands: radare2, driven headless
a slice of the AI chat that guided the analysis
Loop: the AI proposes a hypothesis (e.g. "fcn.00021208 is a hex parser"); we check it in radare2 — seek, pd/pdf, axt, string search — then disassemble the next function. Every claim is tied to a concrete file offset (e.g. the checksum loop at 0x21458), so it stays verifiable.
Disassembly
No symbols, so r2 names them only fcn.XXXXXXXX. Counts are approximate (nearest domain anchor): 3,562 code functions, plus 352 aaa hits that land inside data (compressed resources, tables) — false recognitions.
Method & result
Same method for every asset: search the signature, validate it, find the consumer, land on the runtime moment. Found: 4 JPEG, 5 WAV, 12 GPZP. GP+ is Generalplus's own container for these UI assets (a name/ID table plus GPZP / JPEG / WAV payloads).
The object
2 MiB · SHA-256 d8797ced… · byte-sum 113C7C08 · no ELF · MMIO init table in the header · GP+ package at 0x120000
Rendering
All four frames, decoded — each 800×600, YUV 4:2:0 stacked from 3 layers.
Custom content
Custom content
Hardware from code
Logical blocks, not a physical PCB layout: sensor, JPEG, SPI-NOR, SD, display, buttons, audio, USB, battery.
Input
Firmware IDs and MMIO bit numbers — not physical pinout.
Input
Capture
Trigger → IMG.WAV → pipeline → C:\DCIM\PICT%04d.jpg
Audio
A keypress can trigger an action even when no sound plays — the selector checks options and state first.
Highlight
Search on the SD card, verify the byte-sum, then erase, program and read back — and what it forgets to check.
Update
The expected value comes only from the 8 hex chars in the filename; what is checked is the byte-sum of the whole file (mod 232) — not a CRC, not a signature.
Hidden features
Hidden feature
~780 KiB of SDK source in six layers (SDIO → driver → TCP/IP → WPA → app/HTTP). Debug log strings leaked the original function names — free reconnaissance.
Questions?
Tools: extract_gpz.py · jh_sum.py · check_update_candidate.py · verify_update.py (Unicorn) · radare2
Reproducible: hash-pinned scripts, exported evidence, no firmware written without a backup.