|
| 1 | +<!-- ©AngelaMos | 2026 --> |
| 2 | +<!-- README.md --> |
| 3 | + |
| 4 | +``` |
| 5 | + ______ _____ ____ ____ _ |
| 6 | + / ___/ | / / _ \/ __ \/ __ `/ |
| 7 | + / / | |/ / __/ / / / /_/ / |
| 8 | +/_/ |___/\___/_/ /_/\__, / |
| 9 | + /____/ |
| 10 | +``` |
| 11 | + |
| 12 | +[](https://github.com/CarterPerez-dev/Cybersecurity-Projects/tree/main/PROJECTS/advanced/rveng) |
| 13 | +[](https://www.python.org) |
| 14 | +[](https://fastapi.tiangolo.com) |
| 15 | +[](https://react.dev) |
| 16 | +[](https://www.capstone-engine.org) |
| 17 | +[](#it-never-runs-a-binary) |
| 18 | +[](https://www.gnu.org/licenses/agpl-3.0) |
| 19 | + |
| 20 | +> An interactive reverse-engineering learning platform. It hands you a real compiled binary, asks a concrete question about it, gives you an in-browser hex viewer, disassembler, section map, and string scanner to answer it, and grades your answer. Only when you are right does it reveal the original C source, so you connect the machine code you just read back to the code that produced it. One framework-free Python analysis engine wears three faces: a web app, a read-only HTTP API, and an embeddable library. |
| 21 | +
|
| 22 | +## Why a reverse-engineering platform |
| 23 | + |
| 24 | +A pile of worksheets and pre-compiled binaries can walk you through reverse engineering, but it cannot check your work and it cannot become anything more. rveng keeps the solve-then-reveal loop and makes it a real system. The engine that parses ELF, drives the disassembler, resolves imports, and grades answers is the core. The web app turns the worksheet into an interactive lab: an in-browser hex viewer, live disassembly, a section map, and a gradeable challenge runner. The engine and its lesson content are decoupled from the web framework, so they embed into a larger application as a standalone reverse-engineering feature. One core, three consumers, curated content. |
| 25 | + |
| 26 | +## It never runs a binary |
| 27 | + |
| 28 | +The single load-bearing decision, and the reason a web app that eats binaries is safe: the backend never executes any binary. Every operation is reading and parsing bytes. |
| 29 | + |
| 30 | +- Hex dump, ELF header parse, section walk, symbol read, and string scan are all pure byte reads. |
| 31 | +- Disassembly is decoding, not running. capstone reads instruction bytes and returns their text form; it never transfers control to the decoded code. |
| 32 | +- Patch challenges are graded by a static byte diff against a known-good patched target. The patched binary is never executed. |
| 33 | + |
| 34 | +Challenge binaries are curated and pre-compiled and shipped as static assets; nobody uploads an executable to run. So there is no arbitrary-code-execution surface, no sandbox to escape, and no resource-exhaustion path through a hostile binary, because nothing is ever run. This is a hard constraint, not a preference. Any feature that would require executing a binary is out of scope until it is redesigned against this posture, most likely by moving execution to an isolated, disposable sandbox that is explicitly not the analysis backend. |
| 35 | + |
| 36 | +## Features |
| 37 | + |
| 38 | +**The engine** |
| 39 | +- A hand-rolled ELF64 parser: header, section table, and symbol table read straight from raw bytes |
| 40 | +- x86-64 disassembly via capstone in Intel syntax, annotated with comparisons, conditional branches, call targets, and RIP-relative data references |
| 41 | +- Import resolution through the PLT, walking `.plt`, `.rela.plt`, `.dynsym`, and `.dynstr` the way the loader would, so a bare `call 0x401050` becomes `call atoi` |
| 42 | +- Cross-references (who calls this, what data it touches) and a basic-block control-flow graph for a single function |
| 43 | +- Function discovery in stripped binaries by scanning executable sections for the standard prologue, so a symbol-stripped binary is still navigable |
| 44 | + |
| 45 | +**The platform** |
| 46 | +- Six curated challenges over one sample binary, spanning the five core reverse-engineering skills plus a stripped variant |
| 47 | +- Solve-then-reveal grading in three machine-checkable categories: found-value, identified-symbol, and patched-bytes |
| 48 | +- Progress persisted in SQLite behind a swappable interface, so it drops into a host application's own store without touching the engine |
| 49 | +- A React web app and a read-only FastAPI, served together for one-command self-hosting |
| 50 | + |
| 51 | +## Quick Start |
| 52 | + |
| 53 | +rveng self-hosts on localhost with Docker. No account, no secret, no external service, nothing to configure. |
| 54 | + |
| 55 | +```bash |
| 56 | +curl -fsSL https://angelamos.com/rveng/install.sh | bash |
| 57 | +# then open http://localhost:8790 |
| 58 | +``` |
| 59 | + |
| 60 | +One command takes a fresh machine to the app built and running: it installs Docker if it is missing, builds the engine image and the React app, brings the stack up, and waits until it answers. The first thing you do is open the browser. |
| 61 | + |
| 62 | +Already have the repo cloned? Run it straight from the project directory: |
| 63 | + |
| 64 | +```bash |
| 65 | +just up # build and serve on http://localhost:8790 |
| 66 | +just dev-up # hot-reload dev stack on http://localhost:8791 |
| 67 | +just test # engine tests, no server (uv run pytest -q) |
| 68 | +just typecheck # frontend type check, no server |
| 69 | +just down # stop |
| 70 | +``` |
| 71 | + |
| 72 | +> [!TIP] |
| 73 | +> This project uses [`just`](https://github.com/casey/just) as a command runner. Type `just` to see every recipe grouped by area: `dev` (dockerized Vite hot-reload), `prod` (the self-host stack), `verify` (tests and type check), and `cleanup`. |
| 74 | +> |
| 75 | +> Install: `curl -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin` |
| 76 | +
|
| 77 | +## Architecture |
| 78 | + |
| 79 | +One analysis engine with three faces. The engine is framework-free Python that knows nothing about HTTP or React. A thin FastAPI layer adapts it to the web. A React app consumes that API. Progress lives behind a small interface so the whole thing embeds into a larger application without dragging a web framework along. |
| 80 | + |
| 81 | +``` |
| 82 | + +--------------------------+ |
| 83 | + | rveng/engine/ (pure) | |
| 84 | + | elf disasm plt xref cfg | |
| 85 | + | hex strings patch discover| |
| 86 | + | challenge (grading) | |
| 87 | + +------------+--------------+ |
| 88 | + | |
| 89 | + +-----------------+-----------------+ |
| 90 | + | | |
| 91 | + +-----+------+ +------+------+ |
| 92 | + | HTTP API | | library | |
| 93 | + | FastAPI | | (import it)| |
| 94 | + +-----+------+ +-------------+ |
| 95 | + | |
| 96 | + +-----+------+ |
| 97 | + | React app | |
| 98 | + +------------+ |
| 99 | +``` |
| 100 | + |
| 101 | +In production, nginx serves the built frontend and proxies `/api` to the engine container, which stays a pure API and never learns to serve a SPA. Development mirrors that with nginx fronting the Vite dev server for hot reload. The two stacks use structural-literal project names (`rveng` and `rveng-dev`), so they are namespace-isolated by construction and never collide. |
| 102 | + |
| 103 | +``` |
| 104 | +PROD (compose.yml, "rveng") DEV (dev.compose.yml, "rveng-dev") |
| 105 | +
|
| 106 | + browser :8790 browser :8791 |
| 107 | + | | |
| 108 | + [ nginx ] serves dist [ nginx ] --> [ vite HMR ] |
| 109 | + | /api | /api |
| 110 | + [ api ] uvicorn [ api ] uvicorn --reload |
| 111 | + | | |
| 112 | + rveng_data (sqlite progress) rveng_data_dev |
| 113 | +``` |
| 114 | + |
| 115 | +## Project Structure |
| 116 | + |
| 117 | +``` |
| 118 | +rveng/ |
| 119 | +├── compose.yml # prod self-host: nginx + api (name: rveng) |
| 120 | +├── dev.compose.yml # dev: nginx + vite HMR + api --reload |
| 121 | +├── justfile # dev / prod / verify / cleanup recipes |
| 122 | +├── install.sh # one-shot curl|bash: installs Docker, builds, runs |
| 123 | +├── uninstall.sh # tears the stacks down and removes the cache |
| 124 | +├── infra/ |
| 125 | +│ ├── docker/ # api.dockerfile, vite.dev, vite.prod (multistage -> nginx) |
| 126 | +│ └── nginx/ # dev.nginx (fronts Vite HMR), prod.nginx (serves dist) |
| 127 | +├── src/rveng/ |
| 128 | +│ ├── engine/ # the pure analysis core (no HTTP, no framework) |
| 129 | +│ │ ├── elf.py # hand-rolled ELF64 header / sections / symbols |
| 130 | +│ │ ├── disasm.py # capstone x86-64 decode + annotation |
| 131 | +│ │ ├── plt.py # PLT / GOT import resolution |
| 132 | +│ │ ├── xref.py # cross-references from decoded instructions |
| 133 | +│ │ ├── cfg.py # basic-block control-flow graph |
| 134 | +│ │ ├── discover.py # stripped-binary function discovery |
| 135 | +│ │ ├── hex.py strings.py patch.py # dump, string scan, byte diff |
| 136 | +│ │ └── challenge.py # the challenge model and solve-then-reveal grader |
| 137 | +│ └── api/ # thin FastAPI adapter over the engine |
| 138 | +│ ├── app.py # create_app() and every read-only route |
| 139 | +│ ├── store.py # challenge loader + ProgressStore (in-memory / sqlite) |
| 140 | +│ ├── schemas.py limits.py middleware.py server.py |
| 141 | +├── challenges/ # the six curated challenges (target + source + answer) |
| 142 | +├── frontend/ # the React face (self-contained, extractable) |
| 143 | +└── learn/ # the teaching track |
| 144 | +``` |
| 145 | + |
| 146 | +## Learn |
| 147 | + |
| 148 | +This project ships a full teaching track. Read it in order, or jump to what you need. |
| 149 | + |
| 150 | +| Doc | What it covers | |
| 151 | +|-----|----------------| |
| 152 | +| [`learn/00-OVERVIEW.md`](learn/00-OVERVIEW.md) | What rveng is, the no-execution posture, and a quick tour | |
| 153 | +| [`learn/01-CONCEPTS.md`](learn/01-CONCEPTS.md) | Reverse-engineering theory: static analysis, ELF, symbols, the PLT, patching, grounded in real analysis | |
| 154 | +| [`learn/02-ARCHITECTURE.md`](learn/02-ARCHITECTURE.md) | The one-engine-three-faces design and how a request flows, with diagrams | |
| 155 | +| [`learn/03-IMPLEMENTATION.md`](learn/03-IMPLEMENTATION.md) | A code walkthrough of every engine module against the sample binary | |
| 156 | +| [`learn/04-CHALLENGES.md`](learn/04-CHALLENGES.md) | The six challenges, what each teaches, and how to add your own | |
| 157 | + |
| 158 | +## License |
| 159 | + |
| 160 | +[AGPL 3.0](LICENSE). |
0 commit comments