vi

blog

REA: letting an agent reverse engineer software, then testing the oracle too

REA lets a coding agent read a program that has no source, down to machine code, then rebuild that feature in its own code. Whether the rebuild is right is up to the oracle, and the oracle is code too, with its own bugs.

Published
AIAgentClaude CodeReverse engineeringMCP
An assembly window on the left passes through an oracle gate in the middle and becomes a new code window on the right, while a broken version is stopped in front of the gate

REA (Reverse Engineer Anything) lets a coding agent read a program that has no source, down to machine code, then rebuild that feature in its own code. Models are fairly good at reading and rewriting. The oracle, the check that compares the new version with the original, decides whether the rebuild is right, and an oracle with a blind spot will quietly approve hundreds of wrong functions.

REA is an MCP server plus a CLI, and the analysis runs on your machine

REA is by morluto, under the MIT license. The agent calls it over MCP; the same set of tools can also be called from the terminal. Measured on 29 Oct 2026, the repo had 403,103 stars and the latest release was rea-agents 6.8.0, out on 25 Oct 2026. The analysis runs locally and the program is not uploaded anywhere; the agent only sees what REA returns, and the model provider's data policy applies to that part.

REA can read the following kinds of programs, each needing a different toolset:

TypeAlso needs
JavaScript, Electron appsNode.js and npm
Native programs, gamesHopper, Ghidra, IDA or Kuna
.NET assemblies (metadata, CIL instructions)nothing, static reading only
Android APKsJADX running headless and a full JDK
Websites, HAR filesa Chrome-family browser
FirmwareBinwalk or Unblob on Linux
WASM, Go binaries, EVM bytecode, recorded Linux crashesWABT, no Go install needed, pwntools depending on the type

Static analysis of JavaScript and .NET only reads files; it does not run the app. Supported clients include Claude Code, Codex, Cursor, Gemini CLI, Antigravity, Windsurf, OpenCode, VS Code and eight others.

Installing REA in Claude Code

The commands come from the Get started page:

bash
# Node 22 (>=22.23.4), 24 (>=24.22.1) or 26.12.1 and later
node -v

# connect REA to the agent: prints a plan, backs up the config, asks before editing
npx rea-agents@latest setup

# restart the agent, then check
npx -y rea-agents@latest doctor --client claude_code --json

Only if you need to read native programs do you also install Ghidra 12.1.x and a 64-bit JDK, version 21 or later:

bash
export GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra_12.1.4_PUBLIC
export JAVA_HOME=/absolute/path/to/jdk-21
npx -y rea-agents@latest doctor --provider ghidra --json

On Windows x64, native support is still experimental and read-only. Update REA with rea update. To try it first on an Electron app already on your machine:

bash
npx -y rea-agents@latest analyze-javascript-application "/absolute/path/to/app" --json

On macOS, Electron apps keep their code in /Applications/<App>.app/Contents/Resources/app.asar.

Three kinds of oracle: compare bytes, compare behavior, run the whole program

An oracle is any check that compares the rebuild with a reference you trust. There are three levels. A byte oracle compiles the new version and compares it with the original's machine code. A behavior oracle feeds the same set of inputs to both and compares the outputs. A runtime oracle builds the whole program and uses it. The examples on rea.tools and in the Touhou project in the next section use all three levels.

DX-Ball: 63 bytes matching byte for byte

DX-Ball is a 1996 Windows game. REA traced a sound-playing call to the function that computes the left/right stereo offset of the sound, and the agent rewrote that function in C. The behavior oracle ran 3,205 test cases against the original x86 code, and all of them passed. The byte oracle recompiled it with the exact same compiler, VC4.0, and all 63 bytes of the function matched the original.

Windows Calculator: why 200 + 10% gives 220

REA pulled the branch that handles the % key out of the Calculator installed on the machine:

text
0x180124949: CMP EAX, 0x5c
0x18012494c: JZ 0x180124aba
0x180124952: CMP EAX, 0x5b
0x180124955: JZ 0x180124aba
0x18012495b: MOV EDX, 0x64

