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.
| # | Step | Deliverable | Who does it | Typical cost |
|---|---|---|---|---|
| 1 | Name the behaviour | One falsifiable sentence | You | 10 min |
| 2 | Fingerprint the stack | Layer + confidence + evidence | Agent | 5 min |
| 3 | Pick the cheapest layer | A plan that starts outside | You | 5 min |
| 4 | Inspect and instrument | Observations with file/line or call evidence | Agent | 30–90 min |
| 5 | Extract the rule | Pseudocode you can hand someone | You + agent | 20–40 min |
| 6 | Rebuild and diff | A minimal implementation + a divergence list | You | 1–3 h |
Loop checklist
Saved in this browser
0 / 6 steps complete
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:
- Holding the jump button makes the character rise higher than a tap does, by a fixed amount — so gravity is not the whole story.
- Typing a term in a different spelling still returns the result, and no request leaves the machine while you type.
- The six-digit code stays valid for a few seconds past its window, and the previous code also works.
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:
| Layer | What it can prove | What it cannot |
|---|---|---|
| 1. Behaviour from outside | Thresholds, response to edge inputs, timing | Why. Two different rules can look identical from here. |
| 2. Runtime observation | Order of operations, what a call actually receives, what is client-side | Anything that never executes on your path |
| 3. Readable artifacts | The rule as written: JS bundles, .NET metadata and CIL, Android packages | Well-obfuscated code, or logic moved server-side |
| 4. Disassembly | Instruction-level truth about a native binary | Nothing 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.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:
- 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.
- 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:
- Inputs — everything that can change the outcome, including state you did not think of as input (window size, elapsed time, previous action).
- State — what persists between triggers, and what resets.
- Constants — the numbers, each marked observed or inferred.
- Branch conditions — and the case you could not trigger.
- Outputs — what the user sees, as a function of the above.
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.
- 01
Fewer lines than feels responsible.
A rebuild you have to read twice will hide exactly the branch you are testing for.
- 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.
- 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.
- 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.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
| Trap | How you know | Which step fixes it |
|---|---|---|
| Opening a disassembler first | Two hours in, no question answered | 3 — behaviour and runtime are cheaper |
| Accepting a confident summary | Findings with no file, line or trace behind them | 4 — two passes, evidence per claim |
| Target too big to finish | Weeks in, still “understanding the architecture” | 1 — one falsifiable sentence |
| No diff | “It works!” — on the three cases you tried | 6 — same harness, both sides |
| Rebuilding the copy, not the rule | Your version cannot explain an input you have never seen | 5 — 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.