HomeMethodThe 6-step loop

FrameworkFirst published 18 Oct 2026

How to reverse engineer software: the 6-step loop that works with AI agents

Most people who try this stall in the same place: they open a tool before they know what question they are asking. This is the loop I run instead — six steps, each with a defined output and an explicit reason to stop. It is written for the agent-assisted case (see how to set REA up in Claude Code), but the order holds without any agent at all.

Independent guide. This is our own workflow, not official project documentation, and we are not affiliated with the authors of morluto/rea. We document behaviour and rebuild rules in code we wrote. We do not publish cracks, licence bypasses, or download links for anyone's software.

The loop at a glance

Every step has a deliverable. If you cannot name the deliverable, you are not done with the step — and you should not start the next one.

#StepDeliverableWho does itTypical cost
1Name the behaviourOne falsifiable sentenceYou10 min
2Fingerprint the stackLayer + confidence + evidenceAgent5 min
3Pick the cheapest layerA plan that starts outsideYou5 min
4Inspect and instrumentObservations with file/line or call evidenceAgent30–90 min
5Extract the rulePseudocode you can hand someoneYou + agent20–40 min
6Rebuild and diffA minimal implementation + a divergence listYou1–3 h

Loop checklist

Saved in this browser

0 / 6 steps complete

Tick each step as you finish it. A long session keeps its place on this machine.

Step 1 — Name the behaviour

Deliverable: one sentence that someone else could test and potentially disprove.

“The search is really good” is not a target. It has no edge, so the agent will return a survey of the search implementation and you will read it and learn nothing you can use. The work of step 1 is to find the surprising detail — the thing you could not have guessed from the marketing.

A good step-1 statement contains: the trigger, the input, the observable output, and something that constrains the explanation. Examples:

Each of those implies a mechanism and rules one out. That is what makes it a target.

Scope rule, and it is the one people ignore. One behaviour per page. “Understand how the editor works” is a book, not a loop; “understand how it decides which line to re-wrap after a resize” is an afternoon. If you cannot finish the sentence with a comma and a constraint, you have scoped it too wide.

Turn my vague wish into a testable target. Ask me three questions,
then write one sentence naming the trigger, the input, the observable
output, and the constraint any explanation must satisfy. Do not start
inspecting anything until I approve that sentence.

What to cut in step 5 when this is your target

Pick a target whose behaviour you can trigger repeatedly and measure. Anything that depends on a network round-trip or a timer you cannot control will make step 6 noisy — you will not know whether your rebuild differs because you got the rule wrong or because the original is nondeterministic.

Step 2 — Fingerprint the stack

Deliverable: which layer the target lives at, with evidence, stated confidence, and what you did not check.

Stack decides cost more than difficulty does. An Electron app and a stripped native binary can contain the identical rule, and one of them is roughly twenty times cheaper to read. Skipping the fingerprint is how people end up disassembling something whose logic was sitting in a readable bundle the whole time.

What a fingerprint actually looks at: the file layout (an .app bundle with an app.asar inside, a pile of managed assemblies, a single ELF or Mach-O), whether a developer-tools surface is reachable, whether the process spawns helpers, and — the cheapest signal of all — whether anything leaves the machine while the behaviour happens.

Fingerprint only. Report: packaging, likely runtime, the entry points
you can see, and whether the behaviour looks client-side or server-
coordinated. Give each conclusion its evidence and a confidence level.
List what you could not determine. Do not read the logic yet.

The last line matters more than it looks. An agent that has started explaining will keep explaining, and you lose the ability to choose. Keep the fingerprint pass separate.

Step 3 — Pick the cheapest layer that can answer the question

Deliverable: a written plan that starts outside the binary and says what would force it down a level.

Cheapness is a strict order, not a preference:

LayerWhat it can proveWhat it cannot
1. Behaviour from outsideThresholds, response to edge inputs, timingWhy. Two different rules can look identical from here.
2. Runtime observationOrder of operations, what a call actually receives, what is client-sideAnything that never executes on your path
3. Readable artifactsThe rule as written: JS bundles, .NET metadata and CIL, Android packagesWell-obfuscated code, or logic moved server-side
4. DisassemblyInstruction-level truth about a native binaryNothing else — and it is slow

You descend one level only when the level above has produced a contradiction rather than a gap. “I could not see it” is not a reason to disassemble; it is a reason to look somewhere else. “I saw two behaviours that no single rule explains” is a reason.

Given the fingerprint, plan the cheapest route to the answer. Start at
behaviour, then runtime. Name the specific finding that would justify
moving to a lower layer. Confirm which analysis engines are actually
available to you before proposing one.

Native work is where tooling decides everything: static JavaScript and .NET need no analysis engine, but a native binary needs one of Hopper, Ghidra or IDA already installed and properly licensed. That trade-off is its own page — REA vs Ghidra — and it is the single biggest factor in how long the loop takes.

Step 4 — Inspect and instrument

Deliverable: observations, each tied to a location, plus an explicit list of what could not be confirmed.

This is the step agents genuinely changed. Tracing an action from a button down through an IPC boundary to whatever it calls, correlating two logs, opening seventeen files to find one constant — that was an afternoon of tedium, and it is now minutes. What did not change is that a summary is worth nothing without its evidence.

