blog/reverse-engineering-across-domains.md
Reverse engineering across domains
What happens when you point one AI-assisted decompilation workbench at firewall firmware, two pinball machines, a 2001 MMO client, and a home server — and let bytes, not vibes, decide who is right.
Most of my first half of 2026 went into one thing: byte-exact decompilation of a handful of old console games, driven by fleets of AI agents wired into a disassembler and a battery of verification tools. By June the machine worked, which raised a better question than “can an agent write plausible C?” Plausible is cheap. The real question was whether the machine around the agent — tooling, orchestration, and above all the verification oracles that can prove a guess wrong — was about games at all.
So I pointed it at four things that have nothing to do with games, or with each other: the stripped firmware of enterprise firewalls, two pinball machines I own, a 2001 MMO client, and a home server. This post is about what happened.
The through-line: trust the bytes
┌── firewall firmware bytes vs. labels
one workbench ────┼── pinball machines bytes + behavior
+ oracles ├── 2001 MMO client pixel parity
└── home server app spec + tests
Every project below shares one obsession: an oracle that trusts bytes over rendered output. An AI agent will happily hand you decompiled code that looks correct. The only defense is an independent check that can disprove it — assembly matched instruction-for-instruction, an emulator reproducing identical behavior, a framebuffer pixel-identical to the original. Without that, you are generating confident-sounding text. With it, you have a research tool.
The second thread is an honesty ethos I care about more than any single result: catching your own over-claim is a win, not a loss. A finding you talk yourself out of, or a “match” you reject because it cheated, is a first-class outcome. That matters enormously once agents are optimizing against whatever metric you give them — they will find every hollow way to satisfy it. It matters just as much, it turns out, when the one over-claiming is me.
Firmware security: methodology, not exploits
The first retarget was the highest-stakes one: aiming the game-decomp workbench at the stripped firmware of enterprise network appliances — the kind of boxes that sit at the edge of corporate networks. This part stays deliberately high level, because the point is method and posture, not anything actionable.
The workbench indexed roughly 1,300 firmware images — about 237 GB, across some 26 vendors — defeated several proprietary container formats to reach the userspace binaries inside, and ran a managed fleet of disassembler workspaces against them. Individual targets were enormous: stripped, position-independent executables tens of megabytes in size, each taking around twenty minutes just to auto-analyze.
The engineering I am proud of is the verification harness. Game decompilation always has original assembly to diff against. Firmware has nothing. So the guiding creed became “trust the bytes, not the tool’s labels” — hard-won, because at one point the disassembler’s reported addresses were skewed from the real on-disk offsets and had already produced false analysis. The fix checks the actual instruction bytes instead of the tool’s rendering: cross-checking pseudo-C against raw disassembly to catch calls and arguments the C silently drops, and running functions under CPU emulation to prove or disprove behavior.
The findings went through responsible-disclosure triage, where the most important discipline was self-downgrade. Several promising leads got walked back — one all the way from “confirmed issue” to “not a finding” — once closer analysis showed the code was actually safe. That candor is the entire posture. A vulnerability you disprove is as valuable as one you confirm, because the alternative is crying wolf at a vendor. The work also fed a real upstream bug fix back into the open-source disassembler tooling it was built on. It ran as one intense multi-day burst and is paused, methodology intact.
Pinball firmware, and a research payoff
Next: the firmware of two pinball machines I physically own, which changes the stakes — the goal is eventually to re-flash the real hardware.
The newer machine runs embedded Linux on an ARM processor. Because I own it, byte-exact matching became the method for learning an undocumented platform, and the product a buildable homebrew modding kit. Verified milestones: several hundred of a few thousand tracked functions matched byte-for-byte, every peripheral node-firmware image decrypted, and a full-system emulator boot of the real firmware all the way to a shell — after defeating a cascade of graphics-stub, device-shim, and filesystem-remap blockers.
The research payoff lives here. I built a headless, cost-guarded LLM decompilation loop: take several independent first-shot attempts, keep the best, then run refinement turns each conditioned on the diff against the target, and stop on a match, a plateau, or a budget cap. The loop got generalized into reusable infrastructure and flowed back into the console work as a cross-architecture source of training data. It also handed me a dose of my own medicine: its most counter-intuitive early result — reasoning models losing outright to non-reasoning ones — turned out to be my harness truncating their answers, not a fact about models. What survived is narrower — bounded reasoning beats unbounded — and the walk-back is written up in the flagship post.
That was not the only self-audit. Partway through I discovered earlier campaigns had scored inline-assembly transcriptions as byte-exact “matches” — hollow wins that prove nothing about reconstructing real source. I quantified the taint, shipped a guard that rejects any candidate containing raw assembly, and split the metric into honest tiers. The same discipline as the firmware walk-backs, applied to the very number the agents were optimizing against.
The older machine, bare-metal ARM, had no known original compiler, so byte-exact matching was off the table. Correctness there was behavioral: rebuild the firmware, run it in an emulator, and diff what it does — display frames, solenoids, lamps, sound — against the original. The rebuild matched the original’s exact byte count, and a scenario-driven A/B harness reached zero display-frame mismatches across every shared frame. A self-contained cryptanalysis side-quest was needed just to recover the period-correct compiler in the first place. Paused, but it reached its goal.
Render parity on a totally different substrate
Then something genuinely different: a three-day sprint inside a collaborator’s research project where hundreds of agents inhabit a sandboxed 2001-era MMO world. For bots to see and act faithfully, the project needed a trustworthy client — and the only one that existed was a signed, closed-source, heavily obfuscated JAR.
The obfuscator’s main trick was injecting bogus exception-handling ranges that break the control-flow analysis in Vineflower, the best current Java decompiler: 238 methods across 41 of the client’s 71 classes simply would not decompile. Rather than hand-patch them, I wrote a bytecode pass that strips the bogus ranges before decompilation. One pass cut the failures from 238 to 4, cross-validated against a second independent decompiler.
The acceptance bar was byte-identical framebuffers across three independently built engines: the deobfuscated client, an untouched reference client as an oracle, and a from-scratch port of the software 3D renderer in another language. The crucial call was to preserve the original’s exact arithmetic quirks — shift precedence, unsigned right shifts, integer overflow wraparound — rather than clean them into idiomatic code, because any “correction” would have broken parity. A permanent pixel-diff regression harness was the mergeable proof of equivalence. Same discipline as the console work; completely different toolchain.

The mundane counterpart
The last one is not reverse engineering at all — it is the control group. A small home-server web app that reads a household’s music-listening history to decide which game content stays active on a drive and which gets moved — never deleted — into a backup folder. Proof that the same spec-first, safety-first rigor produces a clean product where there is no binary to fight.
Designed, built, tested, and deployed in a single evening, it opened not with code but with a live survey of the drive committed as pinned facts, then a full design and interface contract, and only then the implementation and a large test suite. The safety-critical core is the only module allowed to mutate the drive — an isolation enforced by a dedicated architectural test — and every move is dry-run by default, type-to-confirm, reversible, and guarded by a reconcile pass that halts on any drift between the database and physical reality. The rationale is blunt — a wrong move silently deletes a song from a family’s game — which is why it has correctly never fired for real. It even carries a thread of the same craft: a game console’s package format parsed from scratch, byte offsets and all, for a completely mundane task.
What it adds up to
None of these are detours. Together they are the actual argument: one AI-assisted reverse-engineering discipline, and one person directing agent swarms against it, carried credibly across firewall-class firmware, two pinball platforms, a JVM MMO client, and a household web app. What made that travel was never the agents. It was the insistence, everywhere, on an oracle that can tell you when you are wrong — and the willingness to listen when it does.