In Calculator's public source, IDC_MUL is 92 (0x5c) and IDC_DIV is 91 (0x5b); 0x64 is 100. That gives the rule: after a multiplication or division, % takes current / 100; after an addition or subtraction, % takes current * previous / 100. So 10% after 200 + is 20, and the result is 220. The version read was Windows Calculator 11.2508.4.0, with REA 4.1.0.

The same page has the browser's dinosaur game (wayou's edition built from Chromium): speed starts at 6, each update adds 0.001, and the cap is 13. They called the original update function itself to check: after 4,000 updates the speed is 10.0, after 10,000 it is 13.0.

TH08: a 0.0 constant in place of 128.0 still passed the byte check

N0zoM1z0 rebuilt the Touhou games with an agent and wrote up the process in a post on the REA blog on 10 Oct 2026.

In TH08, one function had passed the exact byte comparison. Then in September, a port to the Switch reported a bug in the automatic item collection feature. The rebuilt code compared the player's power against 0.0, while the original compared it against 128.0, the maximum power threshold. Power is never negative, so the condition in the rebuild was almost always true.

The bug slipped through because the comparison tool adjusts addresses before comparing instructions. That step is necessary, since the rebuilt binary may place a constant somewhere else in the file. But it does not check the floating-point value stored at that address.