So run it in two passes, and never merge them:

  1. A

    Static pass.

    Read the artifacts without running anything. Output: candidate locations, boundaries, and the constants that look suspicious. Cheap, safe, and it gives the dynamic pass somewhere to point.

  2. B

    Dynamic pass.

    Trigger the behaviour while observing, and confirm or kill each candidate. Only what survives both passes is a finding.

Two passes, separately.
Pass A (static): list candidate locations for this behaviour, each with
file and line, and say what makes each one a candidate.
Pass B (dynamic): trigger the behaviour and report which candidates were
confirmed, which were ruled out, and the evidence for each call.
Then give me the open questions: what you could not confirm and why.

The open-questions block is the highest-value output of the whole step and the one people throw away. It is also the thing worth pasting into your own notes — the agent's stated limits are what stop step 5 from turning into a confident fiction. A worked failure ladder, where the interesting branches return nothing at all, is the clipboard copy that resolves and still pastes nothing.

Trust calibration. An agent that says “the value is clamped to 255” is making a claim. Ask what it saw. If the answer is “that is the usual convention”, it has inferred a rule it has not observed — real, useful, and must be marked as a hypothesis before step 5 treats it as a rule. The difference between a walkthrough and a fairy tale is entirely in this step.

Step 5 — Extract the rule

Deliverable: pseudocode you could hand another engineer who has never opened the target.

Findings are not the rule. A rule has five parts, and if any is missing you have a description, not a specification:

Write the rule as pseudocode. Separate observed from inferred: label
every constant and every branch with "observed" plus evidence, or
"inferred" plus what would falsify it. Note inputs you suspect matter
but never saw change.

The pass/fail test: read your pseudocode aloud and predict an input the original has never seen. If you cannot predict it, you have written down the cases you observed rather than the rule that produced them.

Marking observed-versus-inferred is what makes step 6 fast, because when the rebuild diverges you know exactly which line to attack. Skip it and every divergence becomes a fresh investigation.

Step 6 — Rebuild, then diff against the original

Deliverable: a minimal implementation plus a divergence list. Not a product.

Reimplement the rule in the smallest thing that can run — a page, a script, a test. Then run the same inputs through both, and compare outputs and timing. That comparison is the only real proof that step 5 worked.

  1. 01

    Fewer lines than feels responsible.

    A rebuild you have to read twice will hide exactly the branch you are testing for.

  2. 02

    Same input harness for both.

    Automate the input and record the output for each; eyeballing two apps in parallel does not find an off-by-one that only appears after 400 frames.

  3. 03

    Seek the boring case.

    Empty input, simultaneous input, input at the boundary of the window. Edge cases are where an inferred constant turns out wrong.

  4. 04

    Log divergences and loop back to step 4.

    A divergence is not a bug in the rebuild — it is an unnamed rule.

Rebuild this rule as a standalone implementation in my language. No
dependency on the original. Then list, explicitly, every place where
you had to guess, and give me the test that would confirm or kill each
guess.

The instrument for step 6 is the dinosaur jump lab. The write-ups that feed it are the jump arc and night mode and scoring.

Stop at the rule. The deliverable is understanding plus your own implementation. Copying the original's expression of it — its assets, its code, its protection scheme — is a different activity with different legal exposure and none of the engineering value. Check your licence and your jurisdiction before you ship anything; studying behaviour and interoperability are treated differently from reproducing protected code or defeating technical protection measures.

The six ways people get stuck

TrapHow you knowWhich step fixes it
Opening a disassembler firstTwo hours in, no question answered3 — behaviour and runtime are cheaper
Accepting a confident summaryFindings with no file, line or trace behind them4 — two passes, evidence per claim
Target too big to finishWeeks in, still “understanding the architecture”1 — one falsifiable sentence
No diff“It works!” — on the three cases you tried6 — same harness, both sides
Rebuilding the copy, not the ruleYour version cannot explain an input you have never seen5 — predict an unseen input

Two cases where you should not run this loop at all

Server-side enforcement. If the decision happens on someone else's machine, steps 4 and 5 can only approximate it from outside. Sometimes the honest deliverable is an empirical model of the visible inputs and outputs and nothing more. There is already a documented API. Reading the docs is faster, and a supported integration will not break next Tuesday because someone refactored a bundle.

Questions people ask before starting

How long does one loop take?

A single well-scoped feature in a JS or Electron target: one to three hours with an agent doing the reading. Native binaries are a different order of magnitude — days, because naming a rule at instruction level is slow work.

Do I need assembly?

No. Steps 1–3 deliberately avoid disassembly; most app-level features fall out of behaviour, runtime traces and readable bundles. Assembly only enters if step 3 sends you to the native layer.

Rule or copy?

A rule is a description you can reimplement. A copy is someone else's expression of it. The loop ends at code you wrote, which is the only version you should ship.

Why is the diff mandatory?

A rebuild that looks right usually only matches the obvious cases. Divergence under the same inputs finds the branch you never triggered — and tells you which rule is still missing.

Can I automate the whole loop?

Steps 2 and 4 automate well; the agent reads while you sleep. Steps 1, 3 and 5 resist automation, because they are judgement calls about what is worth knowing and what counts as evidence.

What if the target is obfuscated?

Obfuscation raises the cost of layer 3, not of layers 1 and 2. Behaviour and runtime observation do not care about readable identifiers, so try those before conceding.

Ready to run it? Install REA in Claude Code (ten minutes, with the failure modes pre-loaded), then pick a target from the walkthrough index.

Loop first published 18 Oct 2026 · last updated 18 Oct 2026