Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Reverser

EVM bytecode decompiler and analyzer. Combines Heimdall (pinned to 0.9.3) as the primary readable output with a native analysis pipeline — disassembly, conservative CFG, selector and pattern recovery, a TAC-like IR, and inferred Solidity-like codegen — merged into a single confidence-scored result.

Architecture

Crate Responsibility
analysis-model Shared serde data model + JSON schema
bytecode Hex validation, normalization, metadata stripping, hashing
evm-disasm Opcode table + disassembler
evm-cfg Basic blocks, conservative CFG, selector candidates
evm-patterns Storage / event / error / call / calldata detectors
evm-ir Bounded stack simulation → TAC-like IR + coverage
solidity-codegen Inferred Solidity-like output
result-merger Merges Heimdall + native facts; conflicts + confidence
validator Fixture runner + metrics for the validation dashboard
apps/api axum HTTP API, SQLite persistence, RPC lookup, exports
apps/web React UI (analysis tabs + validation dashboard)

Local setup

Prerequisites

  • Rust (stable) and Cargo
  • Node 24 + pnpm (via corepack enable)
  • Heimdall 0.9.3 on PATH (optional — the native pipeline runs without it):
    curl -L http://get.heimdall.rs | bash
    bifrost -v 0.9.3

Run the API

cargo run -p api
# listens on :8080 (override with PORT)

Configuration via environment variables:

Var Default Meaning
PORT 8080 HTTP port
HEIMDALL_TIMEOUT_SECS 30 Heimdall subprocess timeout
MAX_INPUT_BYTES 1048576 Max bytecode size (raw bytes)
DB_PATH :memory: SQLite path (use a file to persist)
ALLOW_CUSTOM_RPC_URL false Permit client-supplied (SSRF-validated) RPC URLs
LLM_REFINE false Enable the llm_refine_native stage (LLM-refined native output)
LLM_TIMEOUT_SECS 120 Timeout for the LLM refinement subprocess
LLM_COMMAND claude LLM CLI command (non-interactive -p --output-format json)
LLM_MODEL (CLI default) Optional model id passed to the LLM CLI via --model

LLM refinement (optional)

With LLM_REFINE=true, an extra pipeline stage shells out to an LLM CLI (Claude Code by default) to turn the conservative native skeleton into more readable inferred Solidity — folding library patterns (SafeMath, EnumerableSet, AccessControl) back into clean expressions. It uses whatever auth the CLI already has (a logged-in session or its own key); this service never handles API keys. The stage is non-fatal: any failure keeps the deterministic native output. Refined output is marked generated_from: [..., "llm"] with an llm_refined warning — it is plausible but not verified against the bytecode. Best for local/personal use; a hosted multi-user service should use the provider API directly rather than driving a subscription-authed CLI.

Run the web app

pnpm --dir apps/web install
pnpm --dir apps/web dev      # Vite dev server, proxied to the API

Validate analyzer quality

cargo run -p validator -- fixtures      # prints JSON metrics, exits non-zero on regression

API

POST /api/v1/analyses                      # { input: { kind: "bytecode"|"address", ... } }
POST /api/v1/analyses/upload               # multipart file upload (hex)
GET  /api/v1/analyses/:id                  # job status + result
GET  /api/v1/analyses/:id/export/solidity  # readable output (.sol)
GET  /api/v1/analyses/:id/export/json      # full analysis (.json)
GET  /api/v1/analyses/:id/export/cfg-dot   # CFG (Graphviz .dot)
GET  /api/v1/fixtures                       # validation dashboard metrics

Address lookup resolves chain from an explicit allowlist (ethereum, sepolia, base, optimism, arbitrum). Custom RPC URLs are rejected unless ALLOW_CUSTOM_RPC_URL=true, and even then loopback/private/link-local hosts are refused (SSRF protection). Results are cached by bytecode keccak256 hash.

Docker

docker build -t reverser .
docker run -p 8080:8080 -v reverser-data:/app/data reverser

The image pins Heimdall 0.9.3 to match crates/heimdall-adapter.

Fixture progression

Fixtures use a single growing format, seeded in Phase 3 (runtime hex + expected.json) and promoted in Phase 8 to the full layout:

fixtures/
  contracts/<name>/{Contract.sol, fixture.json}   # source + compiler metadata
  bytecode/<name>.runtime.hex                       # analyzed bytecode
  expected/<name>.expected.json                     # partial ground truth

Expected facts are partial ground truth (selectors, event topics, storage summaries), not full reconstructed source.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages