โจ What it does
EasyRE-Agent runs an AI agent inside a Docker container. The container holds your reverse engineering toolchain, and the agent uses it to solve a task you give it.
๐ tasks/crackme โโบ ๐ triage โโบ ๐ค agent (your model) โโบ โ
verifier โโบ ๐ report.md / ๐ก hints.md
| Feature | ||
|---|---|---|
| ๐ค | Any model | Claude Code, OpenAI Codex, or any OpenAI-compatible API: OpenRouter, Ollama, LM Studio, vLLM |
| โ | Verified answers | Checks against a regex, a success message, or your own command. The agent keeps working until a candidate passes. |
| ๐ | Hint mode | The agent solves privately and gives you graded hints, one at a time. The answer stays hidden, including in the console output. |
| ๐งโ๐ซ | Tutor mode | An interactive tutor: you tell it what you did, and it guides you step by step without spoiling the answer |
| ๐ฐ | Stats | Totals of time, tokens and cost per model over all jobs |
| โฑ๏ธ | Budgets | Time and token limits per job |
| ๐ | Resumable | Continue a job later with more time or a different model |
| ๐ | Triage | File types, runtimes and packers detected up front |
| ๐ | Reports | Answer, solution path, scripts, dead ends, exact model, time and tokens |
| ๐งฐ | Your toolchain | Plug-in install script plus Markdown skills (playbooks) the agent reads on demand |
๐ฏ Why it exists
Reversing a crackme or a CTF binary is mostly the same loop every time: figure out what the file is, pick the right tools, find the check, pull out the constants, write a small solver, and confirm the answer. An AI agent can do much of that loop, but in practice three things get in the way:
- The toolchain. A typical task needs a mix of Ghidra, Detect It Easy, Wine, and decompilers for .NET, Python, Java or JavaScript. Installing and wiring all of that on every machine is slow and fragile. EasyRE-Agent ships it as one Docker image that behaves the same on Windows, Linux and macOS.
- Guessing. Without tools and a checker, a model tends to produce an answer that only looks plausible. Here the agent works on the real file with real tools, follows a playbook (skill) for that kind of target, and an answer counts only when the program or your checker accepts it.
- Lock-in. It works with Claude, Codex or any OpenAI-compatible model, including local ones, so you can use what you already have and compare models on the same task.
It is also built for learning. Being handed the answer teaches little, so hint and tutor modes let the agent solve the task privately and then guide you toward the solution without spoiling it.
EasyRE-Agent is meant for crackmes, CTF reversing challenges and puzzle binaries, not for analyzing real malware (see
๐ Quick start
Pick your system: ๐ป Windows ยท ๐ง Linux ยท ๐ macOS
You need about 10 GB of free disk space (see ๐พ Disk space).
๐ป Windows
- Install Docker. Get Docker Desktop, install it with the default settings (WSL 2), and start it.
- Run the setup. Double-click
setup.cmdin the project folder. It builds the image, asks which model to use, logs you in and checks everything (see โ๏ธ What the setup does). - Add a task. Put the files into a folder under
tasks:tasks\crackme\crackme.exe tasks\crackme\task.md (description, optional) - Solve it. Open PowerShell or Command Prompt in the project folder:
The first command solves the task. The second opens the report.
.\easyre.cmd run crackme --success "Correct" .\easyre.cmd open
๐ง Linux
- Install Docker. Install Docker Engine with the Compose plugin. Then let your user run Docker without
sudo, and log out and back in:sudo usermod -aG docker $USER - Run the setup in the project folder:
- Add a task: put its files into
tasks/crackme/(plus an optionaltask.mdwith the description). - Solve it:
sh easyre.sh run crackme --success "Correct" sh easyre.sh open
The image is x86-64 (Wine needs 32-bit x86 libraries). On ARM Linux it runs only with x86 emulation (qemu/binfmt) and is slow.
๐ macOS
- Install Docker. Get Docker Desktop for Mac and start it. On Apple Silicon (M1โM4), enable Settings โ General โ Use Rosetta for x86_64/amd64 emulation. The image is x86-64, so it runs emulated there: it works, but builds and runs more slowly.
- Run the setup in Terminal, in the project folder:
- Add a task: put its files into
tasks/crackme/(plus an optionaltask.mdwith the description). - Solve it:
sh easyre.sh run crackme --success "Correct" sh easyre.sh open
๐ The examples below use the Windows form
.\easyre.cmd. On Linux and macOS, writesh easyre.shinstead; everything else is the same.
โ๏ธ What the setup does
-
Checks that Docker runs.
-
Builds the image (the first build takes several minutes).
-
Asks which model you want to use and logs you in:
Choice You need ๐ Claude subscription (recommended) Claude Pro or Max account ๐ Claude API key Anthropic API key ๐ข ChatGPT account via Codex ChatGPT account ๐ข OpenAI API key OpenAI API key ๐ต OpenRouter OpenRouter API key ๐ Google Gemini Gemini API key from aistudio.google.com ๐ฆ Ollama (local, free) Ollama running with a model that supports tool calling ๐ฅ๏ธ LM Studio (local, free) LM Studio server running -
Runs
easyre doctorto check that everything works.
--success is the text the program prints when the answer is right. Leave it out if the program prints nothing like that.
๐ฉบ Something wrong?
doctor checks your model profile, credentials, API connection, Wine and skills. It shows each problem together with how to fix it. To switch models later, run the setup again, or see ๐๏ธ Profiles.
Colors: the output is colored. If you see codes like โ[32m instead of colors, use Windows Terminal, or add NO_COLOR=1 to .env.
๐น๏ธ Usage
easyre run TARGET [options] solve a task (folder or file under ./tasks)
easyre resume JOB [options] continue a job
easyre hint JOB [--answer] next hint (or reveal the answer)
easyre tutor JOB continue a tutor conversation
easyre jobs list jobs
easyre open [JOB] [folder] open a job's report (or tutor chat, or folder)
easyre profiles list model profiles
easyre doctor check that the setup works
easyre stats time, tokens and cost per model
easyre tools [JOB] bar chart of the shell tools the agent used
easyre close JOB mark a job as closed (resume reopens it)
easyre rm JOB... | --FILTER delete job folders; filters: --open --closed --unsolved --solved --unverified --all
easyre login claude|codex authenticate a backend
| Option | Description |
|---|---|
--desc / --desc-file |
Task description. Default: task.md, opis.txt or README.md from the task folder. |
--success REGEX |
Output that means the answer was accepted |
--verify CMD |
Checker command. {} is replaced by the candidate; otherwise the candidate goes to stdin. |
--flag-format REGEX |
Required answer shape, e.g. "flag\{.+\}" |
--flag-sha256 HEX |
SHA-256 of the known answer. easyre check compares hashes, so the agent gets a real correct/incorrect without ever seeing the flag. For testing on already-solved tasks. |
--profile |
Model profile from config/backends.toml |
--backend |
claude (default), codex or openai |
--model |
Model name for the backend |
--effort LEVEL |
Reasoning effort. Claude: low, medium, high, xhigh (extra in the Claude app; both work), max; default is high, but medium on Opus 5.5 and Haiku 5.5. Codex: none, minimal, low, medium (default), high, xhigh, max (support depends on the model). OpenAI-compatible: the same names, sent as reasoning_effort only when set (non-reasoning models reject it). Unset = the backend default. A profile can set effort = "..." in config/backends.toml. |
--mode |
solve (default), hint or tutor |
--budget-min / --budget-tokens |
Time budget in minutes (default 120) and output-token budget |
--logs |
Stream the agent's commands live instead of the progress bar |
-i |
Interactive session (Claude Code / Codex UI) |
๐ Examples
.\easyre.cmd run crackme --success "(?i)correct" .\easyre.cmd run keygenme --verify "wine /work/src/app.exe {}" --success "Valid" .\easyre.cmd run ctf-rev --flag-format "CTF\{.+\}" .\easyre.cmd run crackme --mode hint .\easyre.cmd hint crackme .\easyre.cmd run crackme --mode tutor --success "Correct" .\easyre.cmd tutor crackme .\easyre.cmd run crackme --backend openai --model qwen3-coder --budget-min 30 .\easyre.cmd resume crackme --budget-min 60
Testing on solved challenges (e.g. past FLARE-On). Give the flag shape and the SHA-256 of the known flag. easyre check then answers correct/incorrect for real, a wrong-shape answer is rejected at once, and the flag itself never reaches the agent:
# SHA-256 of the known flag (example flag, not a real one) (Get-FileHash -InputStream ([IO.MemoryStream]::new([Text.Encoding]::UTF8.GetBytes('example_fl4g@flare-on.com')))).Hash.ToLower() .\easyre.cmd run ThreatInvaders --flag-format ".*@flare-on\.com" --flag-sha256 <hash-from-above>
On Linux/macOS: printf '%s' 'example_fl4g@flare-on.com' | sha256sum. Without a known flag, --flag-format ".*@flare-on\.com" alone already stops wrong-shape answers. The hash sits in the job's .easyre/job.json: it guards against wrong answers, not against brute force, so a short answer (a 4-digit PIN) can be recovered from it - use it for testing, not as a secret.
๐ Opening results
.\easyre.cmd open .\easyre.cmd open crackme .\easyre.cmd open crackme folder
- Opens the newest job's
report.md(ortutor.mdfor tutor jobs) in your default app. - Opens the newest job whose name starts with
crackme. - Opens that job's folder in Explorer.
open runs on your computer, not in Docker, so it starts instantly.
๐งโ๐ซ Tutor mode
.\easyre.cmd run crackme --mode tutor --success "Correct"
- The agent first solves the task privately. The console shows only progress, so nothing spoils the answer.
- Then a chat starts. Tell it what you did and what you see, and it replies to where you are: it confirms the right path, warns about dead ends, and suggests one small next step.
tutor> This is a small 64-bit console program. What have you tried so far?
you> I found main, it calls checkPassword. What next?
tutor> Good. Look at what checkPassword compares its argument with...
| Command | What it does |
|---|---|
/hint |
Ask for a nudge |
/answer |
Reveal the answer (asks for confirmation) |
/quit |
Leave. Continue later with easyre tutor <job> |
The conversation is saved to tutor.md in the job folder. If a reply accidentally contains the answer, it is replaced with [hidden].
โ How verification works
--flag-formatmust match, if set.- If
--flag-sha256is set, the SHA-256 of the answer must match it. That result is final; nothing is run. --verifyruns, and its output is matched against--success. Without--success, exit code 0 counts as success.- If only
--successis set and the task has exactly one.exeor ELF file, that file is run automatically with the candidate on stdin (through Wine for.exe). - With no verifier,
easyre checkonly records candidates: the agent must prove the answer and submit it witheasyre check --final. The report then saysanswered (unverified), notsolved, andeasyre resumereopens the job. Pass--flag-formatto at least reject answers of the wrong shape.
๐ง Commands the agent uses
easyre --help also lists two commands marked (agent). The agent runs them inside the job's container; you do not need them:
| Command | What it does |
|---|---|
easyre check '<answer>' |
Runs the verifier above on a candidate (with no verifier: recorded as a candidate only, unless --final). Exit 0 = accepted: the answer is stored in the job and the agent writes the report. Any other exit = rejected, and the agent keeps working. Every attempt is logged in .easyre/attempts.log. |
easyre done |
Hint mode only: confirms hints.md has enough sections and does not contain the answer, then marks the hints as finished. |
Run from your own console, they only print a note, because there is no job to check against. Use easyre jobs and easyre open to see results.
๐ฌ How a job runs: a crackme, step by step
The example is a console crackme that asks for a serial and prints Correct! or Wrong:
tasks/keygenme/keygenme.exe
tasks/keygenme/task.md Find the valid serial.
.\easyre.cmd run keygenme --success "Correct"
Steps 1โ3 and 5โ7 are fixed code in easyre. Step 4 is what a model typically does: it is not scripted. The model picks its tools itself, guided by the skills and the rules in config/AGENT.md.
1. Start. easyre.cmd runs docker compose run --rm easyre easyre run โฆ in a fresh container. tasks/ and jobs/ are mounted, and so are easyre, config/ and skills/ (live, so edits need no rebuild).
2. Job setup.
- Creates
jobs/keygenme-<timestamp>/withsrc/(a copy of the task, archives extracted),agent/and.easyre/. - Takes the description from
task.md(oropis.txt,README.md). - Links the job as
/work, the agent's working directory. - Picks the verifier:
--successis set and the task has exactly one.exe, so the verifier iswine /work/src/keygenme.exe, with the candidate on stdin.
3. Triage. For every file: file, byte markers (PyInstaller, .NET, UPX, VMProtect, Themida, Go, Rust, Delphi, AutoIt, WASM, Electron, โฆ), repair of a broken MZ header, diec, plus any PE overlay, PDB build path, and a note when a hosted target has a native host worth analysing. On the main native binary, the Ghidra prefetch starts in the background. It writes functions.txt, strings.txt, imports.txt, decompiled.c and finally DONE to agent/prefetch/. The result goes into triage.md:
- keygenme.exe (48213 B): PE32 executable (console) Intel 80386, for MS Windows
- DIE: Compiler: MinGW
Background Ghidra analysis of `keygenme.exe` started -> /work/agent/prefetch/ ...
4. The agent works. It gets instructions (the rules from AGENT.md, the report format for its mode, the list of skills, the toolchain notes and this job's verifier) and a prompt with the task description and the triage. With the Claude backend, easyre runs claude -p --output-format stream-json --model claude-opus-5-5 โฆ and logs every event to .easyre/log.jsonl. A typical run:
| What the agent does | Tools | |
|---|---|---|
| a | Reads the general plan, then the skill for this kind of target | re-methodology โ native-binaries, ghidra-headless |
| b | Runs the program once | printf 'test\n' | wine /work/src/keygenme.exe โ Wrong |
| c | Finds the success and failure strings | strings -n 6, strings -el, agent/prefetch/strings.txt |
| d | Finds the function that checks the input | grep -n Correct agent/prefetch/decompiled.c |
| e | Reads the check, and the disassembly when the C looks wrong | decompiled.c, objdump -d -M intel, r2 |
| f | Pulls the constants out of the file instead of guessing | xxd at the offset, or the bytes from Ghidra |
| g | Records findings and dead ends | agent/notes.md: hypothesis | evidence | result |
| h | Solves it the cheapest way | an inversion in Python (agent/solve.py); z3 for tangled constraints; unicorn, angr, WINEDEBUG=+relay or winedbg when running beats reading |
| i | Confirms the candidate on the real program | printf 'SERIAL\n' | wine keygenme.exe โ Correct! |
| j | Submits the answer | easyre check 'SERIAL' |
| k | Writes the report | /work/report.md |
Steps eโh on this example. The decompiled check:
if (strlen(s) != 12) return 0; for (i = 0; i < 12; i++) if (((s[i] ^ 0x5A) + i) != expected[i]) return 0; return 1;
The agent reads expected from .rdata and inverts the check:
exp = bytes.fromhex("1b2a3c...") print(bytes(((e - i) & 0xff) ^ 0x5A for i, e in enumerate(exp)).decode())
Other targets change only the skill in step a and the tools: UPX โ upx -d; a simple or custom packer โ run to the OEP and dump-mem; .NET / Unity โ ilspycmd, de4dot, il2cppdumper; PyInstaller โ pyinstxtractor-ng + pycdc; .NET single-file โ dotnet-bundle then ilspycmd; binary + capture โ reverse the algo, then tshark/scapy to decode the pcap; APK / JAR โ jadx, apktool, cfr; Electron โ asar + webcrack; AutoIt โ autoit-ripper; WebAssembly โ wasm-decompile + Node; crypto โ capa, yara, pycryptodome; a custom VM โ handler map โ disassembler โ z3; a virtualizing protector (VMProtect/Themida) โ unpack โ trace handlers โ lift โ z3/angr.
5. Verification. easyre check runs the verifier (see โ
How verification works). Here it runs wine /work/src/keygenme.exe with the candidate on stdin (up to 120 s) and matches the output against Correct. Accepted: the answer is stored in .easyre/ANSWER and the agent is told to write the report. Rejected: the agent keeps working. Every attempt is logged in .easyre/attempts.log.
6. Guard rails.
- Stop hook: if the agent tries to stop without an accepted answer, a hook sends it back to work (up to 30 times). The only early exit is writing
HELP.mdwhen a human is really needed. - Resume loop: if the model process exits, easyre resumes the same session (up to 12 times). It waits 15 minutes on a rate limit, and starts a fresh session if the old one is gone; files and notes stay.
- Budget: 120 minutes by default (
--budget-min), plus an optional token limit (--budget-tokens). When it runs out, the report saysnot solved (time). - Ctrl+C stops the job with a report;
easyre resume keygenmecontinues it. A Ghidra prefetch that had not finished restarts on resume.
7. Result. easyre puts a header on top of the agent's report.md: status, backend and exact model, time, tokens, cost, and the answer with its check result. easyre open opens it.
jobs/keygenme-20261030-201500/
โโโ report.md the result
โโโ TASK.md
โโโ src/ the task, never modified
โโโ agent/ notes.md, solve.py, prefetch/, patched copies
โโโ .easyre/ job.json, triage.md, log.jsonl, attempts.log, ANSWER
In hint and tutor mode, steps 1โ5 are the same: the model solves the task first, but the answer goes to a private .easyre/answer. Then it writes graded hints (hints.md, checked by easyre done) or starts the tutor chat.
๐ค Backends
| Backend | Runs | Setup in .env |
|
|---|---|---|---|
| ๐ | claude |
Claude Code (headless, subagents, skills) | CLAUDE_CODE_OAUTH_TOKEN (subscription) or ANTHROPIC_API_KEY |
| ๐ข | codex |
OpenAI Codex CLI | easyre login codex or OPENAI_API_KEY |
| ๐ต | openai |
Built-in agent loop (bash, read_file, write_file, read_skill) |
OPENAI_BASE_URL, OPENAI_API_KEY, OPENAI_MODEL |
The openai backend covers every OpenAI-compatible API, including Google Gemini (profile gemini), OpenRouter, Ollama and LM Studio.
๐ฆ Local model (Ollama on the host):
EASYRE_BACKEND=openai
OPENAI_BASE_URL=http://host.docker.internal:11434/v1
OPENAI_MODEL=qwen3-coder
The model must support tool calling.
๐๏ธ Profiles
Save your models once in config/backends.toml and switch between them by name:
default = "claude-opus" [profiles.claude-opus] backend = "claude" model = "claude-opus-5-5" [profiles.ollama-qwen] backend = "openai" base_url = "http://host.docker.internal:11434/v1" model = "qwen3-coder" api_key = "ollama" [profiles.openrouter] backend = "openai" base_url = "https://openrouter.ai/api/v1" model = "deepseek/deepseek-chat" api_key_env = "OPENROUTER_API_KEY"
.\easyre.cmd profiles .\easyre.cmd run crackme --profile ollama-qwen .\easyre.cmd resume crackme --profile claude-opus
easyre profileslists the profiles;*marks the default.run ... --profile ollama-qwenuses a local model.resume ... --profile claude-opuscontinues the same job with a stronger model.
Which exact model? The Claude profiles use full model IDs (claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5), so every job runs on a known version. When a new model comes out, change the ID in config/backends.toml. To always follow the newest model instead, use an alias: model = "opus" (or sonnet, haiku). Either way, the exact model the backend reported is saved per job and shown in easyre jobs, easyre stats, the report header and, as last run, in easyre profiles.
api_key_env reads the key from .env, so secrets stay out of the profile file. If you omit --profile, the default profile is used, or EASYRE_PROFILE if it is set in .env. Passing --backend without --profile skips the profiles and uses the backend directly.
๐ฐ Stats
easyre stats sums time, tokens and cost per model over all your jobs. Claude reports its cost itself; this is the API-equivalent price, and a subscription costs nothing extra. For other APIs, add price_in and price_out (USD per 1M tokens) to the profile in config/backends.toml.
.\easyre.cmd tools # all jobs (terminal) .\easyre.cmd tools ThreatInvaders # one job (terminal + writes tools.svg) .\easyre.cmd tools --svg all.svg # all jobs, also write an SVG
easyre tools counts which tools the agent's shell commands invoked, read from each job's .easyre/log.jsonl, and draws a horizontal bar chart. A chained command (a | b; c) counts every tool in it; here-document bodies (pasted scripts) and plain shell glue (cd, ls, cat, echo, ...) are left out, so only real tools show. ghidra counts every command that ran Ghidra (analyzeHeadless, prefetch) or read its output (decompiled.c, the prefetch/ folder), once per command. For a single job it also writes a dark-theme tools.svg bar chart into the job folder (every solved job gets one automatically); --svg PATH sets the file, and easyre stats prints the same chart aggregated over all jobs. It shows what the agent leaned on (decompilers, tshark, scripting) and what it never touched. Works for the claude and codex backends; the openai backend logs only call counts, so it cannot be broken down.
๐ง Skills
Skills are Markdown playbooks in skills/<name>/SKILL.md. The agent sees the list and loads the one that matches the target. Each skill is short and practical: when to use it, the steps, the commands, the pitfalls.
| Skill | Covers | Status | |
|---|---|---|---|
| ๐งญ | re-methodology |
General approach: orient, find the check, extract, solve, verify | โ |
| โ๏ธ | native-binaries |
PE / ELF: C, C++, Go, Rust, Delphi, VB6 | โ |
| ๐ช | dotnet |
.NET and Unity (Mono / IL2CPP) | โ |
| ๐ | python-targets |
Packaged Python apps and bytecode | โ |
| โ | jvm-android |
JAR and APK | โ |
| ๐ | javascript-electron |
JS, Node, Electron | โ |
| ๐งพ | autoit |
Compiled AutoIt scripts | โ |
| ๐ธ๏ธ | wasm |
WebAssembly modules | โ |
| ๐ฐ | custom-vm |
Custom VMs and bytecode interpreters | โ |
| ๐ท | dynamic-wine |
Running and observing Windows programs under Wine | โ |
| ๐ฆ | unpacking |
Packed/protected binaries: unpack a copy, dump from memory at the OEP | โ |
| ๐งฑ | devirtualization |
Virtualized checks (VMProtect, Themida): trace handlers, lift the transform, solve | โ |
| ๐งฌ | code-and-data |
Binary + data file (pcap, blob, dump): recover the algorithm from code, decode the data | โ |
| ๐งช | emulation |
Emulate a program or one function (Qiling/unicorn) when Wine is detected or wrong | โ |
| ๐งฎ | solvers |
Constraint solving, symbolic execution, emulation | โ |
| ๐ | crypto-id |
Recognizing crypto and encoding primitives | โ |
| ๐๏ธ | formats-forensics |
Documents, archives, images, captures, custom formats | โ |
| ๐ | ghidra-headless |
Ghidra without the GUI (headless scripts) | โ |
All skills listed above are ready to use.
To add a skill, copy skills/_TEMPLATE.md to skills/<name>/SKILL.md. It appears to the agent automatically.
๐งฐ Toolchain
The toolchain is installed by toolchain/install.sh during docker compose build. It is split into modules under toolchain/modules/*.sh, chosen with the EASYRE_TOOLS setting in .env. toolchain/README.md has the full tool-by-tool matrix, and the agent reads it.
By default (EASYRE_TOOLS=all) every module below is installed. For a smaller image, list only the modules you need in .env and rebuild:
EASYRE_TOOLS=core,die,ghidra
| Module | What you get | Size |
|---|---|---|
wine (always) |
Wine 32/64 + Xvfb + MinGW runtime DLLs: run .exe, incl. GUI |
โ |
core |
binutils, gdb, radare2, upx, binwalk, yara, FLOSS, capa; py: capstone, keystone, unicorn, z3, lief, pefile, pwntools, pycryptodome | ~0.5 GB |
die |
diec (Detect It Easy) โ triage's compiler/packer detection |
~0.3 GB |
ghidra |
Headless Ghidra + matching JDK + the prefetch hook |
~1.5 GB |
dotnet |
.NET SDK, ilspycmd, Il2CppDumper, de4dot | ~1 GB |
jvm |
jadx, apktool, cfr | ~0.2 GB |
python |
pyinstxtractor-ng, pycdc/pycdas, decompyle3/uncompyle6 | ~0.1 GB |
js |
@electron/asar, webcrack, js-beautify | ~0.1 GB |
autoit |
autoit-ripper, AutoIt interpreter (autoit3, under Wine) |
~0.05 GB |
wasm |
wabt: wasm-objdump, wasm2wat, wasm-decompile, wasm-interp, wasm2c | ~0.02 GB |
net |
tshark + capinfos (packet captures; scapy is in core) |
~0.3 GB |
emu |
Qiling full-binary emulation (anti-Wine); unicorn is in core |
~0.4 GB |
angr |
angr 9.x symbolic execution (pinned below 10, see toolchain/README.md) |
~1 GB |
Each module is standalone and idempotent, so the agent (root, with network) can also pull a missing one in mid-job: sh /opt/easyre/toolchain/modules/ghidra.sh. That install is lost when the job ends, because every job runs in a fresh container, so bake in what you use.
Prefetch hook: the ghidra module installs /opt/easyre/tools/prefetch. When present, triage runs it in the background on the main native binary:
prefetch <binary> <output_dir>
It drives Ghidra headless to drop decompiled.c, functions.txt, strings.txt and imports.txt into agent/prefetch/, so the agent can start reading decompiled code immediately. Without the ghidra module the hook is simply absent and triage skips it.
Prefetch cache: a finished prefetch is stored in the easyre-state volume under /state/prefetch/<sha256>/. A new job on the same binary (a re-run, another mode, another model) copies it instead of running Ghidra again, and easyre doctor shows how many binaries are cached. Set EASYRE_PREFETCH_CACHE=0 in .env to turn it off; to clear it: docker compose run --rm easyre rm -rf /state/prefetch.
๐พ Disk space
Rough estimates, not measured:
| Part | Size |
|---|---|
| Ubuntu base + Python + basic utilities | ~0.4 GB |
| Node.js + Claude Code + Codex CLI | ~0.5 GB |
wine module (Wine 32/64 + Xvfb + prepared prefix + MinGW DLLs) |
~2 GB |
All other modules (core, die, ghidra, dotnet, jvm, python, js, autoit, wasm, angr, net, emu) |
~5.5 GB |
Image total (default EASYRE_TOOLS=all) |
~8โ9 GB |
Build cache during docker compose build |
+1โ3 GB (free with docker builder prune) |
To save space, set EASYRE_TOOLS in .env to a subset and rebuild (see the module table above). The biggest are ghidra ~1.5 GB, dotnet ~1 GB and angr ~1 GB; the rest are a few hundred MB or less. core,die alone gives a ~4โ5 GB image. Jobs in jobs/ also use space: each one holds a copy of the task plus the agent's files.
๐ Output
Every job gets its own folder, jobs/<name>-<timestamp>/:
jobs/crackme-20260930-162257/
โโโ ๐ report.md โ the result: answer, solution path, scripts
โโโ ๐ก hints.md โ graded hints (hint mode)
โโโ ๐งโ๐ซ tutor.md โ tutor conversation (tutor mode)
โโโ ๐ TASK.md โ the task description
โโโ ๐ฆ src/ โ the extracted task files (never modified)
โโโ ๐ ๏ธ agent/ โ the agent's workspace: notes.md, scripts, patched copies
โโโ โ๏ธ .easyre/ โ internal: settings, full log, attempts, instructions
Usually you only need report.md (or hints.md). agent/notes.md shows how the agent reasoned. .easyre/log.jsonl records every step, for debugging.
๐บ๏ธ Project layout
easyre CLI: jobs, triage, backends, verifier, reports, hints
setup.cmd / setup.sh one-time setup: build, choose model, log in, check
easyre.cmd / easyre.sh wrappers to run easyre from Windows / Linux / macOS
config/ agent instructions, mode rules, stop hook, backend settings
skills/ playbooks for the agent
toolchain/install.sh module dispatcher (EASYRE_TOOLS)
toolchain/modules/ one install script per tool group
toolchain/README.md tool matrix the agent reads
Dockerfile base image: agent CLIs + toolchain + skills
docker-compose.yml volumes: tasks/, jobs/, live-mounted easyre, config/, skills/
easyre, config/ and skills/ are mounted live, so edits take effect without rebuilding.
๐ค Contributing
Contributions are welcome: new skills, new toolchain modules, better triage, bug fixes. EasyRE-Agent is for crackmes, CTF reversing and puzzle binaries; please keep contributions within that educational scope.
Getting set up
- Fork and clone the repo, then run the setup once (
setup.cmd/sh setup.sh). easyre,config/andskills/are mounted into the container live, so edits to them take effect on the nexteasyre runwith no rebuild. Changes undertoolchain/or theDockerfileneeddocker compose build.- After a change, run
easyre doctor: it checks the profile, Wine, every toolchain group, the prefetch hook and the skills, and flags what is missing.
Add a skill
A skill is a Markdown playbook the agent loads when it matches the target.
- Copy
skills/_TEMPLATE.mdtoskills/<name>/SKILL.md. - Fill in the front matter (
namemust equal the folder name) and the sections: When to use, Steps, Tools and commands, Pitfalls, Verification. - Reference only tools a module actually installs, and name that module in When to use so the agent can pull it in if it is missing.
- The skill shows up to the agent automatically; no code change is needed, and
easyre doctorwill count it.
Add a toolchain module
- Add
toolchain/modules/<name>.sh. Keep it standalone and idempotent (safe to re-run mid-job), end it with a short self-test that prints[x] <tool>/[ ] <tool>, and resolve the latest upstream release instead of hardcoding a version that will rot. - Add a
COPY+RUN sh /opt/easyre/toolchain/install.sh <name>pair in theDockerfile(one layer per module; put heavy, stable ones earlier so a later failure does not rebuild them). - List it in
toolchain/README.md(the matrix the agent reads), the module table and disk estimates in this README, theEASYRE_TOOLSoptions line in.env.example, and the matching group indoctor()ineasyre. EASYRE_TOOLS=allpicks it up automatically; a subset in.envcan still select it by name.
Add a triage marker
Markers live in the MARKERS list in easyre as (signature, kind, label, mode). mode is any (anywhere in the file), start (a real file magic: leading bytes only) or weak (counts only when a strong marker already produced the same label). Prefer start for magics and weak for common-but-ambiguous strings, so large binaries do not pick up false tags.
Conventions
- Line endings are LF.
.gitattributesenforces it; shell scripts andeasyrebreak in the Linux container if they get CRLF. easyreis a single, stdlib-only Python file. Keep it dependency-free.- Before a PR:
sh -nyour shell scripts,python3 -c "import ast; ast.parse(open('easyre').read())", andeasyre doctor. If you can, solve one of your own crackmes end to end. tasks/,jobs/and.envare gitignored. Do not commit challenge binaries, task files or secrets.
Reporting issues
Open an issue with the target type, the command you ran, the triage output and the tail of .easyre/log.jsonl. Do not attach copyrighted or third-party challenge binaries.
โ ๏ธ Security
The container runs with SYS_PTRACE and seccomp=unconfined, has network access, and runs the targets. It is meant for crackmes and CTF challenges. Analyze real malware only in an isolated VM without network access.
๐ License
MIT, see LICENSE.