The fix (PR #11, merged on 2 Sep 2026) has two halves: fix the source, and let the comparison check the actual bytes of the constant as well. After that came a review of everything that had ever passed the weak check: 1,548 floating-point constant references, which turned up 12 more wrong constants in five functions that had already been approved. The lesson the author wrote down, word for word, is "Test the oracle too": try the cases it should reject, and when it misses a bug, re-examine every earlier result that relied on it.

TH095 done in about 12 days, with the repo as memory

TH08 continues the public rebuild by the GensokyoClub group. N0zoM1z0's own work started on 13 Aug 2026; by 19 Aug the tracking log had source for all 1,107 of the game's functions, and on 24 Aug there was a playable Linux port.

TH095 started on 29 Aug with no confirmed functions. On 7 Sep there was source for all 697 functions, on 8 Sep 696 functions matched exactly, and on 10 Sep the Windows i386 build was playable, about 12 days after it began. TH04, a DOS game for the PC-98 made of four programs running together, has a rebuilt DOS version; the 64-bit port is still in progress.

The second game went faster because it did not start from zero: the code, the evidence, the checks and the lessons from TH08 were already in the repo, including the check fixed after the 128.0 incident. Four of the five lessons the author drew:

  • Give the agent the authority to investigate on its own: set the goal and the acceptance criteria, and let the agent choose what to try next.
  • Use the repo and Git as memory: when the conversation ends the repo is still there, and the next session reads the checkpoint and carries on.
  • Let evidence decide what you claim; "unknown" is a valid answer.
  • Test the oracle too.

On the human side, things have not been smooth. GensokyoClub now develops in private, calls the AI-made reverse engineering projects and ports "grifters" taking credit for their work, and does not accept pull requests written mostly by AI. N0zoM1z0 kept the group's commit history in the continuation.

Clean room: read the original's behavior, not its code

Clean room design dates back to the early 1980s. One team studies the original and writes down what it does. The other team may only read that description to write code, and never sees the original code. Compaq built an IBM PC compatible BIOS this way for the Compaq Portable, announced in November 1982.

US courts have repeatedly protected the "reading" half. In 1992, Sega v. Accolade held that disassembling machine code to read it, in order to make a compatible game, is fair use. In 2000, Sony v. Connectix allowed a PlayStation emulator built after studying Sony's BIOS, because the final product contained none of Sony's code.

The ArtCraft suite follows that same spirit. It has 13 apps written from scratch in Rust, under MIT or Apache-2.0: PhotoCraft targets Photoshop, FilmCraft targets Premiere Pro, and the others cover photos, vectors, motion graphics, page layout, interface design, PDF, documents, spreadsheets, slides, audio and CAD. Most are "In development"; PhotoCraft and PdfCraft are "Early alpha". FilmCraft writes its own H.264, HEVC, ProRes and AAC codecs from the public specifications.

Section 2 of FilmCraft's AGENTS.md, the file every human and agent contributor must follow, is titled "Clean-room code":

Never read, disassemble or copy anything inside Adobe application bundles; file names and listings only. Observe behaviour by using the app; never capture its Home screen, recent projects, account info or file browsers.

So FilmCraft's oracle is Premiere's behavior observed from the outside, plus ffmpeg/ffprobe used only as an external test oracle, never linked in or bundled.

Vietnam's Intellectual Property Law has no provision on reverse engineering

Reading the consolidated text of Vietnam's Intellectual Property Law published in Official Gazette No. 197 of 10 Apr 2026, which includes the amendments up to Law 131/2025/QH15, no article mentions "reverse engineering" or "compatibility". Two articles concern computer programs:

  • Article 22, clause 1 only lets a lawful user make one backup copy, which may not be passed to anyone else.
  • Article 25, clause 3 excludes computer programs from the exception in clause 1 for making a copy for your own research and study: "Copying as provided in clause 1 of this Article does not apply to architectural works, works of fine art, or computer programs".

The US has the Sega and Connectix precedents protecting disassembly for the sake of a compatible product; Vietnamese law has no matching provision. The license you clicked to accept when installing an app is a separate layer of constraints too. This section is not legal advice; if you plan to build a commercial product from reverse engineering results, ask a lawyer before you write the first line of code.

REA draws its own lines in its DISCLAIMER

REA's DISCLAIMER.md states that the tool is for security research, malware and incident analysis, vulnerability research with coordinated disclosure, CTFs and education, interoperability, software preservation, and debugging or auditing software you own or are authorized to test. It does not help bypass DRM, license checks or anti-cheat, does not make game cheats, does not bypass authentication, and does not copy proprietary software or recreate protected code and assets.

One sentence gets little attention: "Obtaining source-like output or an analysis report does not grant rights to the original program, its data, or the generated output." The source-like code REA produces gives you no rights to the original program. REA also refuses cracked copies of IDA, Hopper or JEB; commercial tools need a real license.

A two-room loop with Claude Code and /goal

To bring clean room design to an agent, split the work into two sessions that share no folder and are connected only by a SPEC.md file.

Room A, the reader: a Claude Code session with REA that investigates and writes the spec. Nothing else leaves this room.

text
Use REA to investigate how [feature] works in [app]. Label each claim as an
observation, an inference or unknown, with its evidence. Then write SPEC.md: behavior,
inputs, outputs, edge cases and the open unknowns. No code, no addresses, no function
or resource names taken from the original.

Room B, the builder: a fresh session in a different folder that only receives SPEC.md and never sees the analysis. This room runs on /goal: Claude keeps working across turns until the condition is met. After each turn, a small, fast model reads what the session printed and evaluates the condition. That model does not run commands or read files itself, so the condition has to be something the test output can prove.

text
/goal every case in SPEC.md passes in ./my-version, the oracle rejects all 3 planted
broken versions, and no file outside ./my-version is modified. Or stop after 25 turns.

The /goal documentation recommends a condition with a measurable end state, an explicit check (npm test exits 0), the constraints that must not be broken, and a clause that limits the number of turns. How to build a loop around /goal is in the loop engineering post.

Planting three broken versions to test the oracle

The "3 planted broken versions" in the condition above is the TH08 lesson turned into a habit: before you trust a green run, give the oracle a few versions that are certainly wrong. If one slips through, the oracle has a blind spot, and every earlier green result is suspect.

I wrote the % key rule above as a function, together with three broken versions: one changes a constant, one swaps the two branches, and one forgets the division branch. Then I had two oracles grade them: one that only tests addition, like the 200 + 10% example everyone quotes, and one with at least one case for each operation.

js
// oracle.mjs, run: node oracle.mjs
export function percent(op, previous, current) {
  if (op === '*' || op === '/') return current / 100
  return (current * previous) / 100
}

const apply = (op, a, b) => ({ '+': a + b, '-': a - b, '*': a * b, '/': a / b })[op]
const calc = (fn) => (op, a, b) => apply(op, a, fn(op, a, b))

const mutants = {
  'constant 100 becomes 10': (op, p, c) => (op === '*' || op === '/' ? c / 10 : (c * p) / 10),
  'swapped branches': (op, p, c) => (op === '*' || op === '/' ? (c * p) / 100 : c / 100),
  'missing ÷ branch': (op, p, c) => (op === '*' ? c / 100 : (c * p) / 100),
}

const weak = [['+', 200, 10, 220], ['+', 50, 50, 75], ['+', 0, 10, 0]]
const strong = [...weak, ['-', 200, 10, 180], ['*', 200, 10, 20], ['/', 200, 10, 2000]]

const passes = (fn, cases) =>
  cases.every(([op, a, b, want]) => Math.abs(calc(fn)(op, a, b) - want) < 1e-9)

for (const [name, cases] of [['weak oracle', weak], ['full oracle', strong]]) {
  console.log(`${name}: correct version ${passes(percent, cases) ? 'passes' : 'FAILS'}`)
  for (const [m, fn] of Object.entries(mutants)) {
    console.log(`  ${m}: ${passes(fn, cases) ? 'SLIPS THROUGH (blind oracle)' : 'caught'}`)
  }
}

Output on Node 22:

text
weak oracle: correct version passes
  constant 100 becomes 10: caught
  swapped branches: caught
  missing ÷ branch: SLIPS THROUGH (blind oracle)
full oracle: correct version passes
  constant 100 becomes 10: caught
  swapped branches: caught
  missing ÷ branch: caught

The weak oracle catches the two obvious bugs and misses the third, because it has no case that goes through division. The correct version passes both oracles, so running only the correct version never reveals that blind spot; you need a broken version that slips through.

I computed the numbers in strong from the rule REA extracted; they were not measured on the real Calculator. For a behavior oracle in the full sense, type the same calculations into Calculator on Windows and compare.

Before you trust an oracle:

  • Every branch in the spec has at least one test case that goes through it.
  • There are at least three planted broken versions: a changed constant, swapped branches, an off-by-one at a boundary.
  • The oracle compares values too, not just shape: the bytes of the constant, not only the bytes of the instruction.
  • If the oracle misses a bug, rerun everything that ever passed it.

Opus 5.5 for reading machine code: long context, a lower price, and where it refuses

REA hands the agent raw evidence: assembly, pseudocode, call graphs, unresolved references. The model has to turn that into a correct explanation, a spec someone else can build from, and code that passes the oracle. Work like this runs for a long time and eats a lot of context.

Opus 5.5 has a 1M-token context window, a maximum output of 128K tokens, and medium as its default effort. It costs 4 USD / 20 USD per million input / output tokens; cache reads cost 0.20 USD per million tokens, 60% cheaper than Opus 5.

In the launch post, Anthropic had Opus 5.5 and Fable 5.1 port HAProxy from C to Rust. Both rewrites passed nearly all of HAProxy's own regression tests; Opus 5.5 finished in 9.5 hours against 12 hours for Fable 5.1, at 51% lower cost. That is exactly the rebuild step, with the original project's test suite as the oracle.

A suggested effort split by room: leave reading and mapping at the default medium, use high for writing the spec because every gap in the spec becomes a bug in room B, and xhigh or max for verification and pointer-heavy code.

Opus 5.5 has cybersecurity safeguards similar to Fable 5.1's: most cybersecurity tasks get routed to Opus 4.8. Tracing a feature in your own app, or rebuilding a game function to understand it, is fine. For anything that looks like breaking a protection, writing an exploit or cracking a license, expect to be routed to another model or refused; that is the same line REA draws in its DISCLAIMER, seen from the model's side.

Where REA cannot reach

  • Code that runs on a server. What runs on someone else's server was never sent to your machine, so REA has nothing to read.
  • Decompiled code is not the original source. It is a rebuild, which is why you need an oracle.
  • Stripped C++ binaries are still hard. An Electron app and a native file with no symbols left are two different jobs.
  • Commercial tools need a real license. REA does not come with an IDA, Hopper or JEB license.

This post is based on REA's documentation and the examples REA published itself, plus the oracle script I ran on my machine. I have not yet had REA read a real app; the first try should be REA's own Notes sample app, then an Electron app I use every day, and always starting with three planted broken versions before trusting a green run.

Related posts

all posts →