# Reverse Engineering — full corpus
# LLM Wiki
An open-source template for building LLM-powered knowledge bases, following [Andrej Karpathy's "LLM Wiki" pattern](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f).
You provide raw sources. The LLM reads them, writes structured wiki pages, cross-links everything, and maintains it over time. You never edit the wiki directly — you curate sources and ask questions.
## How It Works
The system has three layers:
```
raw/ Sources you collect (articles, transcripts, notes, PDFs)
wiki/ LLM-written & maintained pages (summaries, concepts, entities, syntheses)
CLAUDE.md Schema that tells the LLM how to structure everything
```
Three operations drive the workflow:
| Operation | Trigger | What happens |
|-----------|---------|--------------|
| **Ingest** | "ingest raw/my-source.txt" | LLM reads the source, creates a summary page, creates/updates concept and entity pages, adds cross-links, updates the index and log |
| **Query** | Ask any question | LLM searches the wiki, synthesizes an answer with citations, optionally creates a synthesis page for novel insights |
| **Lint** | "lint" or "health check" | LLM audits all pages for orphans, contradictions, missing links, incomplete sections, and low-confidence claims — fixes what it can, reports the rest |
## Quick Start
1. **Clone this repo**
```bash
git clone https://github.com/YOUR_USERNAME/llm-wiki.git my-knowledge-base
cd my-knowledge-base
```
2. **Customize CLAUDE.md** for your domain
- Update the Purpose section with your topic
- Replace the placeholder tagging taxonomy with your own categories
- Adjust confidence level descriptions if needed
- Everything else (workflows, page formats, linking rules) works as-is
3. **Drop sources into `raw/`**
- Text files, transcripts, articles, notes — any plain text
- These are immutable once added; the LLM never modifies them
4. **Tell the LLM to ingest**
```
ingest raw/my-first-source.txt
```
The LLM will create summary pages, concept pages, entity pages, cross-links, and update the index.
5. **Ask questions**
```
What are the key differences between X and Y?
```
The LLM answers from the wiki, citing specific pages.
6. **Run health checks**
```
lint
```
The LLM audits the wiki and fixes issues.
## Directory Structure
```
.
├── CLAUDE.md # Schema — the LLM's instructions
├── raw/ # Your source documents (immutable)
└── wiki/
├── index.md # Master catalog of all pages
├── log.md # Append-only activity log
├── dashboard.md # Dataview dashboard (Obsidian)
├── analytics.md # Charts View analytics (Obsidian)
├── flashcards.md # Spaced repetition cards
├── summaries/ # One page per source document
├── concepts/ # Concept and framework pages
├── entities/ # People, tools, organizations, etc.
├── syntheses/ # Cross-cutting analyses and comparisons
├── journal/ # Research/session journal entries
│ └── template.md # Journal entry template
└── presentations/ # Marp slide decks
```
## Enhancements
This template includes several extras beyond the core wiki pattern:
### Dataview Dashboard (`wiki/dashboard.md`)
Live queries that surface low-confidence pages, recent updates, concepts by tag, and pages with the most sources. Requires the [Dataview](https://github.com/blacksmithgu/obsidian-dataview) Obsidian plugin.
### Charts View Analytics (`wiki/analytics.md`)
Visual analytics with pie charts, bar charts, and word clouds. Requires the [Charts View](https://github.com/caronchen/obsidian-chartsview-plugin) Obsidian plugin.
### Mermaid Diagrams
Use Mermaid code blocks in any wiki page to create flowcharts, sequence diagrams, or concept maps. Native support in Obsidian and GitHub.
### Marp Slides (`wiki/presentations/`)
Create slide decks from markdown using [Marp](https://marp.app/). Drop presentation files in this directory.
### Research Journal (`wiki/journal/`)
Track your research sessions, experiments, or applied work with the included template. The LLM can reference journal entries when answering queries.
### Spaced Repetition (`wiki/flashcards.md`)
Flashcards in the format used by the [Spaced Repetition](https://github.com/st3v3nmw/obsidian-spaced-repetition) Obsidian plugin. Ask the LLM to generate flashcards from any wiki page.
### MCP Server
This repo works with Claude Code's MCP server capabilities. Point an MCP-compatible client at this repo and the LLM can read/write the wiki programmatically.
## Customizing for Your Domain
The schema in `CLAUDE.md` is domain-agnostic. To adapt it:
1. **Purpose** — Describe your knowledge domain in one paragraph
2. **Tagging taxonomy** — Replace placeholder categories with your own (e.g., for a cooking KB: `cuisine`, `technique`, `ingredient`, `equipment`)
3. **Confidence levels** — Adjust the descriptions to match your domain's evidence standards
4. **Entity types** — Update the entity page description to match what entities mean in your domain (people, tools, companies, etc.)
5. **Journal template** — Customize `wiki/journal/template.md` for your workflow
Everything else — page format, linking conventions, workflows, rules — is universal and works across domains.
## Example Domains
This template works for any knowledge-intensive topic:
- **Research notes** — papers, experiments, methodologies
- **Book analysis** — themes, characters, author techniques
- **Competitive analysis** — companies, products, market trends
- **Course notes** — lectures, readings, key concepts
- **Personal development** — frameworks, habits, book summaries
- **Technical documentation** — APIs, architectures, design patterns
- **Hobby deep-dives** — any subject you want to master
## License
MIT
---
title: "Reverse Engineering KB — Master Index"
type: index
updated: 2026-10-16
rea_version: "6.5.0"
ghidra_version: "12.1.4"
ida_version: "9.4sp1"
rizin_version: "0.9.1"
cutter_version: "2.5.0"
frida_version: "17.24.0"
jadx_version: "1.5.6"
apktool_version: "3.0.3"
capa_version: "10.0.0"
yara_x_version: "1.21.0"
confidence: high
sources: ["raw/github_release-rea-agents-v6-5-0.md", "raw/github_release-ghidra-12-1-4.md", "raw/github_release-rizin-v0-9-1.md", "raw/github_release-cutter-v2-5-0.md", "raw/github_release-frida-17-24-0.md", "raw/github_release-1-5-6.md", "raw/github_release-v3-0-3.md", "raw/github_release-v10-0-0.md", "raw/github_release-v1-21-0.md", "raw/web_community-9-4sp1-md.md"]
---
# Reverse Engineering KB — Master Index
**Domain:** software reverse engineering for legitimate analysis: triaging unknown binaries and suspicious samples, static analysis and decompilation, debugging and instrumentation, platform-specific targets, network captures, and driving RE tools from AI agents over MCP. Anchored on REA (`rea-agents` 6.5.0) and each tool's own documentation.
**Scope:** interoperability, security and vulnerability research, malware analysis and defence, CTF/education, and software you are authorized to examine. No DRM/licence cracking, anti-cheat bypass, piracy, malware development or evasion. See [[concepts/legal-and-ethics]].
**Pages:** 24 (7 concepts · 6 entities · 11 syntheses)
Start at [[syntheses/overview]] if you are new; it maps "I have X" to the right page.
## Concepts
- [[concepts/legal-and-ethics]] — authorized-use boundaries, 17 USC 1201(f)/(j), UK CDPA s50B, what this wiki will not cover
- [[concepts/executable-formats]] — ELF, PE/COFF, Mach-O (fat binaries, app bundles) and DEX at a working level
- [[concepts/decompilation-concepts]] — how decompilers recover code, why output goes wrong, and how annotations fix it
- [[concepts/dotnet-and-nativeaot]] — IL decompilation with ILSpy/dnSpyEx, and why NativeAOT needs Ghidra plus ghidra-nativeaot
- [[concepts/go-rust-and-dart-binaries]] — Go pclntab and ABI (GoReSym), Rust v0 mangling, Flutter/Dart AOT (blutter)
- [[concepts/webassembly]] — module structure, WABT round-trips, decompiling wasm
- [[concepts/evm-bytecode]] — selectors and ABI recovery with EVMole, opcodes, Solidity metadata, JEB's EVM decompiler
## Entities
- [[entities/ghidra]] — Ghidra 12.1.4: projects, import, auto-analysis, decompiler, data types, version tracking
- [[entities/ida-pro]] — IDA 9.4sp1: disassembler, Hex-Rays decompiler, FLIRT, Lumina, IDAPython, idalib, official MCP
- [[entities/rizin-and-cutter]] — Rizin 0.9.1 CLI model and rz-* tools, Cutter 2.5.0 GUI, rz-pipe scripting
- [[entities/gdb-and-pwndbg]] — GDB for understanding behaviour, plus pwndbg's inspection views
- [[entities/frida]] — Frida 17 modes, CLI tools, Gadget, Android and iOS setup for apps you may analyze
- [[entities/rea]] — REA (`rea-agents` 6.5.0): setup, providers it drives, evidence model, platform limits
## Syntheses
- [[syntheses/overview]] — what reverse engineering covers, the intent map, and the tool landscape
- [[syntheses/triage-an-unknown-binary]] — the first 15 minutes: format, architecture, packing, strings, symbols, next tool
- [[syntheses/malware-triage]] — safe handling, then DIE → FLOSS → capa → YARA/YARA-X, then hand-off
- [[syntheses/choosing-a-disassembler]] — Ghidra vs IDA vs JEB vs Hopper vs Rizin/Cutter vs objdump
- [[syntheses/dynamic-analysis-workflow]] — debugger vs instrumentation vs tracing vs record/replay, sandboxing, evidence
- [[syntheses/android-apk-analysis]] — APK anatomy, JADX, apktool decode, ADB acquisition, Frida and traffic inspection on a test device
- [[syntheses/javascript-and-electron-apps]] — ASAR extraction, source maps, wakaru, Electron fuses, CDP observation
- [[syntheses/firmware-analysis]] — binwalk v3 and unblob extraction, entropy, loading raw images in Ghidra
- [[syntheses/network-captures-and-protocols]] — Wireshark/tshark filters, TLS key logging, mitmproxy modes, HTTP/3
- [[syntheses/re-with-ai-agents-mcp]] — REA vs IDA MCP vs JEB MCP vs GhidraMCP and others: evidence, limits, safety
- [[syntheses/troubleshooting]] — symptom-first fixes for Ghidra, IDA, Frida, JADX, apktool, capa, YARA-X and REA
## XL Edition (Pro)
The gated XL layer adds 8 curated reference pages: Ghidra scripting, headless analysis, debugger and BSim; Ghidra processor and p-code notes; IDA scripting and idalib; Rizin and GDB/pwndbg command references; the Frida JavaScript API; the Android toolchain (jadx, apktool, adb, dex); and the full REA MCP/CLI/provider reference.
---
title: "Decompilation Concepts"
type: concept
tags: [decompiler, p-code, microcode, ghidra, ida-pro, hex-rays, type-recovery, ssa, static-analysis]
created: 2026-10-16
updated: 2026-10-16
confidence: high
sources: ["raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi-3.md", "raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi-2.md", "raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi.md", "raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi-4.md", "raw/web_community-introduction-to-decompilation-vs-disassembly-md.md", "raw/web_community-tricks-md.md", "raw/web_community-failures-md.md", "raw/web_community-get-started-md.md", "raw/web_community-ctree-view-md.md", "raw/web_community-microcode-view-md.md", "raw/github_doc-ghidradocs-ghidraclass-beginner-introduction-to-ghidra-stude.md"]
---
# Decompilation Concepts
## Definition
A decompiler turns the machine code of one function into C-like pseudocode. A disassembler maps each instruction to a mnemonic; a decompiler goes further, running data-flow analysis across the whole function so it can rebuild expressions, variables, parameters and structured control flow (`if`, `switch`, loops). Hex-Rays puts the practical gain at output five to ten times shorter than the equivalent disassembly. The pseudocode is meant to be read, not recompiled: the Hex-Rays documentation says outright that it is deliberately not 100% compilable.
Two decompilers anchor this wiki: Ghidra's Decompiler (see [[entities/ghidra]]) and the Hex-Rays decompiler in IDA (see [[entities/ida-pro]]). They differ in detail but share one model. Lift the instructions into an intermediate representation (IR), analyse that IR, then print it as C. Most bad output comes from wrong *inputs* to that pipeline, such as prototypes, function boundaries, memory attributes and types, so the fix is usually an annotation rather than a different tool.
## How It Works
### 1. Lifting to an IR
- **Ghidra: p-code.** Every instruction is translated into p-code, Ghidra's IR, and all later analysis runs on p-code. The translation comes from the processor's SLEIGH specification (`*.slaspec`/`*.sinc` source, compiled to `*.sla`), chosen by the program's Language ID. A Language ID has five colon-separated fields (processor, endianness, address size, variant, compiler), for example `x86:LE:32:default:windows` or `MIPS:BE:32:micro:default`. All state (RAM, registers, temporaries, constants) lives in named *address spaces* such as `ram`, `register`, `unique`, `stack` and `constant`. A value is a *varnode*, the triple `(address space, offset, size)`.
- **IDA: microcode.** Hex-Rays lifts to microcode, which moves through a series of *maturity levels* before it becomes a C-level tree, the **ctree**. You can watch this happen. The Microcode view (`Ctrl+Shift+F8`) steps through the maturity levels with `>` and `<`, and the newer CTree view (`Ctrl+F6`, which Hex-Rays says is still settling and may change) shows the final syntax tree node by node. The ctree only exists once local variables have been allocated (maturity `MMAT_LVARS` or higher).
Processor behaviour that the model doesn't capture shows up as an opaque operation. In Ghidra that is a `CALLOTHER`, which the decompiler treats as a black box and prints as a named function call. Ghidra lets a *callother-fixup* supply the missing semantics.
### 2. Control flow and the function body
Decompilation starts at the function's **entry point** and traces flow through jumps and fall-throughs until it reaches return-like instructions. It does not follow calls; a call is assumed to fall through. Ghidra re-derives the body from the entry point every time and ignores the stored "formal" body. That is why a body created by hand from a selection can make the Decompiler window jump to an unexpected function.
### 3. Data flow (SSA), variables and merging
Ghidra converts p-code into Static Single Assignment form, where every write creates a new varnode with its own scope. A *HighVariable* is a set of non-overlapping varnodes that together form one source-level variable. Deciding which varnodes to group is called *merging*. Merging is forced when storage meets across control-flow joins and speculative otherwise. Speculative merges only happen between varnodes of the same data type. Data flow is also what lets a decompiler collapse a 20–30 instruction signed-division idiom back into `x = y / 2;` and find a call's arguments even when they were set up far from the call.
### 4. Parameters and calling conventions
A **prototype model** (Ghidra's name for a calling convention such as `__stdcall` or `__thiscall`) says which registers and stack slots can hold parameters and return values. It also lists *unaffected* (callee-saved) locations and *killed-by-call* locations. Without explicit annotations, the decompiler uses the model to guess the prototype. Hidden *auto-parameters* such as `this` and `__return_storage_ptr__` are shown explicitly because analysts need to see them.
### 5. Type propagation
Types flow from annotated sources (parameters, globals, library signatures) through the data flow to unlabeled variables and constants. Ghidra's decompiler does **not** invent structures, unions, typedefs or bitfields on its own. It only propagates ones you or an importer applied, though *Auto Create Structure* builds a structure from how a pointer is used. In IDA, the Hex-Rays getting-started primer shows the same thing. A function full of casts cleans up as soon as you declare a struct in Local Types (`Shift-F1`) and set the argument's type with `Y`.
### 6. Structuring and printing
Finally, the control-flow graph is reduced to `if`/`while`/`do`/`switch`. Edges that can't be structured become `goto`. IDA's Microcode view has a *Structural analysis* action that draws each reduction step.
## Key Parameters
These are the inputs that change pseudocode most. Each is an annotation you can set.
| Lever | What it tells the decompiler | Ghidra | IDA |
|---|---|---|---|
| Function prototype | Parameter and return types and storage | Edit signature. Any Signature Source other than `DEFAULT` (`IMPORTED`, `USER_DEFINED`, `ANALYSIS`, `AI`) forces the whole prototype | `Y` on the function (Edit, Functions, Set function type) |
| No-return | Calls to this function never come back | Function "no return" property | Mark the callee non-returning |
| In-line / call-fixup | Fold a helper's effect into callers | In-line property, or a named call-fixup from the compiler spec | n/a in these sources |
| Read-only memory | Values are constants and can be folded | Mutability `Constant`, or the read-only block flag with "Respect read-only flags" on | Segment without write permission, segment type CODE, or names such as `.rodata`/`.got` |
| Volatile memory | Every access matters | Mutability `Volatile` | Segment named `IO`, `IOPORTS`, `PORTS` or `VOLATILE` (case-insensitive) |
| Register values | A register holds a known constant | Set Register Values. Only applies if the range covers the entry point | n/a in these sources |
| Flow overrides | Reinterpret a branch as a call, return or jump | `BRANCH`, `CALL`, `CALL_RETURN`, `RETURN` overrides | n/a in these sources |
| Indirect-call target | Prototype at a computed call site | Function-pointer types | Set operand type, structure offset (`T`), or Change the callee address (`Alt-F11`, x86 only) |
The Ghidra Decompiler options that most often matter are listed below. Several of them silently change what you see.
| Option | Effect |
|---|---|
| Decompiler Timeout (seconds) | Per-function limit. Not enforced in the Decompiler window; applies to the switch analyzer, `analyzeHeadless`, scripts and other plug-ins |
| Max Instructions per Function | Stops runaway tracing into zero-filled or data regions |
| Max Entries per Jumptable | Sanity cap on recovered switch tables |
| Eliminate unreachable code | Turn it off to see branches pruned by constant propagation (shown as `if (false)`) |
| Respect read-only flags | Pulls read-only initial values into data flow as constants |
| Prototype Evaluation | Calling convention assumed for functions marked `unknown` |
## When To Use
- **Read pseudocode first** for logic: what a function returns, which strings and APIs it touches, how arguments flow. Decompilers answer "where is this set?" and "is this used?" at once.
- **Drop to disassembly or IR** when the pseudocode contradicts itself, a value seems to vanish, or you need exact instruction semantics (anti-analysis tricks, unusual calling conventions, timing). IDA's Microcode view and Ghidra's p-code are the ground truth that the C is printed from.
- **Iterate.** Analysis is a loop: fix a prototype or type, re-decompile, repeat. Both tools re-decompile automatically when annotations change.
## Risks & Pitfalls
**Why pseudocode is wrong, and the usual fix:**
| Symptom | Likely cause | Fix |
|---|---|---|
| Missing or garbage arguments, "call analysis failed" (IDA) | Wrong callee prototype, wrong stack pointer, or an unknown variadic count | Set callee types (`Y`); check the stack-pointer column (Options, General, Stack pointer) and correct it with `Alt-K`. One wrong `__stdcall` arity can break a neighbouring call |
| Code after a call disappears, or the function runs into the next one | Callee not marked no-return | Mark it no-return (IDA suggests "noret"); in Ghidra run `FixupNoReturnFunction.java` |
| `switch` shown as an indirect jump, "switch analysis failed" | Jump-table idiom not recognised | IDA: Edit, Other, Specify switch idiom. Ghidra: Decompiler Switch Analysis one-shot, Max Entries per Jumptable |
| Polling loop decompiles to `while (1);` | Device memory assumed stable | Mark the region volatile |
| `*off_XXXX` where a symbol was expected | Pointer table not known to be constant | Mark it read-only/constant |
| "positive sp value has been found" | Stack pointer tracking went wrong | Fix stack-pointer values (`Alt-K`) |
| Variables in red (IDA) | Local variable allocation failed, so variables overlap | Check prototypes and stack variables; reset with Edit, Other, Reset decompiler information |
| `CONCAT31`, `SUB41`, `ZEXT14`, `CARRY4` (Ghidra) | Partial-register or carry operations with no C equivalent | Often a sign that a variable has the wrong size; retype it. The digits are operand sizes in bytes (`SUB41`: 4-byte input, 1-byte output; `CONCAT31`: 3- and 1-byte inputs; `CARRY4`: 4-byte inputs) |
| `x._8_2_` style field tokens | An access spans several fields or elements | Correct the structure or array layout |
| Worse output after you retype something | Annotations are *forcing*: a wrong type overrides analysis | Use `undefined1/2/4/8` when only the size is known, so propagation can fill in the type |
More cautions:
- **Annotations override analysis.** In Ghidra a typed variable annotation is fixed and won't be changed by propagation. A forcing Signature Source fixes the exact number of parameters, and with no parameters it forces `void`. Retyping is powerful and also the most common way to make output worse.
- **Unreachable-block pruning hides comments.** Comments attached to blocks the decompiler drops are not shown.
- **Context registers** (for example the ARM/Thumb T bit) change how bytes decode. Changing one after disassembly needs a clear and re-disassemble to take effect.
- **Hex-Rays limits:** no 16-bit functions; functions whose unaliased stack exceeds 1 MB are refused; Hex-Rays' troubleshooting page says IDA64 decompiles only 64-bit functions and to use IDA32 for 32-bit (IDA 9.x ships a single unified binary, so check this against your install); `sizeof(int)` and `sizeof(enum)` must both be 4 in the compiler settings. After an internal error ("interr"), reload the database or restart IDA.
- **Don't edit SLEIGH or spec files in place.** Ghidra warns that a program saved from a modified install can corrupt when reopened on a stock one. Use Specification Extensions instead.
## Related Concepts
- [[concepts/executable-formats]]: headers, sections and debug info that feed the initial prototypes and types.
- [[entities/ghidra]] and [[entities/ida-pro]]: the two decompilers described here. [[syntheses/choosing-a-disassembler]] compares them with JEB, Hopper and Rizin/Cutter.
- [[concepts/go-rust-and-dart-binaries]] and [[concepts/dotnet-and-nativeaot]]: runtimes whose calling conventions and metadata break naive decompilation.
- [[syntheses/troubleshooting]]: symptom-first fixes across tools.
## Sources
- `raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi-3.md`
- `raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi-2.md`
- `raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi.md`
- `raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi-4.md`
- `raw/web_community-introduction-to-decompilation-vs-disassembly-md.md`
- `raw/web_community-tricks-md.md`
- `raw/web_community-failures-md.md`
- `raw/web_community-get-started-md.md`
- `raw/web_community-ctree-view-md.md`
- `raw/web_community-microcode-view-md.md`
- `raw/github_doc-ghidradocs-ghidraclass-beginner-introduction-to-ghidra-stude.md`
---
title: ".NET Assemblies and NativeAOT"
type: concept
tags: [dotnet, cil, ilspy, dnspyex, nativeaot, readytorun, ghidra, managed-code]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
sources: ["raw/web_community-assemblies-in-net-net-microsoft-learn.md", "raw/web_community-native-aot-deployment-overview-net-microsoft-learn.md", "raw/github_doc-readme-md-7.md", "raw/web_community-getting-started-with-icsharpcode-decompiler-md.md", "raw/github_doc-readme-md-8.md", "raw/github_doc-readme-md-9.md", "raw/github_doc-docs-managed-code-analysis-md.md", "raw/github_doc-docs-ghidra-nativeaot-md.md"]
---
# .NET Assemblies and NativeAOT
The first question with any .NET target is which kind of build you have. An ordinary assembly still carries CIL (Common Intermediate Language) and metadata, so an IL decompiler such as ILSpy can turn it back into readable C#. A NativeAOT build has been compiled to machine code ahead of time, so there are no CIL method bodies left to decompile. It has to be treated as a native binary in Ghidra (or Hopper), with the ghidra-nativeaot extension recovering the type information the runtime still needs.
## Definition
- **Assembly.** Microsoft describes the assembly as the fundamental unit of deployment, version control, reuse, activation scoping and security permissions for .NET. It ships as an `.exe` or `.dll` PE file containing CIL code, type metadata and an **assembly manifest**. CIL in a PE file is not executed unless a manifest goes with it. Type identity includes the assembly name, so a `MyType` in one assembly is a different type from a `MyType` in another.
- **ReadyToRun (R2R).** A managed assembly that also carries precompiled native code. It is still a PE/CLI assembly. ILSpy lists ReadyToRun binary support for .NET Core, and REA reads a ReadyToRun signature as one of the markers that identify a runtime family.
- **NativeAOT.** Publishing with `true` produces a self-contained app compiled to native code at publish time. It runs without a JIT and bundles a stripped-down coreclr runtime. Each build targets one runtime identifier (RID), for example `win-x64` or `linux-arm64`.
## How It Works
### Managed assemblies: decompile the IL
Because CIL and metadata survive compilation, decompilers can rebuild types, signatures and method bodies with high fidelity.
| Tool | What it is for | Notes from its docs |
|---|---|---|
| **ILSpy** | Browsing and decompiling assemblies to C# | MIT-licensed, cross-platform desktop UI (Avalonia) on Windows, Linux and macOS. Whole-project decompilation, type/method search, a metadata explorer, BAML-to-XAML, ReadyToRun support, plugins. Also ships as the `ilspycmd` dotnet tool, a VS Code extension, PowerShell cmdlets, and the `ICSharpCode.Decompiler` / `ICSharpCode.ILSpyX` NuGet packages. Visual Studio 2022/2026 use its engine for F12 decompilation. The README says it uses no APM service to collect telemetry or metrics. |
| **dnSpyEx** | Debugging and editing assemblies without source | An unofficial continuation of dnSpy, GPLv3. Debugs .NET Framework, .NET and Unity assemblies: breakpoints in any assembly, locals/watch windows, tracepoints, break on module load. Edits metadata, C#/VB method bodies and raw IL. Its hex editor jumps by metadata token, RVA or `#US`/`#Blob` heap offset. It uses ILSpy's decompiler engine and dnlib, which can read obfuscated assemblies. |
dnSpyEx documents two useful debugger behaviours. When an assembly decrypts itself at runtime, dnSpyEx can debug the in-memory image, and you can force it to always use in-memory images. Dynamic modules can be debugged, but dynamic methods cannot because of CLR limitations.
To script decompilation, add the `ICSharpCode.Decompiler` package and construct `new CSharpDecompiler(fileName, new DecompilerSettings())`. `DecompileTypeAsString(new FullTypeName("Demo.ConsoleApp.Test+NestedClassTest"))` returns one type as C#. Note the reflection-style `+` for nested types. Member tokens come from the type system (`typeInfo.Methods.First().MetadataToken`) and can be passed to `DecompileAsString`.
### NativeAOT: the IL is gone
NativeAOT compiles IL to native code during `dotnet publish`, for example `dotnet publish -r win-x64 -c Release` or `dotnet publish -r linux-arm64 -c Release`. Several consequences matter for analysis:
- **No CIL bodies.** A CIL decompiler cannot produce C# for the methods. Decompiling the native code gives pseudocode plus your own inferences, not original source.
- **Not just Windows `.exe`.** Output may be a PE, ELF or Mach-O image, and shared-library builds (PE DLL, ELF `.so`, Mach-O `.dylib`) export chosen entry points explicitly. Identify the format from bytes and loader metadata, not the file extension (see [[concepts/executable-formats]]).
- **Debug info is usually split off.** Publishing writes symbols to a separate file by default: `.dbg` on Linux, `.pdb` on Windows, a `.dSYM` folder on macOS. On Unix-like platforms, `false` embeds them in the binary instead. If you can get the matching sidecar, check that its identity matches before you trust it.
- **Bigger, flatter code.** Every generic instantiation over a struct type gets its own pregenerated code. Generic virtual methods get one instantiation per implementing type. Expect lots of near-duplicate functions.
- **Language features that can't exist.** NativeAOT rules out dynamic loading (`Assembly.LoadFile`), runtime code generation (`System.Reflection.Emit`) and C++/CLI, and on Windows it has no built-in COM. It requires trimming and implies single-file compilation. Don't go looking for these mechanisms in a confirmed NativeAOT binary.
### Recovering NativeAOT metadata in Ghidra
The runtime still needs MethodTables (type descriptors with vtables), frozen objects such as string literals, and a ReadyToRun directory that roots all of it. **ghidra-nativeaot** (Washi1337, MIT) is a Ghidra analyzer and UI plugin for .NET 8+ NativeAOT binaries that rebuilds this layer. It reconstructs the full type hierarchy, annotates frozen objects (strings), detects vtable redirection, and adds a metadata browser and a refactoring engine for virtual methods.
Build and install:
```sh
gradle # assumes Ghidra under /opt/ghidra
gradle -PGHIDRA_INSTALL_DIR=/path/to/ghidra
```
Gradle writes a plugin ZIP to `dist/`. Install it from **File > Install Extensions** in the Ghidra project window. Then open the binary in the CodeBrowser, run the **Native AOT Analyzer** (one-shot or as part of auto-analysis), and open the **Native AOT Metadata Browser** from the Windows menu. General Ghidra workflow is covered in [[entities/ghidra]].
**Finding the ReadyToRun header** is the step most likely to need manual help on stripped binaries. The plugin first looks for the symbol `__ReadyToRunHeader` or the pair `__modules_a` / `__modules_z`. If neither exists, it scans non-executable memory for the `RTR\0` signature plus plausible header fields. When that finds nothing, or finds too many candidates, use the fallback from the README. In `wmain`, the RTR directory list is the **second argument** to `S_P_CoreLib_Internal_Runtime_CompilerHelpers_StartupCodeHelpers__InitializeModules`. To locate that call in a stripped binary, compile a Hello World with symbols, compare the two in Ghidra's Version Tracking tool, follow the pointer array to data that starts with `RTR\0`, and label it `__ReadyToRunHeader`. Then rerun the analyzer. The README's worked example is Flare-On 11's `fullspeed.exe`.
### REA's managed and NativeAOT routes
[[entities/rea]] (rea-agents) splits the two cases cleanly.
- **Managed assemblies.** `rea inspect-managed-artifact /absolute/path/Assembly.dll --json` identifies the assembly and its metadata coverage. `rea inspect-managed-members /absolute/path/Assembly.dll --type Example.Parser` decodes types, signatures, CIL bodies, call edges and `ldstr` literals for selected types. Neither loads nor executes the assembly. Separately, REA's `verify:managed` verification script runs an optional real-ILSpy comparison when `REA_ILSPY_CMD_PATH` points at an absolute `ilspycmd`; ordinary inspection needs no ILSpy.
- **NativeAOT.** A built-in read-only parser handles **x64 PE with .NET 8 RTR 9.1** metadata. It recovers MethodTables, base types, interfaces, virtual slots and frozen strings, and it also runs on REA's experimental Windows Ghidra route (checked manually with Ghidra 12.1.4 and JDK 21). Broader recovery that writes annotations into the Ghidra database needs the optional Linux headless adapter. You build it locally from the pinned ghidra-nativeaot commit and point `REA_GHIDRA_NATIVEAOT_JAR` at the JAR. Typical calls:
```sh
rea inspect-native-load-image ./NativeAotFixture --provider ghidra --json
rea inspect-native-data-type ./NativeAotFixture --address 0xMETHODTABLE_ADDRESS --provider ghidra --json
rea function ./NativeAotFixture 0xADDRESS --provider ghidra --json
```
## Key Parameters
| Item | Value / meaning |
|---|---|
| `true` | MSBuild property that turns on NativeAOT publishing |
| `dotnet publish -r ` | Publishes for one runtime identifier |
| `false` | Unix: embed debug info instead of a sidecar |
| NativeAOT targets (.NET 8) | Windows, Linux and macOS on x64 and Arm64. iOS, tvOS, MacCatalyst and Android are experimental. The .NET 9+ table adds x86 for Windows and Arm for Linux |
| `RTR\0` | Signature at the start of the ReadyToRun directory |
| `__ReadyToRunHeader`, `__modules_a`/`__modules_z` | Symbols ghidra-nativeaot uses to find RTR metadata |
| `0x70xxxxxx` | User-string (`ldstr`) tokens. `0x70000000` is the empty `#US` entry |
| `0x06xxxxxx` | MethodDef tokens. Only meaningful inside one build (module version ID, MVID) |
| `REA_ILSPY_CMD_PATH`, `REA_GHIDRA_NATIVEAOT_JAR` | REA's optional ILSpy check and NativeAOT adapter |
## When To Use
- **CIL present** (the CLI header and metadata parse, method bodies exist): start with ILSpy for reading, dnSpyEx when you need to debug or step through the code, and REA when you need structured facts or cross-build member comparison.
- **Native image, .NET suspected** (no CLI metadata, but an `RTR\0` directory is present, or symbolized builds show `S_P_CoreLib_...` startup helpers): treat it as NativeAOT. Use Ghidra with ghidra-nativeaot, or REA's parser for x64 PE.
- **Single-file bundles and IL2CPP** are separate formats. REA explicitly does not unpack .NET single-file hosts or decode IL2CPP metadata, and it won't infer NativeAOT from an ordinary native PE. Extract the components first and inspect each one on its own.
## Risks & Pitfalls
- **Tokens are not identities.** Metadata tokens and RVAs are build-local. You can't carry `0x06001234` into a different MVID and assume it names the same method. Match members across builds by signature and body structure.
- **Names are not proof.** Linking a NativeAOT native function to a specific managed method needs verified evidence. Similar names or vtables alone don't establish it. ghidra-nativeaot-derived names are generated, so original class and member names and custom field layouts stay unknown.
- **Prototypes are guesses.** REA notes that the upstream plugin's blanket `__thiscall` assignment is not authoritative for the x64 ABI. Check parameters against instructions and call sites (see [[concepts/decompilation-concepts]]).
- **Frozen strings live outside Ghidra's string list** in REA's parser route. Read them from `metadata_recovery.frozen_strings`, not `search_strings`.
- **P/Invoke matches are declarations.** A module and entry-point match between managed imports and native exports shows a declared link, not that the call ever ran. A missing import doesn't rule out dynamic resolution.
- **Debugging executes the target.** dnSpyEx debugging runs the assembly. Do it in an isolated VM for untrusted samples (see [[syntheses/malware-triage]]), and only on software you're authorized to examine ([[concepts/legal-and-ethics]]).
## Related Concepts
- [[concepts/executable-formats]]: PE, ELF and Mach-O containers that hold both managed and NativeAOT output
- [[concepts/decompilation-concepts]]: why native pseudocode differs from IL decompilation
- [[entities/ghidra]]: the host for ghidra-nativeaot
- [[entities/rea]]: managed inspection and NativeAOT parser routes
- [[concepts/go-rust-and-dart-binaries]]: the same metadata-recovery mindset for other compiled runtimes
- [[syntheses/triage-an-unknown-binary]]: telling managed, R2R and NativeAOT apart in the first minutes
- [[syntheses/choosing-a-disassembler]]
## Sources
- `raw/web_community-assemblies-in-net-net-microsoft-learn.md`
- `raw/web_community-native-aot-deployment-overview-net-microsoft-learn.md`
- `raw/github_doc-readme-md-7.md`
- `raw/web_community-getting-started-with-icsharpcode-decompiler-md.md`
- `raw/github_doc-readme-md-8.md`
- `raw/github_doc-readme-md-9.md`
- `raw/github_doc-docs-managed-code-analysis-md.md`
- `raw/github_doc-docs-ghidra-nativeaot-md.md`
---
title: "EVM Bytecode Analysis"
type: concept
tags: [evm, ethereum, smart-contracts, selectors, abi, evmole, cbor-metadata, solidity, jeb, opcodes]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
sources: ["raw/web_community-ethereum-virtual-machine-evm-ethereum-org.md", "raw/web_community-opcodes-for-the-evm-ethereum-org.md", "raw/web_community-contract-metadata-solidity-0-8-38-develop-documentation.md", "raw/github_doc-readme-md-17.md", "raw/github_doc-agent-skills-evm-bytecode-analysis-skill-md.md", "raw/github_doc-agent-skills-evm-bytecode-analysis-references-cli-md.md", "raw/github_doc-agent-skills-evm-bytecode-analysis-references-interpretation.md", "raw/github_doc-agent-mcp-readme-md.md", "raw/web_community-ethereum-analysis-jeb-decompiler.md", "raw/github_doc-docs-evm-bytecode-md.md", "raw/web_community-decompiling-jeb-decompiler.md"]
---
# EVM Bytecode Analysis
Reversing a smart contract without verified source starts from its **deployed runtime bytecode**. The questions are nearly always the same. Which functions does it expose (selectors and their likely argument types)? Does it read or write state, or accept value? Which storage slots does it touch? What compiler metadata does it carry? EVMole answers the interface and storage questions statically. The CBOR trailer gives compiler hints. JEB Pro decompiles the whole contract to Solidity-like code when you need the logic.
## Definition
- **EVM.** A deterministic virtual machine that every Ethereum node runs. Per ethereum.org it is a **stack machine with a depth of 1024 items**, and each item is a **256-bit word**. During execution it also has **memory**, a byte array that does not persist between transactions. Each contract account has **storage**, a persistent word-addressed store that is part of global state. **Transient storage** (`TSTORE`/`TLOAD`) lasts across internal calls within one transaction and is cleared when the transaction ends.
- **Creation vs runtime bytecode.** A contract-creation transaction runs constructor (creation) code, which may copy, transform or return the runtime code. The runtime code is what's stored at the address and executed on every call. EVMole and REA analyse runtime bytecode only. They don't run constructors or dig the runtime out of creation code.
- **Selector.** A 4-byte function identifier that the dispatcher compares against the start of calldata. Static analysis finds selector values, not names.
- **CBOR metadata.** A trailer the Solidity compiler appends by default. It encodes a metadata hash (IPFS, or Swarm `bzzr1`/`bzzr0`) and the `solc` version.
## How It Works
### Reading a dispatcher by hand
The contract entry point is at offset 0 and is generally the dispatcher. JEB names it `start()`. The opening bytes of EVMole's README example show the usual Solidity pattern (opcode values from the ethereum.org table):
| Bytes | Opcode | Meaning |
|---|---|---|
| `6080 6040 52` | PUSH1 0x80, PUSH1 0x40, MSTORE | Store 0x80 at memory offset 0x40 |
| `34 80 15 600e 57` | CALLVALUE, DUP1, ISZERO, PUSH1, JUMPI | Value check: continue only if no ether was sent |
| `5f 80 fd` | PUSH0, DUP1, REVERT | Revert path |
| `6004 36 10 6030 57` | PUSH1 4, CALLDATASIZE, LT, PUSH1, JUMPI | Jump to the revert block at 0x30 if calldata is shorter than 4 bytes |
| `5f 35 60e0 1c` | PUSH0, CALLDATALOAD, PUSH1 0xe0, SHR | Load the first word and shift right 224 bits, leaving the top 4 bytes |
| `80 63 2125b65b 14 6034 57` | DUP1, PUSH4, EQ, PUSH1 0x34, JUMPI | If the selector is `0x2125b65b`, jump to offset 0x34 (52) |
EVMole reports exactly that: selector `2125b65b` at `bytecodeOffset: 52`, arguments `uint32,address,uint224`, mutability `pure`. Opcodes worth recognizing on sight:
| Hex | Opcode | Why it matters |
|---|---|---|
| `35` | CALLDATALOAD | Reads calldata: selector and arguments |
| `34` | CALLVALUE | Payability checks |
| `54` / `55` | SLOAD / SSTORE | Persistent storage reads/writes |
| `5C` / `5D` | TLOAD / TSTORE | Transient storage |
| `56` / `57` / `5B` | JUMP / JUMPI / JUMPDEST | Control flow and jump targets |
| `F1` / `F4` / `FA` | CALL / DELEGATECALL / STATICCALL | External calls |
| `39` | CODECOPY | Copies the running code into memory |
| `F3` / `FD` / `FE` | RETURN / REVERT / INVALID | Exits |
| `5F` | PUSH0 | Pushes the constant 0 |
### EVMole: interface, storage and metadata
EVMole extracts selectors, arguments, state mutability, persistent and transient storage layouts, and CBOR metadata from bytecode, including unverified contracts. It ships JavaScript, Rust, Python and Go bindings. The README says it has been tested on Solidity and Vyper output, and it distinguishes normal ABI dispatch from selectors handled by fallback logic.
```bash
npx -y evmole@latest analyze --bytecode 0x6001600055
npx -y evmole@latest analyze --file runtime.hex --include selectors,arguments,stateMutability,storage
echo '0x6001600055' | npx -y evmole@latest analyze
```
| Option | Meaning |
|---|---|
| `--include` | Any of `selectors`, `arguments`, `stateMutability`, `storage`, `transientStorage`, `metadata`, `disassembly`, `basicBlocks`, `controlFlowGraph` |
| default set | Selectors, arguments, mutability, both storage kinds, metadata. Disassembly and CFG are opt-in |
| `--offset` / `--limit` | Paginate list sections (limit default 1,000, max 5,000) |
| `--pretty` | Human-readable JSON |
| `evmole schema` | Print the version 1 request/response/error schemas |
The CLI needs Node.js 22 or newer and writes one JSON document to stdout. Exit codes: `0` success, `2` bad CLI syntax or ambiguous input, `3` invalid bytecode or request, `4` a size/output limit was exceeded, `1` internal failure. Check every requested section's `pagination.truncated` and the `warnings` before drawing conclusions.
From code: `contractInfo(code, {selectors:true, arguments:true, stateMutability:true})` in JavaScript, or `contract_info(code, selectors=True, arguments=True, state_mutability=True)` in Python. Foundry's `cast selectors $(cast code )` uses EVMole's Rust implementation. Note that `cast code` fetches the code over RPC, and `cast selectors --resolve` adds guessed names to the output.
For agents, `npx -y evmole-mcp@latest` starts a stdio MCP server with one tool, `analyze_evm_bytecode`, which takes hex `bytecode`, optional `include`, `offset` and `limit`. It makes no network requests and fetches no addresses. The portable skill installs with `npx skills add cdump/evmole --skill evm-bytecode-analysis -g`. See [[syntheses/re-with-ai-agents-mcp]].
### Compiler metadata trailer
solc appends a CBOR map to the end of the deployed bytecode. The **last two bytes give the CBOR length**, so decode backwards from the end. Possible keys are `ipfs`, `bzzr1`, `bzzr0` (older), `experimental` and `solc`. Release builds encode the version in 3 bytes (major, minor, patch). Pre-release builds embed a full version string with commit and date. The Solidity docs warn against matching on a fixed prefix such as `0xa264`: the map can hold other keys, so use a real CBOR parser. Developers can suppress the trailer with `--no-cbor-metadata` or `settings.metadata.appendCBOR: false`, so its absence proves nothing. JEB's docs say the same about metadata in general: it's indicative and shouldn't be trusted.
### JEB Pro: Solidity-like decompilation
JEB's EVM analyzer finds public and private methods and tries to name methods and events without an ABI, using a hash dictionary of more than 340,000 entries for public method names. Its decompiler emits Solidity-like code and recognizes ERC20, ERC721 and MultiSigWallet implementations, storage variables, mutability, payability, event emission, `send`/`transfer` and precompile calls. Input files need a `.evm-bytecode` extension (binary or hex), or `.runtime` / `.bin-runtime` (hex, as solc produces). JEB can also download a contract by address from supported networks. In the GUI, Tab switches between assembly and decompiled view, Space shows a routine's CFG, N renames, X lists cross-references, and B cycles number base and rendering. The output deliberately contains constructs that aren't legal Solidity (raw EVM statements, memory accesses, occasionally `goto`), so don't expect it to recompile. Comparison with other tools is in [[syntheses/choosing-a-disassembler]].
### REA's offline route
`rea inspect-evm-interface ./runtime.hex hex --json` (or `./runtime.bin raw`) runs a bundled, unchanged EVMole 0.9.3 in a resource-limited worker. It needs util-linux `prlimit`, with `REA_EVM_PRLIMIT_COMMAND` as an override, and real verification covers flat legacy bytecode on Linux x64. It returns selectors with decoded-byte offsets and independently decodes the terminal CBOR map into `bytecode.metadata`. EOF-style `EF00` containers are rejected as unsupported. REA reports a SHA-256 of the file and of the decoded bytes. Neither is the Ethereum Keccak code hash. See [[entities/rea]].
## Key Parameters
| Item | Value |
|---|---|
| Stack | 1024 items of 256 bits |
| Selector size | 4 bytes |
| Selector extraction idiom | `PUSH0 CALLDATALOAD PUSH1 0xe0 SHR` |
| CBOR length | Last 2 bytes of the bytecode |
| Metadata keys | `ipfs`, `bzzr1`, `bzzr0`, `experimental`, `solc` |
| EVMole list limit | default 1,000, max 5,000 |
| EVMole runtime | Node.js 22+ |
| REA bundled EVMole | 0.9.3 |
## When To Use
- **"What can I call on this contract?"** Run EVMole with `selectors,arguments,stateMutability`.
- **"Does it touch slot X / use transient storage?"** Request `storage` / `transientStorage`.
- **"What compiled it?"** Decode the CBOR trailer (EVMole `metadata` or REA), and treat the result as a claim.
- **"What does function 0x… actually do?"** Use JEB's decompiler, or EVMole's `disassembly` / `controlFlowGraph` and read by hand.
## Risks & Pitfalls
- **Inference, not ABI.** Arguments, mutability and storage types are inferred from bytecode. Optimization, assembly, proxies or unreachable paths can make them incomplete or wrong. Say "EVMole infers `view`", not "the function is `view`".
- **Selectors are not names.** Don't present a guessed name (from a signature database or JEB's dictionary) as fact. EVMole's guidance is to resolve names only through a separately authorized signature source.
- **Creation code in, garbage out.** Passing constructor bytecode, or an address instead of bytecode, gives misleading or invalid results.
- **Empty results don't prove absence.** No candidates doesn't mean no callable interface, and a missing trailer doesn't mean the contract wasn't built with Solidity.
- **Stay offline unless you mean not to.** Both EVMole and REA avoid RPC and explorer lookups. Fetching code, resolving names or interacting with a live contract are separate, deliberate steps. Keep analysis within authorized audit, research or CTF scope ([[concepts/legal-and-ethics]]).
## Related Concepts
- [[concepts/webassembly]]: another stack-machine bytecode
- [[concepts/decompilation-concepts]]: why decompiled output needs verification
- [[syntheses/choosing-a-disassembler]]: JEB's EVM support among the options
- [[syntheses/re-with-ai-agents-mcp]]: evmole MCP and REA for agent workflows
- [[entities/rea]]
## Sources
- `raw/web_community-ethereum-virtual-machine-evm-ethereum-org.md`
- `raw/web_community-opcodes-for-the-evm-ethereum-org.md`
- `raw/web_community-contract-metadata-solidity-0-8-38-develop-documentation.md`
- `raw/github_doc-readme-md-17.md`
- `raw/github_doc-agent-skills-evm-bytecode-analysis-skill-md.md`
- `raw/github_doc-agent-skills-evm-bytecode-analysis-references-cli-md.md`
- `raw/github_doc-agent-skills-evm-bytecode-analysis-references-interpretation.md`
- `raw/github_doc-agent-mcp-readme-md.md`
- `raw/web_community-ethereum-analysis-jeb-decompiler.md`
- `raw/github_doc-docs-evm-bytecode-md.md`
- `raw/web_community-decompiling-jeb-decompiler.md`
---
title: "Executable Formats: ELF, PE, Mach-O and DEX"
type: concept
tags: [elf, pe, coff, mach-o, fat-binary, universal-binary, dex, dalvik, app-bundle, ipa, headers, sections, segments, imports, relocations, debug-info]
created: 2026-10-16
updated: 2026-10-16
confidence: high
sources: ["raw/web_community-elf-5-linux-manual-page.md", "raw/web_community-pe-format-win32-apps-microsoft-learn.md", "raw/web_community-loader-h.md", "raw/web_community-dalvik-executable-format-android-os-core-topics-android-open.md", "raw/github_doc-docs-binary-diagnostics-md.md", "raw/github_doc-docs-apple-application-analysis-md.md", "raw/github_doc-docs-native-investigation-md.md", "raw/web_community-readelf-gnu-binary-utilities.md", "raw/github_doc-src-tools-rz-bin-intro-md.md", "raw/web_community-objdump-gnu-binary-utilities.md", "raw/github_doc-readme-md-21.md"]
---
# Executable Formats: ELF, PE, Mach-O and DEX
## Definition
An executable format is the on-disk layout that tells a loader (or a disassembler) where code and data are, how to map them into memory, what external libraries and symbols are needed, and where relocation and debug information lives. Reverse engineers meet four formats constantly:
| Format | Platforms | Magic at offset 0 | Typical tools |
|---|---|---|---|
| **ELF** | Linux, Android native libs, most Unix, much firmware | `0x7f 'E' 'L' 'F'` | `readelf`, `objdump`, `nm`, `rz-bin` |
| **PE/COFF** | Windows (EXE, DLL), .NET assemblies | `MZ` (MS-DOS stub), then `PE\0\0` at the offset stored at `0x3c` | `objdump -p`, `rz-bin`, DiE |
| **Mach-O** | macOS, iOS | `0xfeedface` (32-bit) / `0xfeedfacf` (64-bit), or byte-swapped `0xcefaedfe` / `0xcffaedfe` | `rz-bin`, `otool`, [[entities/rea]] Mach-O tools |
| **DEX** | Android (Dalvik/ART bytecode) | `dex\n` + 3-digit version + `\0`, e.g. `dex\n039\0` | JADX, apktool, `rz-bin` |
## How It Works
### ELF
An ELF file starts with the ELF header, which is always at offset zero. It is followed by a **program header table** (segments, used by the loader), a **section header table** (sections, used by linkers and analysts), or both.
- **Header (`ElfN_Ehdr`).** `e_ident` holds the magic, the class (`ELFCLASS32`/`ELFCLASS64`), data encoding (`ELFDATA2LSB`/`ELFDATA2MSB`), and OS/ABI. `e_type` is `ET_REL`, `ET_EXEC`, `ET_DYN` (shared object, which also covers PIE executables) or `ET_CORE`. `e_machine` gives the architecture and `e_entry` the entry point.
- **Segments.** `PT_LOAD` segments are mapped into memory. If `p_memsz` > `p_filesz`, the extra bytes are zero-filled, which is how `.bss` works. `PT_INTERP` names the dynamic loader. `PT_DYNAMIC` points at dynamic-linking info. `PT_NOTE` holds notes. `PT_GNU_STACK` flags control whether the stack is executable.
- **Sections that matter in RE.** Code is in `.text`. Read-only data such as strings and constants is in `.rodata`. `.data` is initialized data and `.bss` is zero-initialized data (`SHT_NOBITS`, no file bytes). The full symbol table is `.symtab`/`.strtab`, and `.dynsym`/`.dynstr` is the runtime subset. Dynamic linking uses `.dynamic`, `.plt` (procedure linkage table) and `.got` (global offset table). Relocations are in `.rel*`/`.rela*`, notes in `.note*`, and debug info in `.debug*` (DWARF).
- **Imports.** ELF has no import table as such. Needed libraries are `DT_NEEDED` entries in `.dynamic`, and imported functions are undefined dynamic symbols resolved through the PLT/GOT.
When a binary is stripped, `.symtab` is gone. Look at `.dynsym` instead: whatever the dynamic linker needs to resolve is still listed there.
### PE/COFF
A PE image starts with an **MS-DOS stub** (the "This program cannot be run in DOS mode" program). At `0x3c` is the file offset of the 4-byte signature `PE\0\0`. After the signature come the **COFF file header** (machine type such as `IMAGE_FILE_MACHINE_AMD64` or `IMAGE_FILE_MACHINE_ARM64`, number of sections, characteristics), the **optional header**, and the section table.
- **PE32 vs PE32+.** The optional-header magic is `0x10b` for PE32 and `0x20b` for PE32+ (64-bit address space, image size still limited to 2 GB).
- **RVAs.** Most addresses in a PE are **relative virtual addresses**: the address after loading minus the image base. An RVA almost never equals the file offset. Convert between them through the section table.
- **Data directories** in the optional header locate the export table (`.edata`), the import table (`.idata`), resources (`.rsrc`), exception data (`.pdata`), base relocations (`.reloc`), the TLS table, the debug directory, the **Import Address Table (IAT)**, delay-load imports, and the **CLR runtime header**, which marks the file as a .NET assembly.
- **Imports.** The import directory table has one entry per DLL, each pointing to an import lookup table of names or ordinals and to the IAT. The loader fills the IAT with real addresses, and code calls through it.
- **Certificates.** The attribute certificate table (Authenticode) is not mapped into memory, so its directory entry holds a **file pointer**, not an RVA. Certificates and debug data must sit at the end of the image file.
- **Debug info.** The debug directory says what kind of debug data exists and where. CodeView/PDB references are the usual source of leaked build paths.
### Mach-O
- **Header.** The magic is `MH_MAGIC` (`0xfeedface`) or `MH_MAGIC_64` (`0xfeedfacf`). The CIGAM forms mean the other byte order. File types include `MH_EXECUTE` (0x2), `MH_DYLIB` (0x6), `MH_BUNDLE` (0x8), `MH_DSYM` (0xa, debug companion only), and `MH_FILESET` (0xc). The `MH_PIE` flag (main executables only) tells the OS to load the program at a random address.
- **Load commands** follow the header and describe everything else:
| Load command | Purpose |
|---|---|
| `LC_SEGMENT_64` | maps a segment (`__PAGEZERO`, `__TEXT` with `__text`, `__DATA`, `__LINKEDIT`) |
| `LC_MAIN` | entry point (replaces `LC_UNIXTHREAD`) |
| `LC_LOAD_DYLIB` / `LC_LOAD_WEAK_DYLIB` | needed libraries |
| `LC_RPATH` | run-path entries used by `@rpath` |
| `LC_SYMTAB` / `LC_DYSYMTAB` | symbol tables |
| `LC_DYLD_INFO_ONLY` / `LC_DYLD_CHAINED_FIXUPS` | binding and rebasing info |
| `LC_FUNCTION_STARTS` | compressed function-start table, handy on stripped binaries |
| `LC_UUID`, `LC_BUILD_VERSION` | identity, platform and minimum OS |
| `LC_CODE_SIGNATURE` | code signature location |
| `LC_ENCRYPTION_INFO(_64)` | encrypted range: `cryptoff`, `cryptsize`, `cryptid` (0 = not encrypted) |
- **Universal (FAT) binaries** wrap several Mach-O slices, one per architecture. Every analysis has to pick a slice. Offsets inside a slice are relative to that slice, so add the slice's container offset to get a position in the original file. REA reports this as `selected_architecture` plus `container_file_offset`.
- **Library resolution** follows dyld rules. `@executable_path` is the main executable's directory. `@loader_path` is the directory of the image that holds the load command. `@rpath` tries each `LC_RPATH` of the loading image, then those of its loaders. REA's `trace_dylib_resolution` reproduces this statically, off macOS, without executing anything.
**App bundles.** An iOS IPA holds `Payload/.app`, a shallow bundle with `Info.plist` at its root. A macOS `.app` has `Contents/` with executables in `Contents/MacOS/`. Inside a bundle, look for nested code: `*.appex` app extensions, `*.xpc` services, `*.framework` (versioned with `Versions/`), login items in `Contents/Library/LoginItems/`, privileged helpers in `Contents/Library/LaunchServices/`, and launchd plists in `Contents/Library/LaunchAgents/` or `LaunchDaemons/`. **Each executable is its own process root**, with its own `@executable_path`.
### DEX
A `.dex` file holds Dalvik bytecode for a whole set of classes. After the header come sorted, de-duplicated ID tables: `string_ids`, `type_ids`, `proto_ids`, `field_ids`, `method_ids`, then `class_defs` (superclasses and interfaces listed before the classes that use them), `call_site_ids`, `method_handles` (unsorted, duplicates allowed), the data area (which holds the `map_list`), and `link_data`. `endian_tag` is `ENDIAN_CONSTANT` `0x12345678`. **Versions**: `039` came with Android 9.0 (adds `const-method-handle`/`const-method-type`), `040` with Android 10 (wider `SimpleName` characters), and `041` is experimental in Android 16 for the container format and is not meant for production code. APK packaging and decompilation are covered in [[syntheses/android-apk-analysis]].
## Key Parameters
| Need | ELF | PE | Mach-O |
|---|---|---|---|
| Architecture | `e_machine` | COFF `Machine` | `cputype` (per slice) |
| Entry | `e_entry` | `AddressOfEntryPoint` (RVA) | `LC_MAIN` |
| Needed libraries | `DT_NEEDED` | import directory | `LC_LOAD_DYLIB` |
| Load-time fixups | `.rel(a).*`, RELR | `.reloc` base relocations | `LC_DYLD_CHAINED_FIXUPS` / dyld info |
| Debug info | `.debug_*` (DWARF) | debug directory → PDB | `MH_DSYM` companion |
| Address model | virtual addresses in `p_vaddr` | RVAs + image base | segment `vmaddr` |
Commands: `readelf -h -l -S -d -s f` (ELF header, segments, sections, dynamic, symbols), `objdump -p f` (format-specific headers, including PE data directories), `rz-bin -H f` (header fields), `rz-bin -SSS f` (section-to-segment map), `rz-bin -A f` (list the arch slices in a FAT binary), `rz-bin -R f` (relocations).
## When To Use
- **Triage**: identify the format and architecture before choosing tools ([[syntheses/triage-an-unknown-binary]]).
- **Fixing a bad load**: wrong base, wrong slice, missing segments, or a raw blob with no header ([[syntheses/firmware-analysis]]).
- **Interpreting decompiler output**: knowing PLT/GOT and IAT thunks explains the indirect calls you see ([[concepts/decompilation-concepts]]).
- **Malware work**: section anomalies, overlays, and certificate placement are classic signals ([[syntheses/malware-triage]]).
## Risks & Pitfalls
- **Linked address ≠ runtime address.** PIE and ASLR relocate images. REA keeps the runtime load base null in static results for exactly this reason.
- **RVA vs file offset confusion** in PE. Patching or carving at an RVA without converting it corrupts the wrong bytes.
- **`ET_DYN` is ambiguous.** It can be a shared library or a PIE executable. Check for `PT_INTERP` and a non-zero entry point, but neither is conclusive: elf(5) notes `PT_INTERP` may also occur in shared objects.
- **Section headers can be missing or damaged.** An ELF file only needs a program header table *or* a section header table, and segments are what get mapped. A file with no section table can still run. A missing symbol or relocation section doesn't mean the runtime tables are absent, and RELRO or canary heuristics that depend on sections become incomplete. DiE's format-integrity checks flag damaged PE structures the same way.
- **Names aren't paths.** `DT_NEEDED`, `PT_INTERP`, and `LC_LOAD_DYLIB` give names to resolve, not resolved files.
- **FAT slice offsets** are container-relative for the slice table and slice-relative inside it. Mixing them up misplaces every address.
- **Encrypted Mach-O** (`cryptid` ≠ 0): the encrypted range is unreadable statically. This wiki does not cover decrypting App Store binaries.
- **Absent canary symbol** doesn't prove every function lacks a stack protector.
## Related Concepts
- [[syntheses/triage-an-unknown-binary]]: commands that read these structures
- [[concepts/dotnet-and-nativeaot]]: what the CLR header means for analysis
- [[syntheses/android-apk-analysis]]: DEX in its APK context
- [[entities/frida]]: iOS and macOS runtime work on Mach-O targets
- [[concepts/decompilation-concepts]]: how disassemblers use these tables
## Sources
- `raw/web_community-elf-5-linux-manual-page.md`
- `raw/web_community-pe-format-win32-apps-microsoft-learn.md`
- `raw/web_community-loader-h.md`
- `raw/web_community-dalvik-executable-format-android-os-core-topics-android-open.md`
- `raw/github_doc-docs-binary-diagnostics-md.md`
- `raw/github_doc-docs-apple-application-analysis-md.md`
- `raw/github_doc-docs-native-investigation-md.md`
- `raw/web_community-readelf-gnu-binary-utilities.md`
- `raw/github_doc-src-tools-rz-bin-intro-md.md`
- `raw/web_community-objdump-gnu-binary-utilities.md`
- `raw/github_doc-readme-md-21.md`
---
title: "Go, Rust and Flutter/Dart Binaries"
type: concept
tags: [go, golang, pclntab, goresym, rust, symbol-mangling, cargo-auditable, flutter, dart, blutter, pwndbg]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
sources: ["raw/web_community-abi-internal-md.md", "raw/github_doc-readme-md-10.md", "raw/github_doc-docs-go-binaries-md.md", "raw/github_doc-docs-tutorials-go-debugging-md.md", "raw/web_community-symbol-mangling-the-rustc-book.md", "raw/web_community-v0-symbol-format-the-rustc-book.md", "raw/github_doc-docs-rust-binaries-md.md", "raw/github_doc-readme-md-11.md", "raw/github_doc-docs-flutter-analysis-md.md"]
---
# Go, Rust and Flutter/Dart Binaries
Go, Rust and Flutter (Dart AOT) binaries are native code, but each toolchain leaves its own structured metadata behind. Recover that metadata first, before you start reading disassembly. A "stripped" Go binary usually still has its function table and types. A Rust binary may carry its full crate dependency list. A Flutter app's snapshot hash tells you which Dart SDK built it, and the SDK version decides which tools can parse it.
## Definition
| Runtime | Metadata worth recovering | Main tools |
|---|---|---|
| **Go** | `pclntab` (function names, entry/end addresses, file and line tables), `moduledata` (types, typelinks, itablinks), embedded build info (compiler version, modules, build settings) | GoReSym, REA (rea-agents, see [[entities/rea]]) `inspect-go-binary`, pwndbg `go-dump`/`go-type` |
| **Rust** | Mangled symbols (v0 scheme, `_R` prefix), optional cargo-auditable `.dep-v0` dependency section, `/rustc//` paths in read-only data | `rustfilt`, gdb/lldb demangling, REA `inspect-rust-binary` |
| **Flutter/Dart** | `libapp.so` AOT snapshot (hash, object pool, `package:` URIs), `libflutter.so` engine build ID | blutter, REA `identify-flutter-build` / `inspect-dart-aot` |
## How It Works
### Go: pclntab, moduledata and the register ABI
The Go runtime needs function and type metadata for stack traces, garbage collection and reflection, so the linker keeps it even when symbols are stripped. **GoReSym** (Mandiant, MIT) is built from the Go compiler's own `debug/gosym` and runtime code. It extends that code to handle stripped binaries, malformed UPX-unpacked binaries, data ranges split across sections, and locating `moduledata`. It reads ELF, PE and Mach-O on x86/x64 and ARM64 in either byte order.
```
GoReSym.exe -t -d -p /path/to/input.exe
```
| Flag | Effect |
|---|---|
| `-t` | Recover Go type names |
| `-d` | Include standard-library packages as well as user packages |
| `-p` | Print file paths embedded in the `pclntab` |
| `-strings` | Pull Go strings via the string internment table |
| `-m ` | Recursively dump the `RTYPE` structure at that address |
| `-v ` | Override automatic version detection (needed for some stripped binaries) |
| `-human` | Flat text instead of JSON |
The JSON output includes `Version`, `BuildId`, `Arch`, `TabMeta` (pclntab virtual address, layout version, pointer size), `ModuleMeta`, and arrays of `Types`, `Files`, `Strings`, `UserFunctions` and `StdFunctions`. To apply the results in IDA, run the repository's `IDAPython/goresym_rename.py` against the JSON (see [[entities/ida-pro]]).
GoReSym parses `pclntab` from Go 1.2 onward, and types from Go 1.5 onward because `moduledata` didn't exist before 1.5. It handles the pre-1.2, 1.2, 1.16, 1.18 and 1.20 layouts and has added Go 1.24 parsing. The pclntab layout version never exceeds the runtime version: a Go 1.19 binary uses the 1.18 layout. When someone has tampered with the pclntab magic, GoReSym can locate the table through a `runtime_modulesinit` signature and repair the magic.
For supply-chain questions without a disassembler, `rea inspect-go-binary /absolute/path/to/application --json` reads the embedded build info: compiler version string, main module, dependencies with versions and checksums, replacements, and settings such as `GOOS`, `GOARCH` and `vcs.revision`. It covers ELF, PE and thin Mach-O, never runs the file, and doesn't need Go installed. Function names and types are outside its scope.
**Why Go decompiles badly.** Go functions use **ABIInternal**, a register-based convention the Go docs describe as unstable between Go versions. It is not the platform C ABI, so a decompiler that assumes System V or Microsoft x64 gets the parameters wrong. Hand-written assembly uses the stable stack-based **ABI0**, and wrappers bridge the two.
| | amd64 | arm64 |
|---|---|---|
| Integer args/results | RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11 | R0–R15 |
| Float args/results | X0–X14 | F0–F15 |
| Current goroutine | R14 | R28 |
| Closure context | RDX | R26 |
| Fixed zero | X15 (except Plan 9) | ZR |
| Frame pointer | RBP | R29 (link register R30) |
Each argument or result travels entirely in registers or entirely on the stack. Values that don't fit the remaining registers, or that contain non-trivial arrays, go on the stack. The caller also reserves (but doesn't fill) spill space for register arguments. Composite layouts follow from that: a `string` is pointer + length, a slice is pointer + `len` + `cap`, and an interface is a type or itab pointer plus a data pointer. That's why one Go `string` parameter shows up as two register arguments in pseudocode. Retyping prototypes is covered in [[concepts/decompilation-concepts]].
**At runtime**, pwndbg can dump Go values with Go syntax ([[entities/gdb-and-pwndbg]]):
```
pwndbg> go-dump map[string]int 0xc0000b20f0
pwndbg> go-dump struct(24){0:a:int;8:b:string} 0xc000108120
pwndbg> go-type 0x49fbc0
```
Structs are written as `struct(SIZE){OFFSET:NAME:TYPE;...}`. `-p` pretty-prints and `-x` shows integers in hex. Interface values (`any`, `interface`) dump without extra type info because the compiler emits a type object for every type the program uses. To find a type pointer for a non-interface value, look at what's passed to allocators such as `runtime.newobject` or `runtime.makeslice`, then pass that address to `go-type` or straight to `go-dump`.
### Rust: demangling and embedded dependencies
rustc mangles symbol names, and `-C symbol-mangling-version` selects the scheme. According to the rustc book (fetched 2026-10-16), **v0** is the one stable scheme, and switching back to the legacy scheme needs a nightly toolchain. v0 symbols start with `_R`, can be restricted to `A-Z`, `a-z`, `0-9` and `_` (Unicode is optional), and encode generic parameters in a way that can be decoded. The format is not a stable ABI, and the book defines no single canonical demangled form, so different tools may print the same symbol differently.
- Recent gdb and lldb demangle Rust symbols natively.
- `rustfilt` demangles on the command line: `rustfilt _RNvCskwGfYPst2Cb_3foo16example_function` prints `foo::example_function`.
- The `rustc-demangle` crate does it programmatically.
- Items marked `#[no_mangle]` or `#[export_name]`, and items declared in `extern` blocks, keep plain names. These are often FFI or export boundaries worth looking at first.
When a binary was built with cargo-auditable, it carries a zlib-compressed JSON dependency graph in `.dep-v0` (ELF and PE) or `__DATA,.dep-v0` (thin Mach-O). `rea inspect-rust-binary /absolute/path/to/application --json` decodes the crate names, versions, sources, root package and dependency edges. It also reports a `rustc_commit` when it finds a `/rustc/<40-hex>/` path in read-only data. That commit is an inference, not proof of which compiler built the file.
### Flutter/Dart: identify the SDK, then parse
In an Android Flutter APK, app code is an AOT snapshot in `lib//libapp.so` and the engine is `libflutter.so`. Snapshot data sections start with the magic `f5 f5 dc dc`. The **snapshot hash** sits 20 bytes after that magic and identifies the Dart SDK build. Mapping the hash to an SDK release needs an external dataset, such as the one reFlutter maintains. This matters because the snapshot layout changes with each release, so typed class and function recovery needs a parser built for that exact SDK.
Triage with REA, which parses the APK without running anything:
```sh
rea identify-flutter-build /targets/Example.apk
rea inspect-dart-aot /targets/Example.apk
rea inspect-dart-aot /targets/Example.apk --abi armeabi-v7a
```
The default ABI preference is arm64-v8a, then x86_64, x86, armeabi-v7a. The output includes the snapshot hash, per-ABI engine SHA-256 and GNU build IDs, the four `_kDart*Snapshot*` exported symbols, and a bounded string-pool projection. That projection lists `package:` URIs (effectively a dependency inventory) and `dart:` SDK URIs. Release engines often strip the `Dart VM version` string, and REA reports that as `null` rather than guessing. REA does not do the hash-to-SDK lookup itself.
**blutter** goes further by compiling the Dart AOT runtime for the detected version and using it to read the snapshot. Its limits are significant: Android `libapp.so` only, **arm64 only**, recent Dart versions only. It needs a C++20 compiler (g++>=13 or Clang>=16), and the author tests on Debian sid/trixie.
```
python3 blutter.py path/to/app/lib/arm64-v8a out_dir
python3 blutter.py path/to/app/lib/arm64-v8a out_dir --rebuild
```
Extract the APK's `lib` directory first. The script detects the Dart version from the engine and, if no matching blutter executable exists, checks out the Dart source and builds one. `--rebuild` forces a rebuild after `git pull`. Outputs are `asm/*` (libapp assembly with symbols), `pp.txt` (all Dart objects in the object pool), `objs.txt` (a nested object dump) and `blutter_frida.js` (a Frida script template for the app, see [[entities/frida]]). The author's TODO list says obfuscated apps still miss many functions, iOS binaries aren't supported, and APK or IPA input isn't supported yet.
## Key Parameters
| Item | Value |
|---|---|
| Go pclntab support (GoReSym) | Go 1.2 and later. Types from Go 1.5 |
| Go amd64 goroutine register | R14 |
| Rust v0 prefix | `_R` |
| Rust mangling flag | `-C symbol-mangling-version` |
| cargo-auditable section | `.dep-v0` / `__DATA,.dep-v0` |
| Dart snapshot magic | `f5 f5 dc dc` (hash at +20 bytes) |
| blutter target | Android arm64 `libapp.so`, recent Dart |
| blutter toolchain | g++>=13 or Clang>=16 |
## When To Use
- **Any Go binary:** run GoReSym before opening a disassembler, then import the names. If type parsing fails on a stripped binary, find the real Go version (for example from build info) and pass it with `-v`.
- **Go supply-chain or provenance questions:** read build info (`inspect-go-binary`) instead of reversing code.
- **Rust:** demangle everything first. Check for `.dep-v0` to get an exact crate list before you try to identify library code by hand.
- **Flutter:** identify the snapshot hash and engine first. Use blutter for arm64 Android with a recent Dart. For other cases (other ABIs, iOS, older SDKs), fall back to native analysis of `libapp.so` or JEB's Dart support (see [[syntheses/choosing-a-disassembler]]). APK handling itself is covered in [[syntheses/android-apk-analysis]].
## Risks & Pitfalls
- **Wrong Go version, wrong types.** GoReSym warns that type parsing fails when the `-v` version is inaccurate.
- **Absence proves little.** REA treats `build_info: null` as "no record found", not "not Go", and a missing `.dep-v0` doesn't mean the binary has no dependencies. Stripping, old toolchains and deliberate removal all erase metadata.
- **Embedded metadata is unauthenticated.** Compiler strings, module versions and crate lists are just file content. They don't authenticate the compiler, the dependencies or the commit.
- **Fat Mach-O.** REA's Go and Rust readers need a thin slice. Universal binaries aren't interpreted as one architecture, so extract a slice first ([[concepts/executable-formats]]).
- **Platform-ABI assumptions** produce misleading Go pseudocode. Fix calling conventions before trusting argument counts.
- **Dart class-like tokens are byte patterns**, not deserialized classes. Treat REA's `class_like_tokens` as hints.
- **blutter's Frida template is for apps you're permitted to test.** Instrumentation runs the target (see [[concepts/legal-and-ethics]]).
## Related Concepts
- [[concepts/dotnet-and-nativeaot]]: metadata recovery for another AOT-compiled runtime
- [[concepts/decompilation-concepts]]: fixing prototypes and calling conventions
- [[entities/gdb-and-pwndbg]]: pwndbg's Go commands
- [[entities/ida-pro]] and [[entities/ghidra]]: where recovered symbols get applied
- [[syntheses/android-apk-analysis]]: getting `lib//` out of an APK
- [[entities/rea]]: build-info, cargo-auditable and Flutter triage tools
- [[syntheses/triage-an-unknown-binary]]
## Sources
- `raw/web_community-abi-internal-md.md`
- `raw/github_doc-readme-md-10.md`
- `raw/github_doc-docs-go-binaries-md.md`
- `raw/github_doc-docs-tutorials-go-debugging-md.md`
- `raw/web_community-symbol-mangling-the-rustc-book.md`
- `raw/web_community-v0-symbol-format-the-rustc-book.md`
- `raw/github_doc-docs-rust-binaries-md.md`
- `raw/github_doc-readme-md-11.md`
- `raw/github_doc-docs-flutter-analysis-md.md`
---
title: "Legal and Ethical Boundaries of Reverse Engineering"
type: concept
tags: [legal, ethics, dmca, 1201, cdpa, interoperability, security-research, scope, authorization]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
sources: ["raw/github_doc-disclaimer-md.md", "raw/github_doc-security-md.md", "raw/web_community-17-u-s-code-1201-circumvention-of-copyright-protection-syste.md", "raw/web_community-rulemaking-proceedings-under-section-1201-of-title-17-u-s-co.md", "raw/web_community-copyright-designs-and-patents-act-1988.md", "raw/web_community-faq-jeb-decompiler.md", "raw/github_doc-doc-faq-md.md", "raw/github_doc-readme-md-21.md"]
---
# Legal and Ethical Boundaries of Reverse Engineering
> **Not legal advice.** This page explains the statutory hooks that practitioners cite and the boundaries this wiki keeps. Whether a particular project is lawful depends on your jurisdiction, the licence you accepted, and the facts. If the answer matters, ask a lawyer.
## Definition
Reverse engineering (RE) is recovering how software works from its compiled form: reading headers, disassembling, decompiling, tracing at runtime, or capturing traffic. Most of it is ordinary engineering work. Legal risk shows up in three places:
1. **Copyright.** Decompiling makes copies and derived representations of someone else's code.
2. **Anti-circumvention law.** In the US this is 17 U.S.C. § 1201 (the DMCA). Getting past a technical protection measure can be unlawful on its own, even when no copyright infringement follows.
3. **Contracts and access law.** Licence terms, terms of service, and computer-misuse statutes control what you may run, attach to, or intercept.
This wiki covers **legitimate analysis only**: interoperability, security and vulnerability research, malware analysis and defence, CTFs and education, software preservation, and auditing software you own or are authorized to examine. The REA project draws the same line in its responsible-use statement.
## How It Works
### US: 17 U.S.C. § 1201
Section 1201(a)(1)(A) forbids circumventing a technological measure that effectively controls access to a copyrighted work. Two statutory exceptions matter most to RE:
| Subsection | Who it covers | Conditions (paraphrased) |
|---|---|---|
| **1201(f) Reverse engineering** | Someone who has lawfully obtained the right to use a copy of a program | You may circumvent only to identify and analyse the elements needed for **interoperability** of an *independently created* program, only where that information was not already readily available to you, and only as far as the acts are not infringement. (f)(2) allows building the tools needed for this. (f)(3) allows sharing the results only to enable interoperability. (f)(4) defines interoperability as programs exchanging information and using what was exchanged. |
| **1201(j) Security testing** | Someone testing a computer, system or network | "Security testing" means access **with the authorization of the owner or operator**, solely for good-faith testing, investigating or correcting a security flaw. The act must not infringe copyright or break other law, and the statute names 18 U.S.C. § 1030 (the CFAA). Factors: whether findings were used only to improve the owner's security or shared directly with the developer, and whether they were handled in a way that does not enable infringement, privacy violations or security breaches. |
Two practical consequences:
- **1201(f) is narrow.** It covers interoperability, not "understanding the app" in general, and it does not apply once the information is readily available, for example in public documentation or an SDK.
- **1201(j) depends on authorization.** Without the owner's or operator's permission there is no security-testing exemption.
**Temporary exemptions.** Section 1201(a)(1)(B)–(C) has the Librarian of Congress, on the Register of Copyrights' recommendation, grant class-based exemptions in three-year cycles. The Copyright Office is running the **tenth triennial** proceeding now. Renewed exemptions will run **October 2027 to October 2030**, and past final rules go back to 2000. This page does not reproduce the current exemptions, so check copyright.gov/1201 for what applies today.
### UK: Copyright, Designs and Patents Act 1988, s50B (Decompilation)
Section 50B lets a **lawful user** of a program written in a low-level language convert it to a higher-level form, and make incidental copies while doing so, only when:
- decompiling is **necessary** to get the information needed to create an independent program that can operate with the decompiled program or another program (the "permitted objective"), and
- the information is not used for anything else.
The conditions are **not** met if you:
- already had the information readily available,
- decompile more than you need for the objective,
- give the information to people who don't need it for the objective, or
- use it to build a program substantially similar in expression to the original, or to do any other act restricted by copyright.
Under s50B(4), contract terms that try to prohibit a permitted decompilation are **void** (via s296A). The section was inserted with effect from 1 January 1993.
### Authorization is per operation
REA's disclaimer makes a point that applies to any toolchain. Permission to inspect an artifact does not automatically extend to **executing** it, **intercepting** its traffic, **changing a device**, or **interacting with another party's system**. Having a public download, a file on disk, or an open debugging port does not by itself give you permission for every operation. Static reading, runtime instrumentation, and network capture are separate questions, and each can need its own authorization. You are also responsible for credentials, personal data, and captured traffic you collect.
## Key Parameters
Questions to answer before you start:
| Question | Why it matters |
|---|---|
| Did you lawfully obtain the copy and accept its licence? | Both 1201(f) and s50B start from a *lawful user* |
| What is the objective: interoperability, security fix, malware defence, education? | The exceptions are purpose-bound |
| Is the information already available (docs, SDK, spec)? | Ready availability defeats both 1201(f) and s50B |
| Do you have the owner's or operator's authorization for testing? | Required for 1201(j). Also needed for runtime, network, and device work |
| Who will see the results? | s50B limits recipients to those who need the information; 1201(f)(3) limits sharing to enabling interoperability |
| Are you using commercial tools within their licence? | See "Tool licences" below |
### Tool licences
Commercial analysis tools have their own terms. Non-OEM JEB licences, for example, are subscription-based, with keys derived from machine identifiers. An expired licence still opens existing projects but cannot create new ones, and floating and OEM licences have their own rules. REA states that IDA Pro and the Hex-Rays decompilers, Hopper, JEB and other proprietary tools must be obtained legally. An open-source bridge or API does **not** grant extra user, machine, automation, hosting, or output-sharing rights. Cracked copies, stolen keys, and unauthorized key sharing are out of bounds. Getting source-like output from a decompiler also does not give you rights to the original program or to the output.
## When To Use
Read this page:
- before analysing third-party software for interoperability or vulnerability research,
- before any runtime, interception, or device step. Static authorization may not cover these. See [[syntheses/dynamic-analysis-workflow]], [[syntheses/network-captures-and-protocols]], and [[syntheses/android-apk-analysis]],
- before handling live malware. See [[syntheses/malware-triage]],
- before publishing findings, decompiled code, or tools.
### What this wiki deliberately does not cover
These topics are out of scope on every page, whatever the stated purpose:
- circumventing DRM, licence checks, activation, or other technical protection measures,
- anti-cheat bypass, game cheats, or tampering with online services,
- piracy, or reproducing proprietary code and assets,
- malware development, AV/EDR evasion, or packer and crypter authoring,
- certificate-pinning bypass recipes,
- getting around authentication, bot protection, or access controls on systems outside your testing authorization.
Dual-use techniques such as Frida hooking, apktool rebuilds, and TLS interception appear only in the context of an app or device you are permitted to test. Tools that *detect* protections (for example DiE, which reports DRM and licensing markers) are described for triage only.
## Risks & Pitfalls
- **Assuming 1201(f) covers curiosity.** It covers interoperability of an independently created program, nothing broader.
- **Testing without authorization.** 1201(j) requires the owner's or operator's permission and does not override the CFAA or other law.
- **Over-sharing.** s50B bars giving the information to anyone who does not need it for the permitted objective, and 1201(f)(3) allows sharing only to enable interoperability. Publishing decompiled code goes well beyond them.
- **Treating local work as risk-free.** Running a target can still affect accounts, devices, and services. REA notes that local operation does not guarantee executed targets or third-party tools have no external effects.
- **Treating analysis tooling as a sandbox.** REA's security policy says its authenticated Ghidra and Hopper bridges are **not a sandbox**: opening an untrusted binary hands parsing to the provider with your user's permissions. Isolation is your job.
- **Security tools trip antivirus.** capa ships rules that resemble malware features, and its PyInstaller packaging can alert AV/EDR. Download releases from the project's official release page, or build it yourself.
- **Leaking private material in bug reports.** REA asks for private vulnerability reports through the repository's Security tab, without proprietary binaries, Ghidra projects, Hopper documents, or capability tokens.
- **Jurisdiction.** Only the US and UK provisions are covered here. EU member states, contract law, export controls, and privacy law are not covered.
## Related Concepts
- [[syntheses/overview]]: what RE covers and where to start
- [[syntheses/malware-triage]]: safe handling and defensive analysis
- [[syntheses/dynamic-analysis-workflow]]: why execution needs its own authorization and isolation
- [[syntheses/android-apk-analysis]]: dual-use rebuild and proxy steps, framed for permitted testing
- [[entities/rea]]: the agent toolkit whose responsible-use terms are quoted above
## Sources
- `raw/github_doc-disclaimer-md.md`
- `raw/github_doc-security-md.md`
- `raw/web_community-17-u-s-code-1201-circumvention-of-copyright-protection-syste.md`
- `raw/web_community-rulemaking-proceedings-under-section-1201-of-title-17-u-s-co.md`
- `raw/web_community-copyright-designs-and-patents-act-1988.md`
- `raw/web_community-faq-jeb-decompiler.md`
- `raw/github_doc-doc-faq-md.md`
- `raw/github_doc-readme-md-21.md`
---
title: "WebAssembly Modules"
type: concept
tags: [webassembly, wasm, wat, wabt, wasm2wat, wasm-objdump, jeb, decompilation]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
wabt_version: "1.0.42"
sources: ["raw/web_community-overview-webassembly-3-0-2026-10-13.md", "raw/web_community-modules-webassembly-3-0-2026-10-13.md", "raw/github_doc-readme-md-14.md", "raw/github_doc-docs-doc-wasm2wat-1-html.md", "raw/github_doc-docs-doc-wasm-objdump-1-html.md", "raw/github_doc-docs-doc-wasm-validate-1-html.md", "raw/github_doc-docs-doc-wasm-strip-1-html.md", "raw/github_doc-docs-doc-wasm2c-1-html.md", "raw/github_doc-docs-doc-wasm-interp-1-html.md", "raw/web_community-webassembly-analysis-jeb-decompiler.md", "raw/github_doc-docs-wasm-artifacts-md.md", "raw/web_community-source-map-format-specification.md"]
---
# WebAssembly Modules
A WebAssembly (Wasm) module is a compact, validated binary for a stack-based virtual machine. In practice you analyse one in three moves: validate it and list its sections and imports/exports, decode it to the text format (WAT) to read the code, and decompile it to C-like pseudocode once the code is too large to read as WAT. WABT handles the first two. JEB Pro decompiles; `wasm2c` gives a mechanical C translation as an alternative.
## Definition
The core specification (WebAssembly 3.0, spec build dated 2026-10-13) describes:
- **Values.** Four basic number types: 32- and 64-bit integers and IEEE 754 floats. Integers have no signedness. Each operation decides whether to read them as signed (two's complement) or unsigned. 32-bit integers double as booleans and memory addresses.
- **Instructions.** A stack machine: simple instructions pop operands and push results on an implicit operand stack. Control flow is **structured** (well-nested blocks, loops and branches), so there are no arbitrary jumps to recover.
- **Functions** take and return sequences of values, can declare mutable locals that act like virtual registers, and share an implicit call stack that code can't access directly.
- **Tables** hold opaque references selected by a dynamic index. This is how indirect calls and function pointers are emulated.
- **Linear memory** is one contiguous, mutable array of raw bytes, accessed by load and store instructions. Data-section initializers are copied into it.
- **Modules** define functions, tables, memories and globals. They can **import** definitions by a module/name pair and **export** them under names. The **embedder** (browser, Node, a standalone runtime) supplies the imports. That host boundary is where most of a module's behaviour becomes visible.
- **Traps** abort execution immediately and are reported to the host. Wasm code can't catch them.
## How It Works
### Binary layout
A module starts with the 4-byte magic `\0asm` (`0x00 0x61 0x73 0x6D`) and a version field. The current binary version is 1. Sections follow, each made of a one-byte id, a `u32` byte length, and the contents. Every section is optional. Custom sections may appear anywhere. All other sections appear at most once, in a prescribed order that doesn't always match their numeric ids.
| Id | Section | Analyst use |
|---|---|---|
| 0 | custom | Debug names, `sourceMappingURL`, toolchain markers such as `dylink` |
| 1 | type | Function signatures |
| 2 | import | Host API surface: the first thing to read |
| 3 | function | Type index of each defined function |
| 4 | table | Indirect-call tables |
| 5 | memory | Linear memory limits |
| 6 | global | Globals |
| 7 | export | Entry points the host can call |
| 8 | start | Function run at instantiation |
| 9 | element | Table initializers (indirect-call targets) |
| 10 | code | Function bodies |
| 11 | data | Memory initializers: strings and constants live here |
| 12 | data count | Count of data segments |
| 13 | tag | Exception tags |
Function signatures and bodies are split between the function and code sections so engines can compile in parallel and while streaming. Their counts must match. Errors inside a custom section's data must not invalidate the module, which is why tools can offer to ignore custom-section errors. A module can point to a source map with a custom section named `sourceMappingURL` whose content is the URL. The source map spec says a module must not carry two of them.
### WABT: validate, inspect, decode
WABT (the WebAssembly Binary Toolkit) aims for full spec fidelity, including 1:1 text/binary round-trips with no instruction changes. Unlike Binaryen, it is not an optimizer.
| Task | Command |
|---|---|
| Validate | `wasm-validate test.wasm` |
| Section headers + details (imports, exports, etc.) | `wasm-objdump -h -x module.wasm` |
| Disassemble function bodies | `wasm-objdump -d module.wasm` |
| One section / raw bytes / relocations | `-j SECTION`, `-s`, `-r` |
| Binary to text | `wasm2wat test.wasm -o test.wat` |
| Text without embedded debug names | `wasm2wat test.wasm --no-debug-names -o test.wat` |
| Text back to binary | `wat2wasm` |
| C source + header | `wasm2c test.wasm -o test.c` (also writes `test.h`) |
| Remove custom sections | `wasm-strip test.wasm` (`-k SECTION` keeps one) |
| Size/opcode statistics | `wasm-stats` |
Useful `wasm2wat` switches for reading code:
- `--generate-names` gives auto-generated names to unnamed functions and types.
- `-f` / `--fold-exprs` writes nested (folded) expressions where possible.
- `--inline-imports` / `--inline-exports` put import and export declarations on their items.
- `--ignore-custom-section-errors` gets past a damaged custom section.
**Feature flags matter.** Exceptions, SIMD, multi-value, tail calls, bulk memory, reference types, memory64, multi-memory, extended-const and relaxed SIMD are on by default. Threads, function references, custom page sizes, compact imports and wide arithmetic are off by default and need `--enable-threads`, `--enable-function-references` and so on. The man pages also list `--enable-gc` and `--enable-code-metadata`. When a module that runs fine in a browser fails `wasm-validate`, try `--enable-all` before concluding it's malformed.
`wasm-interp` decodes **and runs** a module (`--run-export=FUNCTION`, `--run-all-exports`, `--trace`, `--wasi` (WebAssembly System Interface) with `--env`/`--dir`). It's handy for capture-the-flag (CTF) puzzles, but it executes the code, so treat it as dynamic analysis of an untrusted sample.
### Decompiling: JEB Pro
JEB Pro parses, disassembles and decompiles Wasm into C-like pseudocode on top of its native-code pipeline. It maps the module into a pseudo address space so native tooling can work with it:
| Pseudo-segment | Start | Holds |
|---|---|---|
| `.data` | `0` | The memory section and its data initializers |
| `.table` | `0x4000000` | Pointers to functions referenced from the table |
| `.code` | `0x50000000` | Internal function bodies, back to back in index order |
| `.globals` | `0x60000000` | Globals, treated like ordinary global variables |
| `.imports` | `0x70000000` | References to imported functions and globals |
JEB assumes a wasm32, little-endian environment. A `dylink` custom section in the Sections view likely means an Emscripten build with `SIDE_MODULE=1`. You can't modify code, because that would break the function index space. Documented decompiler limits: advanced global analysis is partly disabled, floating-point operations aren't converted, and memory stack frames can't be accessed or customized. Wasm 3.0 parsing arrived in JEB 5.36. SIMD instructions are wrapped as untranslated IR, `try_table` exception control flow and `br_on_null` / `br_on_non_null` aren't decompiled, and atomics (the `0xFE` table) are only partly parsed. Where JEB fits among disassemblers is covered in [[syntheses/choosing-a-disassembler]].
### REA's offline route
[[entities/rea]] inspects modules without running them, using a caller-supplied **WABT 1.0.42**. Point `REA_WABT_BIN_DIRECTORY` at a directory containing `wasm-validate`, `wasm-objdump` and `wasm2wat`:
```bash
REA_WABT_BIN_DIRECTORY=/absolute/wabt/bin rea inspect-wasm-artifact \
/artifacts/module.wasm --glue_paths /artifacts/glue.js \
--candidate_paths /artifacts/other/module.wasm --format json
```
Internally it runs `wasm-validate --enable-all`, `wasm-objdump -h -x` and `wasm2wat --enable-all`. Files over 8 MiB are refused. The optional JS glue files are parsed for `.wasm` string literals to suggest which file the page loads. Those are literal associations, not proven loading. `rea compare-wasm-interfaces comparison-input.json --json` diffs the typed imports and exports of two inspected modules. REA's general `open_binary` route deliberately refuses Wasm (`wasm_requires_static_inspection`) and redirects you to this tool. The real-tool profile has been exercised on macOS arm64. Other hosts need their own verification.
## Key Parameters
| Item | Value |
|---|---|
| Magic / version | `0x00 0x61 0x73 0x6D` / 1 |
| Section ids | 0 (custom) to 13 (tag) |
| Enable every proposal | `--enable-all` |
| Ignore debug names on decode | `--no-debug-names` |
| Synthesize names | `--generate-names` |
| JEB code base address | `0x50000000` |
| REA toolchain variable | `REA_WABT_BIN_DIRECTORY` (WABT 1.0.42) |
## When To Use
- **First pass on any `.wasm`:** run `wasm-validate`, then `wasm-objdump -h -x`. Read the import section to see which host functions (DOM, WASI, crypto, network glue) the module depends on, and the export section to see what the host calls.
- **Small modules or CTF checks:** read `wasm2wat --generate-names -f` output directly.
- **Large compiled C/C++/Rust:** use a decompiler (JEB Pro) or `wasm2c` and read the C. Concepts in [[concepts/decompilation-concepts]] carry over.
- **Wasm inside a web or Electron app:** recover the JS glue first ([[syntheses/javascript-and-electron-apps]]). The glue shows how imports are wired and which file actually loads.
## Risks & Pitfalls
- **WAT is a decoding, not source.** REA notes that custom annotations can preserve payloads, but WAT doesn't prove the original binary layout or byte-for-byte round-trip identity. Keep the original bytes and their hash.
- **Debug names can mislead.** Names embedded in the binary come from whoever produced it. Decode once with `--no-debug-names` and compare when names look too convenient.
- **Disabled proposals look like corruption.** Validation errors on threads, GC or function-reference opcodes often just mean a flag is missing.
- **Running is not inspecting.** `wasm-interp`, browsers and runtimes execute the module. Use an isolated environment for untrusted samples ([[syntheses/malware-triage]], [[syntheses/dynamic-analysis-workflow]]).
- **Static type equality is not compatibility.** REA states that matching interface signatures don't prove runtime or ABI compatibility or that linking will succeed.
## Related Concepts
- [[concepts/executable-formats]]: native containers for comparison
- [[concepts/decompilation-concepts]]
- [[syntheses/javascript-and-electron-apps]]: the JS that loads most modules
- [[syntheses/choosing-a-disassembler]]: JEB and other options
- [[entities/rea]]
- [[concepts/evm-bytecode]]: another stack-machine bytecode with its own recovery tools
## Sources
- `raw/web_community-overview-webassembly-3-0-2026-10-13.md`
- `raw/web_community-modules-webassembly-3-0-2026-10-13.md`
- `raw/github_doc-readme-md-14.md`
- `raw/github_doc-docs-doc-wasm2wat-1-html.md`
- `raw/github_doc-docs-doc-wasm-objdump-1-html.md`
- `raw/github_doc-docs-doc-wasm-validate-1-html.md`
- `raw/github_doc-docs-doc-wasm-strip-1-html.md`
- `raw/github_doc-docs-doc-wasm2c-1-html.md`
- `raw/github_doc-docs-doc-wasm-interp-1-html.md`
- `raw/web_community-webassembly-analysis-jeb-decompiler.md`
- `raw/github_doc-docs-wasm-artifacts-md.md`
- `raw/web_community-source-map-format-specification.md`
---
title: "Frida"
type: entity
tags: [frida, dynamic-instrumentation, frida-trace, frida-server, gadget, android, ios, javascript, tracing, hooking]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
frida_version: "17.24.0"
sources: ["raw/github_doc-i18n-en-docs-index-md.md", "raw/github_doc-i18n-en-docs-modes-md.md", "raw/github_doc-i18n-en-docs-installation-md.md", "raw/github_doc-i18n-en-docs-quickstart-md.md", "raw/github_doc-i18n-en-docs-bridges-md.md", "raw/github_doc-i18n-en-docs-frida-cli-md.md", "raw/github_doc-i18n-en-docs-frida-ps-md.md", "raw/github_doc-i18n-en-docs-frida-ls-devices-md.md", "raw/github_doc-i18n-en-docs-frida-kill-md.md", "raw/github_doc-i18n-en-docs-frida-trace-md.md", "raw/github_doc-i18n-en-docs-gadget-md.md", "raw/github_doc-i18n-en-docs-android-md.md", "raw/github_doc-i18n-en-docs-ios-md.md", "raw/github_doc-i18n-en-docs-best-practices-md.md", "raw/github_doc-i18n-en-docs-troubleshooting-md.md", "raw/github_issue-java-is-not-defiend.md", "raw/github_release-frida-17-24-0.md", "raw/github_doc-docs-frida-instrumentation-md.md"]
---
# Frida
## Overview
**Frida** is a dynamic code instrumentation toolkit. It loads a JavaScript runtime into a running program so your script can hook functions, read and write memory, enumerate modules and their imports and exports, and call native functions from inside the process. Results flow back to your host over a two-way message channel. It runs on Windows, macOS, GNU/Linux, iOS, watchOS, tvOS, Android, FreeBSD and QNX.
Use Frida when you want to watch what an app does at function level (which APIs it calls, with what arguments, in what order) without stopping it at every step the way a debugger does. Typical legitimate uses from Frida's own docs: tracing an app you need to interoperate with, adding diagnostics to your own deployed software without shipping a new build, and black-box testing in-house apps. Use it on your own builds, test devices, or apps you are authorized to analyze; see [[concepts/legal-and-ethics]].
The current release is **Frida 17.24.0** (published 2026-10-16). Its GitHub release page only points to frida.re/news for details, so per-release changes are not covered here.
## Characteristics
### Modes of operation
| Mode | How it gets in | When to use |
|---|---|---|
| **Injected** | frida-core injects the GumJS runtime as a shared library into a process you spawn, attach to, or catch at spawn. On mobile devices, `frida-server` exposes this over TCP, listening on `localhost:27042` by default | The normal case: rooted Android, jailbroken iOS, desktop processes you own |
| **Embedded** | You build `frida-gadget` (a shared library) into the program | When injection is not possible, e.g. jailed iOS or non-rooted Android, using your own debuggable build |
| **Preloaded** | Gadget configured to load a script from disk on its own, like an `LD_PRELOAD` for JavaScript | Autonomous instrumentation with no host connection |
The core (Gum) is written in C; scripts run in QuickJS by default, and `frida-trace` accepts `--runtime {qjs,v8}`. Bindings exist for Python, Node.js, Swift, .NET, QML, Go and C.
### The Frida 17 bridge change
From Frida 17.0.0, the **Java, ObjC and Swift bridges are no longer bundled** with the GumJS runtime. Only the REPL (`frida`) and `frida-trace` still ship all three bridges for compatibility. A script you load through the Python (or other) API that calls `Java.perform()` fails with `ReferenceError: 'Java' is not defined` until you bundle the bridge:
```
$ frida-create -t agent
$ npm install
$ npm install frida-java-bridge
```
Then import it at the top of the TypeScript agent (`import Java from "frida-java-bridge";`; likewise `frida-objc-bridge` and `frida-swift-bridge`) and either compile with `frida-compile script.ts -o _agent.js -S -c` or build from Python with `frida.Compiler()` and `compiler.build("script.ts", project_root=...)` before `session.create_script(bundle)`. Putting a bare `import` into an inline Python script string does not work; it must go through the compiler. The same rule applies to Gadget's autonomous script mode.
## How to Use
### Install and smoke-test
```
$ pip install frida-tools
```
Requirements: Python 3.x on Windows, macOS or GNU/Linux. To check it works, start a throwaway process (`cat`, or a copy at `/tmp/cat` on macOS, which rejects attaching to system binaries) and attach to it from a Python script that calls `frida.attach("cat")`. On GNU/Linux, attaching to non-child processes needs `sudo sysctl kernel.yama.ptrace_scope=0`.
### The CLI tools
| Tool | Use it to | Example |
|---|---|---|
| `frida` | Open the REPL attached to a process, load a script, live-reload it | `frida -U Safari`; `frida Calculator -l calc.js` (`%reload` reloads; `--debug` opens a Node.js-compatible debugger on port 5858) |
| `frida-ps` | List processes or apps on a device | `frida-ps -U` (USB processes), `frida-ps -Ua` (running apps), `frida-ps -Uai` (installed apps), `frida-ps -D ` |
| `frida-ls-devices` | List local, USB and remote devices with their IDs | `frida-ls-devices` |
| `frida-trace` | Auto-generate hooks for matching functions and log calls | see below |
| `frida-kill` | Kill a process on a device | `frida-kill -D ` |
In the REPL, tab completion lists the JavaScript API objects (`Interceptor`, `Memory`, `Module`, `Process`, `NativeFunction`, `Stalker`, `ObjC`, `Dalvik` and so on), which is a quick way to explore a live process.
### Tracing with frida-trace
`frida-trace` generates a handler file per matched function (`onEnter`/`onLeave`), prints each call, and reloads the handlers when you edit them. It is the fastest way to answer "does this app call X, and when?".
| Flag | Meaning |
|---|---|
| `-U` / `-D ID` / `-H HOST` | Target a USB device, a device by ID, or a remote frida-server |
| `-f TARGET` | Spawn the program or app |
| `-n NAME` / `-N IDENTIFIER` / `-p PID` | Attach by name, app identifier or PID |
| `-i` / `-x` `[MODULE!]FUNCTION` | Include or exclude functions by glob |
| `-I` / `-X MODULE` | Include or exclude whole modules |
| `-a MODULE!OFFSET` | Trace an unexported function at an offset |
| `-m` / `-j` / `-y` | ObjC methods, Java methods, Swift functions |
| `-d`, `--decorate` | Add the module name to each log line |
| `-o OUTPUT` | Write messages to a file (useful as evidence) |
| `-O FILE` | Read more options from a file |
```
$ frida-trace -i "recv*" -i "read*" # network reads
$ frida-trace -U -i open -N com.android.chrome # open() on Android
$ frida-trace -U -i "Java_*" # JNI entry points
$ frida-trace -p 1372 -i "msvcrt.dll!*mem*" # one module on Windows
$ frida-trace -p 1372 -a "libjpeg.so!0x4793c" # an unexported function
```
Edit a generated handler to print real arguments, for example `args[0].toInt32()` or `args[0].readUtf8String()`. Store per-call state on `this` in `onEnter` so `onLeave` can read it.
### Android setup (test device or emulator)
Frida's tutorial uses a rooted device; it recommends a Pixel or Nexus on current official software, or a Google Android 9 arm/arm64 emulator image (x86 has had less testing). You need `adb` from the Android SDK.
```
$ adb shell getprop ro.product.cpu.abilist # pick the matching frida-server build
$ unxz frida-server.xz
$ adb root # might be required
$ adb push frida-server /data/local/tmp/
$ adb shell "chmod 755 /data/local/tmp/frida-server"
$ adb shell "/data/local/tmp/frida-server &"
$ adb devices -l
$ frida-ps -U
```
Run frida-server as root. If `adb root` says `adbd cannot run as root in production builds`, prefix commands with `su -c`. When writing your own Python tools, use `frida.get_usb_device().attach()` in place of `frida.attach()`. Without root, Frida's docs mention embedding Gadget into the app or using a debugger. For the wider APK workflow see [[syntheses/android-apk-analysis]].
### iOS setup
- **Jailbroken device**: add the `https://build.frida.re` repository in Cydia, install the `Frida` package, then `frida-ps -U` over USB (usbmuxd integration makes `-U` work on Linux too). Example trace: `frida-trace -U -i "CCCryptorCreate*" Twitter`.
- **Non-jailbroken device**: Frida instruments **debuggable** apps (your own development builds) and injects Gadget automatically. It wants iOS 13 or newer (older is experimental), a mounted Developer Disk Image (Xcode mounts it, or use `ideviceimagemounter`), and the latest Gadget in the cache, on macOS `~/.cache/frida/gadget-ios.dylib`.
### Gadget
Gadget starts when the dynamic linker runs its constructor. You load it by adding it to your program's build, linking it in, or with `LD_PRELOAD`/`DYLD_INSERT_LIBRARIES`. A config file named like the library with a `.config` extension (on Android, `libgadget.config.so` next to `libgadget.so`, because the package manager only copies `lib*.so` files) selects one of four interactions:
| Interaction | Behaviour |
|---|---|
| `listen` (default) | frida-server-compatible endpoint on `127.0.0.1:27042`; the process appears as `Gadget` / `re.frida.Gadget`. `on_load: "wait"` blocks startup until you attach or resume; set `"resume"` to let it boot |
| `connect` | Joins a `frida-portal` cluster (default port `27052`) |
| `script` | Runs a script from disk before the entry point; `init(stage, parameters)` and `dispose()` hooks via `rpc.exports` |
| `script-directory` | Loads scripts from a directory |
Other root keys: `teardown` (`minimal` or `full`), `runtime` (`default`, `qjs`, `v8`), and `code_signing` (`optional` or `required`; `required` lets it run on jailed iOS without a debugger but disables the Interceptor API). Listen mode accepts `token` and `certificate` for authentication and TLS; keep the default loopback address unless you need remote access.
### Common failures
| Symptom | Likely cause |
|---|---|
| `ValueError: ambiguous name` | Several processes share the name; attach by PID |
| `PTRACE_ATTACH failed: 1` | No permission, or `ptrace_scope` restricts non-child attach |
| `task_for_pid ... failure` on macOS | Frida not signed correctly, or a permission prompt you cannot answer over SSH |
| `'Java' is not defined` | Frida 17 bridge not bundled (see above) |
Two scripting pitfalls from the best-practices doc: writing a longer string over an argument can overflow the buffer, so allocate a new one with `Memory.allocUtf8String()` and keep it alive on `this`; and cache `args[n]` in a local when you read it more than once.
### Through REA
REA exposes Frida device and process listing, attach or spawn sessions, script loading and resume as MCP tools and the one-shot `instrument-with-frida` CLI. It does not install frida-server, starts spawned processes paused so scripts load first, caps captured messages at 16 MiB, and records the script's path and SHA-256 rather than its source. See [[entities/rea]].
## Related Entities
- [[syntheses/dynamic-analysis-workflow]]: Frida versus a debugger, tracing or record/replay.
- [[entities/gdb-and-pwndbg]]: breakpoint-level inspection when you need to stop the process.
- [[syntheses/android-apk-analysis]]: where Frida fits in an APK investigation.
- [[entities/rea]]: agent-driven Frida sessions with evidence capture.
- [[syntheses/troubleshooting]]: more attach and spawn failures.
## Sources
- `raw/github_doc-i18n-en-docs-index-md.md`
- `raw/github_doc-i18n-en-docs-modes-md.md`
- `raw/github_doc-i18n-en-docs-installation-md.md`
- `raw/github_doc-i18n-en-docs-quickstart-md.md`
- `raw/github_doc-i18n-en-docs-bridges-md.md`
- `raw/github_doc-i18n-en-docs-frida-cli-md.md`
- `raw/github_doc-i18n-en-docs-frida-ps-md.md`
- `raw/github_doc-i18n-en-docs-frida-ls-devices-md.md`
- `raw/github_doc-i18n-en-docs-frida-kill-md.md`
- `raw/github_doc-i18n-en-docs-frida-trace-md.md`
- `raw/github_doc-i18n-en-docs-gadget-md.md`
- `raw/github_doc-i18n-en-docs-android-md.md`
- `raw/github_doc-i18n-en-docs-ios-md.md`
- `raw/github_doc-i18n-en-docs-best-practices-md.md`
- `raw/github_doc-i18n-en-docs-troubleshooting-md.md`
- `raw/github_issue-java-is-not-defiend.md`
- `raw/github_release-frida-17-24-0.md`
- `raw/github_doc-docs-frida-instrumentation-md.md`
---
title: "GDB and pwndbg"
type: entity
tags: [gdb, pwndbg, debugger, gdbserver, remote-debugging, breakpoints, watchpoints, memory-inspection, heap-inspection, dynamic-analysis, linux]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
pwndbg_version: "2026.09.15"
sources: ["raw/web_community-starting-debugging-with-gdb.md", "raw/web_community-attach-debugging-with-gdb.md", "raw/web_community-breakpoints-debugging-with-gdb.md", "raw/web_community-set-watchpoints-debugging-with-gdb.md", "raw/web_community-continuing-and-stepping-debugging-with-gdb.md", "raw/web_community-memory-debugging-with-gdb.md", "raw/web_community-registers-debugging-with-gdb.md", "raw/web_community-backtrace-debugging-with-gdb.md", "raw/web_community-signals-debugging-with-gdb.md", "raw/web_community-core-file-generation-debugging-with-gdb.md", "raw/web_community-server-debugging-with-gdb.md", "raw/web_community-connecting-debugging-with-gdb.md", "raw/github_doc-readme-md-4.md", "raw/github_doc-docs-setup-md.md", "raw/github_doc-docs-features-md.md", "raw/github_doc-docs-commands-context-context-md.md", "raw/github_doc-docs-commands-memory-vmmap-md.md", "raw/github_doc-docs-commands-memory-telescope-md.md", "raw/github_doc-docs-commands-memory-search-md.md", "raw/github_doc-docs-commands-memory-xinfo-md.md", "raw/github_doc-docs-commands-memory-hexdump-md.md", "raw/github_doc-docs-commands-glibc-heap-heap-md.md", "raw/github_doc-docs-commands-glibc-heap-bins-md.md", "raw/github_doc-docs-commands-glibc-heap-arena-md.md", "raw/github_doc-docs-commands-glibc-heap-vis-heap-chunks-md.md", "raw/github_doc-docs-commands-start-attachp-md.md", "raw/github_doc-docs-commands-start-entry-md.md", "raw/github_doc-docs-reverse-engineering-tools-md.md"]
---
# GDB and pwndbg
## Overview
**GDB** is the GNU debugger: you run or attach to a process, stop it at chosen points, and look at its memory, registers and call stack to understand what it is actually doing. It debugs native programs locally and can also drive a program on another machine through **gdbserver**.
**pwndbg** is a Python plug-in for GDB (and, experimentally, LLDB) that replaces hand-typed inspection such as `x/30gx $rsp` with richer views: a context panel printed at every stop, an annotated memory map, recursive pointer dereferencing, memory search and allocator-state views. The packaged release referenced in its setup docs is **pwndbg 2026.09.15**.
Use this page to understand program behaviour at runtime. To decide whether a debugger is the right tool at all (versus Frida instrumentation, tracing or record/replay), see [[syntheses/dynamic-analysis-workflow]].
## Characteristics
| Aspect | GDB | pwndbg |
|---|---|---|
| Role | Core debugger: run, attach, stop, step, inspect, core files, remote targets | Inspection layer on top of GDB/LLDB |
| Requirements | Native OS support for processes (attach does not work on bare-board targets without an OS) | Python 3.10+ and GDB 12.1+ for `pwndbg-gdb`; Python 3.12+ and LLDB 19+ for the experimental `pwndbg-lldb` |
| Remote | `target remote` / `target extended-remote` over serial, Unix socket, TCP, UDP or a pipe | Works over the same remote connection; its docs single out Android phones over remote GDB |
| Heap views | None built in beyond raw memory | glibc malloc, jemalloc, and the Linux buddy and SLUB allocators |
| Other targets | — | qemu-user (QEMU 8.1+, vFile API needed for `vmmap`), qemu-system (QEMU 6.2+), Mach-O on macOS via LLDB only |
When REA drives GDB for an agent, its persistent GDB sessions and the one-shot `rea debug-with-gdb` command run only on Linux; macOS lacks its descriptor verification and Windows is unsupported (see [[entities/rea]]).
## How to Use
### Start or attach
| Goal | Command | Notes |
|---|---|---|
| Start under the debugger | `run` (`r`), with program arguments after it | Arguments go through `/bin/sh` (or `$SHELL`) unless you `set startup-with-shell off` |
| Stop at `main` | `start` | A temporary breakpoint on the main procedure, then `run`. C++ static constructors may run first |
| Stop at the very first instruction | `starti` | Use when you need the startup/elaboration phase |
| Stop at the ELF entry point (pwndbg) | `entry` | Not always the first executed instruction; use `starti` for that |
| Inject an environment variable for one run | `set exec-wrapper env 'LD_PRELOAD=libtest.so'` then `run` | Local debugging only; not DJGPP, Cygwin or Windows |
| Attach to a running process | `attach ` | You need permission to signal the process. GDB stops it first; `continue` to let it run |
| Attach by name (pwndbg) | `attachp ` | Looks up the PID via `pidof`, then falls back to partial matches from `ps -eo pid,args`; GDB only |
| Release it | `detach` | The process keeps running independently |
| Snapshot a live process | `gcore [file]` | Default name `core.`; implemented on GNU/Linux, FreeBSD, Solaris and S390 |
If startup itself crashes with `During startup program terminated with signal SIGSEGV`, the shell or exec-wrapper crashed, not your program; check your shell's non-interactive init file or turn off `startup-with-shell`. Do not pipe the debuggee's output to another program; GDB may end up debugging the wrong process.
When attaching, `set exec-file-mismatch ask|warn|off` controls what happens if the loaded file differs from the running one (compared by build ID where available); the default is `ask`.
### Stop where it matters
- **Breakpoints** (`break`) stop at a line number, function name or exact address. Each gets a number; enable, disable and delete take single numbers or ranges such as `5-7`.
- **Watchpoints** stop when data changes, so you don't have to guess where the write happens. `watch foo` breaks on writes that change the value; `rwatch` breaks on reads; `awatch` on reads or writes. To watch a raw address, dereference it: `watch 0x600850` is refused as a constant, `watch *(int *) 0x600850` works. `-l`/`-location` watches the memory an expression resolves to rather than respecting variable scope.
- **Catchpoints** stop on events such as C++ exceptions or library loads; `catch signal` stops on signals.
Hardware watchpoints are fast and report the exact instruction that changed the value. If GDB has to fall back to software watchpoints, it single-steps the program (hundreds of times slower) and reports the change at the next statement. `rwatch` and `awatch` only work in hardware. Too many hardware watchpoints surface only on resume as `Could not insert watchpoint`; delete or disable some. Wide values (for example an 8-byte double on a target that watches at most 4 bytes) may not fit a hardware watchpoint.
Signals: by default GDB passes benign signals such as `SIGALRM` through and stops on error signals. `info signals` shows the table; `handle nostop noprint` changes it (keywords `stop`, `nostop`, `print`, `noprint`, among others). `SIGINT` and `SIGTRAP` belong to GDB and are excluded from `handle all`.
### Step through code
| Command | Moves by | Calls |
|---|---|---|
| `step` / `s` | Source line | Enters functions that have line info |
| `next` / `n` | Source line | Runs calls without stopping |
| `stepi` / `si` | One machine instruction | Enters calls |
| `nexti` / `ni` | One machine instruction | Runs over calls |
| `finish` / `fin` | Until the selected frame returns | Prints the return value |
| `until` / `u` | Past the current line, in this frame | Useful to leave a loop |
| `advance ` | To a location, which may be in another frame | — |
| `continue` / `c` | Until the next stop | `continue N` ignores the current breakpoint N more times |
`step` silently runs through functions compiled without debug info; on stripped code use `stepi`, or `set step-mode on` so `step` stops at the first instruction of such functions. When single-stepping instructions, `display/i $pc` shows the next instruction at every stop.
### Inspect memory, registers and the stack
- **Memory**: `x/nfu addr`. `n` is a count (negative reads backwards), `f` a format (`x`, `d`, `u`, `o`, `t`, `a`, `c`, `f`, `s`, `i`), `u` a unit (`b`, `h`, `w`, `g`). Examples from the manual: `x/4xw $sp` shows four words above the stack pointer in hex; `x/5i $pc-6` disassembles around the current instruction, marked with `=>`. After an `x`, `$_` holds the last address and `$__` its contents. `compare-sections -r` checks the loaded read-only sections against the file, to see whether the program has corrupted its own read-only sections.
- **Registers**: `info registers` (general registers in the selected frame), `info all-registers` (adds floating-point and vector). `$pc`, `$sp`, `$fp` and `$ps` work as portable names, so `p/x $pc` and `x/i $pc` work on most machines. Register values are relative to the selected frame; select `frame 0` to see true hardware contents. Caller-saved registers in outer frames may print ``.
- **Backtraces**: `bt` (alias `where`), `bt 3` for the innermost three frames, `bt -full` to add locals, and `thread apply all backtrace` for every thread, which is handy on a multi-threaded core dump. Optimised builds show `` arguments. `set backtrace past-main on` continues into startup code.
### Debug remotely with gdbserver
Run the program on the target under `gdbserver`, keep an unstripped copy with symbols on the host, and connect:
```
target> gdbserver host:2345 ./prog args
host$ gdb ./prog
(gdb) set sysroot /path/to/target/libs
(gdb) target remote target-ip:2345
```
- Attach on the target instead with `gdbserver --attach :2345 `, or by name with `` gdbserver --attach :2345 `pidof prog` ``.
- Over SSH without opening a port: `(gdb) target remote | ssh -T hostname gdbserver - hello`.
- `target remote` mode does not support `run` or `attach`, and gdbserver exits when the program exits. `target extended-remote` (optionally with `gdbserver --multi`) stays connected so you can rerun or attach; end it with `monitor exit`.
- `--once` stops listening after the first connection; `--debug` and `monitor set debug on` add diagnostics.
- Don't use `load` in `target remote` mode; the program is already on the target.
**gdbserver has no built-in security.** Anyone who connects gets the privileges of the user running it, so never expose it on a public network.
### Install pwndbg
The recommended path is the install script, which provides a `pwndbg` binary you use like `gdb`:
```
curl --proto '=https' --tlsv1.2 -LsSf 'https://install.pwndbg.re' | sh -s -- -t pwndbg-gdb # system-wide
curl --proto '=https' --tlsv1.2 -LsSf 'https://install.pwndbg.re' | sh -s -- -t pwndbg-gdb -u # current user
```
Other routes in the setup docs: `uv tool install git+https://github.com/pwndbg/pwndbg[gdb,lldb]`, Homebrew (`brew install pwndbg/tap/pwndbg-gdb`), Nix, distro package files named for 2026.09.15 (for example `apt install ./pwndbg_2026.09.15_amd64.deb`), or the classic setup that appends `source $(uv tool dir)/pwndbg/share/pwndbg/gdbinit.py` to `~/.gdbinit` so your system `gdb` loads it. Distro repository packages are not officially supported for bug reports.
### pwndbg inspection views
| View | Command | What it tells you |
|---|---|---|
| Context | `context` (`ctx`), printed automatically at each stop | Registers, stack, backtrace, disassembly with resolved operands and call arguments, all pointers dereferenced and colour-coded by memory type. Pick sections with e.g. `context regs stack`; `contextprev`/`contextnext` page through history; `contextoutput` sends sections to other terminals; `layout pwndbg` uses GDB TUI windows |
| Memory map | `vmmap` (aliases `lm`, `libs`) | Mappings with permissions; filter by address or module name, `-w` writable, `-x` executable, `--gaps` for unmapped holes |
| Pointer chains | `telescope [address] [count]` | Recursive dereference from `$sp` by default, 8 lines; `-f` shows the frame from rsp to rbp |
| Offsets | `xinfo [address]` | Where an address sits relative to useful locations (default `$pc`) |
| Hex dump | `hexdump [address] [count]` | 64 bytes from `$sp` by default; accepts a module name |
| Search | `search [mapping]` | Bytes, strings, integers (`-1`/`-2`/`-4`/`-8`), pointers (`-p`) or `--asm`; restrict with `-e`/`-w`; `--save` then `-n` narrows to earlier hits |
| Heap walk | `heap` | Chunks of the current thread's heap; `-c N` limits count, `-v` prints every field |
| Allocator bins | `bins`, `arena` | Contents of the arena's bins and the thread's tcache |
| Heap picture | `vis-heap-chunks` (`vis`) | Visual layout of the next 10 chunks by default |
| Process state | `procinfo` | UID/GID, groups, SELinux context, open file descriptors |
Heap views are for understanding allocator state, for example confirming which chunk a pointer belongs to or why memory use grows. pwndbg also integrates with decompilers (IDA, Binary Ninja, Ghidra, angr-management).
## Related Entities
- [[syntheses/dynamic-analysis-workflow]]: when to use a debugger versus instrumentation, tracing or record/replay.
- [[entities/frida]]: instrumentation when breakpoints are too intrusive.
- [[entities/rizin-and-cutter]]: an alternative debugger plus static views.
- [[entities/ghidra]]: static context to set breakpoints from.
- [[entities/rea]]: agent-driven GDB sessions (Linux only).
- [[syntheses/triage-an-unknown-binary]]: what to do before you attach a debugger.
## Sources
- `raw/web_community-starting-debugging-with-gdb.md`
- `raw/web_community-attach-debugging-with-gdb.md`
- `raw/web_community-breakpoints-debugging-with-gdb.md`
- `raw/web_community-set-watchpoints-debugging-with-gdb.md`
- `raw/web_community-continuing-and-stepping-debugging-with-gdb.md`
- `raw/web_community-memory-debugging-with-gdb.md`
- `raw/web_community-registers-debugging-with-gdb.md`
- `raw/web_community-backtrace-debugging-with-gdb.md`
- `raw/web_community-signals-debugging-with-gdb.md`
- `raw/web_community-core-file-generation-debugging-with-gdb.md`
- `raw/web_community-server-debugging-with-gdb.md`
- `raw/web_community-connecting-debugging-with-gdb.md`
- `raw/github_doc-readme-md-4.md`
- `raw/github_doc-docs-setup-md.md`
- `raw/github_doc-docs-features-md.md`
- `raw/github_doc-docs-commands-context-context-md.md`
- `raw/github_doc-docs-commands-memory-vmmap-md.md`
- `raw/github_doc-docs-commands-memory-telescope-md.md`
- `raw/github_doc-docs-commands-memory-search-md.md`
- `raw/github_doc-docs-commands-memory-xinfo-md.md`
- `raw/github_doc-docs-commands-memory-hexdump-md.md`
- `raw/github_doc-docs-commands-glibc-heap-heap-md.md`
- `raw/github_doc-docs-commands-glibc-heap-bins-md.md`
- `raw/github_doc-docs-commands-glibc-heap-arena-md.md`
- `raw/github_doc-docs-commands-glibc-heap-vis-heap-chunks-md.md`
- `raw/github_doc-docs-commands-start-attachp-md.md`
- `raw/github_doc-docs-commands-start-entry-md.md`
- `raw/github_doc-docs-reverse-engineering-tools-md.md`
---
title: "Ghidra"
type: entity
tags: [ghidra, disassembler, decompiler, nsa, sre, static-analysis, headless, pyghidra, version-tracking]
created: 2026-10-16
updated: 2026-10-16
confidence: high
ghidra_version: "12.1.4"
sources: ["raw/github_release-ghidra-12-1-4.md", "raw/github_release-ghidra-12-1.md", "raw/github_tag-ghidra-12-1-4-ghidradocs-gettingstarted-md.md", "raw/github_doc-ghidra-configurations-public-release-src-global-docs-whatsne.md", "raw/github_doc-docs-installation-md.md", "raw/github_doc-third-party-notices-md.md", "raw/github_doc-ghidradocs-ghidraclass-beginner-introduction-to-ghidra-stude.md", "raw/github_doc-ghidra-features-base-src-main-help-help-topics-importerplugi.md", "raw/github_doc-ghidra-features-base-src-main-help-help-topics-autoanalysisp.md", "raw/github_doc-ghidradocs-cheatsheet-html.md", "raw/github_doc-ghidra-features-base-src-main-help-help-topics-search-search.md", "raw/github_doc-ghidra-features-base-src-main-help-help-topics-datatypemanag.md", "raw/github_doc-ghidradocs-ghidraclass-intermediate-versiontracking-html.md", "raw/github_doc-ghidra-runtimescripts-support-analyzeheadlessreadme-md.md", "raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi-3.md", "raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi.md", "raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi-4.md", "raw/github_doc-readme-md-23.md"]
---
# Ghidra
## Overview
Ghidra is NSA's free software reverse-engineering framework. It covers disassembly, assembly, decompilation, debugging, emulation, graphing and scripting across many processors and executable formats, and it runs interactively or headless. It is licensed under Apache-2.0, with government-work qualifications in its NOTICE file. Work happens inside a **project**: you import binaries into it, run auto-analysis, then annotate in the **CodeBrowser** tool, whose Listing and Decompiler views stay in sync.
The current release line is **12.1**. Ghidra 12.1 was published 2026-05-13 and the **12.1.4** patch release 2026-09-21. For how the decompiler works and why its output goes wrong, see [[concepts/decompilation-concepts]].
## Characteristics
| Aspect | Detail |
|---|---|
| Cost / licence | Free, Apache-2.0 |
| Host OS | Windows 10 (build 1809+), Linux, macOS 10.13+. 32-bit OS installs are deprecated |
| Prebuilt natives | Windows x86-64, Windows ARM64 (through x86 emulation), Linux x86-64. macOS (x86-64 and ARM64), Linux ARM64 and FreeBSD (no debugger support) need natives you build yourself |
| Java | A 64-bit JDK 21 (runtime and development kit); free LTS builds come from Adoptium Temurin and Amazon Corretto |
| Python | Python 3.9–3.14, for both the Debugger and PyGhidra (the built-in CPython 3 integration, formerly Pyhidra) |
| Hardware | 4 GB RAM and 1 GB disk minimum; dual monitors "strongly suggested" |
| Import formats | PE, ELF, Mach-O, DYLD Shared Cache, COFF, NE, MZ, PEF, OMF, DEX, Android APK, Java class, Intel/Motorola Hex, DBG/MAP/DEF, dump files, Raw Binary, Ghidra GZF/GDT/GZT, XML and SARIF |
| Teaming | Ghidra Server gives check-in/check-out shared projects. **12.1 clients need Ghidra Server 12.1/12.0.5 or a newer compatible version** (stated in the 12.2 What's New) |
| Agent access | No first-party MCP server is documented. Community GhidraMCP and REA drive it (see [[syntheses/re-with-ai-agents-mcp]]) |
**Next version (12.2, unreleased).** Ghidra's in-development What's New describes 12.2. Its notable changes are a JDK 25 minimum, enforced TLS server authentication for Ghidra Server and BSim PostgreSQL connections, a Server Allow List, C99 built-in integer types (`int8_t`, `uint8_t`, `int16_t`, `uint16_t`, `int64_t`, `uint64_t`, `intptr_t`, `uintptr_t`), an interactive structure/union/enum merger, and beta "timeless debugging" (TENET-format traces captured with an Intel PIN tool). None of that applies to a 12.1.4 install. The requirements and install steps on this page come from the Getting Started document tagged `Ghidra_12.1.4_build`.
## How to Use
### Install and launch
1. Install a 64-bit JDK 21, on `PATH` or through `JAVA_HOME`. If both are set, `JAVA_HOME` wins.
2. Extract the zip. There is no installer. **Don't extract over an existing install.** On macOS, clear Gatekeeper quarantine first: `xattr -d com.apple.quarantine ghidra__.zip`.
3. Launch with `ghidraRun.bat` (Windows) or `ghidraRun` (Linux/macOS). For CPython scripting, run `support/pyghidraRun` instead. If the `pyghidra` module is missing it offers to install it, or install it yourself:
```bash
python3 -m pip install --no-index -f /Ghidra/Features/PyGhidra/pypkg/dist pyghidra
```
4. To build missing natives: `cd /support/gradle/` then `gradle buildNatives` (or `./gradlew buildNatives` with Internet access). This needs Gradle 8.5+ and a C/C++ toolchain (Xcode or its Command Line Tools on macOS; GCC or Clang plus make on Linux/FreeBSD; Visual Studio 2017+ or the C++ Build Tools on Windows).
A path containing `!` (any OS) or `^` (Windows) stops Ghidra from launching. If it exits silently, set `LAUNCH_MODE` to `fg` in the launch script, or run `support/ghidraDebug`, which listens on `127.0.0.1:18001`.
### First analysis of a binary
1. **File → New Project** (`Ctrl+N`). Choose Non-Shared for solo work and keep the project directory on a local disk.
2. **File → Import File** (`I`), or drag the file onto a project folder. Ghidra proposes the detected format and a Language/compiler spec. **Raw Binary** is always offered; if you pick it you must choose the language yourself (see [[syntheses/firmware-analysis]]). Under **Options…**, *Load Libraries From Disk* / *Link Existing Project Libraries* resolve imported DLLs and shared objects into linked programs.
3. Open the program in the CodeBrowser and accept **Analyze**. Auto-analysis starts at the entry points, follows flow, creates functions at call targets and builds cross-references. Depending on options it also demangles names, recognises library functions, recovers switch tables and imports decompiler-derived signatures (*Decompiler Parameter ID*).
4. For large binaries, work in layers. Run a first pass without the speculative analyzers (stack analysis, for example), then run the rest from **Analysis → One Shot**: Decompiler Parameter ID, Decompiler Switch Analysis, Stack. Red X marks and error bookmarks point at bad flow. Bundled scripts help: `FixupNoReturnFunction.java`, `FindPotentialDecompilerProblems.java`, `FindSharedReturnFunctions.java`, `CreateFunctionsFromSelection.java`.
5. Analysis with a selection active is limited to that selection, though analyzers may still change addresses outside it. *Analyze All Open* only analyzes programs whose language and compiler spec match the current one.
### CodeBrowser keys you'll use constantly
| Key | Action | Key | Action |
|---|---|---|---|
| `G` | Go To address or label | `L` | Add/edit label, rename function or variable |
| `D` | Disassemble | `C` | Clear code/data |
| `F` (at the start address) | Create Function, when analysis missed one (on defined data, `F` cycles float types) | `T` | Choose data type |
| `Ctrl+L` | Retype variable (Decompiler) | `[` / `P` / `Shift+[` | Array / pointer / structure |
| `Ctrl+E` | Open Decompiler window | `S` | Search Memory |
| `Ctrl+Shift+E` | Search Program Text | `Ctrl+Shift+G` | Patch instruction |
| `Alt+←` / `Alt+→` | Back / forward | `F1` / `F4` | Help / set key binding on hovered action |
On macOS, `Ctrl` is `⌘`. Undo history is cleared when you save.
### Types, search and diffing
- **Data types.** The Data Type Manager holds the built-in archive, the program's own types, and archives you open. *File archives* (`.gdt`) can live anywhere on disk; only one user can have one open for writing at a time. *Project archives* are versioned with the project. **File → Parse C Source** imports C headers. Ghidra's decompiler docs call variable names and data types its most important annotations, so accurate types pay off most.
- **Memory search** (`S`) accepts hex, binary, string and decimal input. Hex and binary support nibble/bit wildcards, and strings support regular expressions. Results can be combined with set operations and re-scanned for changes, which is useful while debugging.
- **Version Tracking** carries your markup from an analysed *source* program to a new *destination* version. Run validators first; poor memory-map similarity, missing functions or unmarked no-return calls make matches incomplete. Then run correlators (exact/similar data and symbol matches, exact function matches, reference correlators) and accept or apply matches from the table.
### Headless and scripting
Batch import and analysis uses `support/analyzeHeadless`:
```bash
analyzeHeadless /Users/user/ghidra/projects Project1 -import /binaries/binary1.exe
analyzeHeadless /Users/user/ghidra/projects Project1 -import /Users/user/sourceFiles/*.exe -noanalysis
```
`-preScript`/`-postScript` run scripts around analysis, and `-process` re-runs over files already in the project. The full flag reference, PyGhidra, the Debugger and BSim are covered in the Pro reference on Ghidra scripting, headless use and the debugger. The Ghidra Decompiler timeout option applies to headless runs but not to the interactive window.
### Extensions and upgrades
- **File → Install Extensions**, then **+** to add a zip. Restart to load it. Extensions installed this way go to `/Extensions`. Admins can unzip into `/Ghidra/Extensions` instead.
- Before upgrading, **back up each project's `.rep` directory and `.gpr` file**. Opening an older program prompts an upgrade; saving over the original makes it unreadable by older Ghidra, so use **Save As** if others still run the old version.
## Related Entities
- [[entities/ida-pro]] and [[entities/rizin-and-cutter]]: the main alternatives. [[syntheses/choosing-a-disassembler]] compares all of them.
- [[concepts/decompilation-concepts]]: p-code, annotations, and why output goes wrong.
- [[syntheses/triage-an-unknown-binary]]: what to check before importing.
- [[syntheses/malware-triage]]: capa can use Ghidra as its analysis backend and shows results inside Ghidra.
- [[entities/rea]] and [[syntheses/re-with-ai-agents-mcp]]: agent access to a Ghidra 12.1.x install.
- [[concepts/dotnet-and-nativeaot]]: Ghidra plus ghidra-nativeaot for NativeAOT output.
## Sources
- `raw/github_release-ghidra-12-1-4.md`
- `raw/github_release-ghidra-12-1.md`
- `raw/github_tag-ghidra-12-1-4-ghidradocs-gettingstarted-md.md`
- `raw/github_doc-ghidra-configurations-public-release-src-global-docs-whatsne.md`
- `raw/github_doc-docs-installation-md.md`
- `raw/github_doc-third-party-notices-md.md`
- `raw/github_doc-ghidradocs-ghidraclass-beginner-introduction-to-ghidra-stude.md`
- `raw/github_doc-ghidra-features-base-src-main-help-help-topics-importerplugi.md`
- `raw/github_doc-ghidra-features-base-src-main-help-help-topics-autoanalysisp.md`
- `raw/github_doc-ghidradocs-cheatsheet-html.md`
- `raw/github_doc-ghidra-features-base-src-main-help-help-topics-search-search.md`
- `raw/github_doc-ghidra-features-base-src-main-help-help-topics-datatypemanag.md`
- `raw/github_doc-ghidradocs-ghidraclass-intermediate-versiontracking-html.md`
- `raw/github_doc-ghidra-runtimescripts-support-analyzeheadlessreadme-md.md`
- `raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi-3.md`
- `raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi.md`
- `raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi-4.md`
- `raw/github_doc-readme-md-23.md`
---
title: "IDA Pro"
type: entity
tags: [ida-pro, hex-rays, disassembler, decompiler, flirt, lumina, idapython, idalib, mcp, debugger]
created: 2026-10-16
updated: 2026-10-16
confidence: high
ida_version: "9.4sp1"
sources: ["raw/web_community-9-4sp1-md.md", "raw/web_community-9-4-md.md", "raw/web_community-whats-new-md.md", "raw/web_community-basic-usage-md.md", "raw/web_community-get-started-md.md", "raw/web_community-microcode-view-md.md", "raw/web_community-ctree-view-md.md", "raw/web_community-failures-md.md", "raw/web_community-running-in-batch-mode-md.md", "raw/web_community-overview-md-2.md", "raw/web_community-makesig-md.md", "raw/web_community-overview-md-5.md", "raw/web_community-overview-md.md", "raw/web_community-overview-md-3.md", "raw/web_community-getting-started-md.md", "raw/web_community-idapython-environment-md.md", "raw/web_community-overview-md-4.md", "raw/web_community-getting-started-md-2.md", "raw/web_community-where-mcp-fits-md.md", "raw/web_community-agentic-ida-with-idalib-md.md", "raw/github_doc-readme-md-24.md", "raw/github_doc-readme-md-18.md", "raw/github_doc-docs-ida-provider-md.md"]
---
# IDA Pro
## Overview
IDA is Hex-Rays' commercial interactive disassembler. The Hex-Rays decompiler adds C-like pseudocode on top, and an integrated debugger, IDAPython scripting, headless use as a library (**idalib**) and an official **MCP server** for AI agents round it out. You load a file, IDA auto-analyses it into a database, and you refine that database by renaming, retyping and commenting, with the disassembly, pseudocode and hex views kept in sync.
Current release: **IDA 9.4sp1** (build `9.4.260915`), a service pack on IDA 9.4 (build `9.4.260714`). For how its decompiler lifts code (microcode → ctree) and why output fails, see [[concepts/decompilation-concepts]].
## Characteristics
| Aspect | Detail |
|---|---|
| Editions named in the docs | IDA Home, IDA Pro (Essential / Expert / Ultimate), IDA Classroom. idalib and the official MCP server ship with all three. Embedding idalib in your own product, SaaS or a shared server needs an IDA Pro OEM licence |
| Decompilers | Which architectures you can decompile, and whether decompilation is local or remote, depends on product and plan |
| Database | Analysis lives in an IDB with the `.i64` extension. Once loaded, IDA no longer needs the original binary, and edits never touch it |
| Debugger | Local backends: Win32 and WinDbg (kernel mode and Time Travel Debugging) on Windows, Linux, and Intel/ARM macOS. Remote debug servers, Android native (ARM32, AArch64, x86, x86_64), a Dalvik debugger, remote iOS through Apple's `debugserver` (including iOS 17 CoreDevice), and a trace Replayer that can step backwards |
| Library recognition | FLIRT signatures. **IDA Feeds** (Pro only) bulk-applies the FLIRT Signature Bundle and can generate Rust signatures on demand. **Lumina** shares function metadata |
| Scripting | IDC, IDAPython, the higher-level Domain API (v0.5.0 released with 9.4), and the C++ SDK |
| Agent access | Official Hex-Rays IDA MCP Server (9.4+), plus community `ida-pro-mcp` |
**What 9.4 added**, in brief:
- A rebuilt Dyld Shared Cache workflow: a DSC Index subview with on-demand loading, scriptable through `ida_dscu`.
- Swift ABI support (`__swiftself`, `__swiftthrows`, `__swiftasync`) and better Rust and Go recovery.
- ARM SVE2/SME disassembly, plus new Qualcomm Hexagon and MCore processor modules.
- A **Pathfinder** call-path widget (`Shift-F9`).
- Jump Anywhere as the default `g` dialog, deep links into databases, Git-backed Teams, a unified Scripts window, and functions grouped by compilation unit.
- A native Windows-on-ARM64 build.
**What 9.4sp1 fixed**, among other things:
- Faster decompilation of functions with many basic blocks.
- User microcode changes not being applied in batch mode.
- An idalib crash on exit when `close_database()` wasn't called.
- An IDAPython link-time dependency that ignored the runtime chosen by `idapyswitch`.
- Several decompiler INTERRs.
## How to Use
### Load and navigate
1. **Quick start → New**, pick the file, and accept the suggested loader and processor. Auto-analysis runs, and the status-bar `AU` indicator reads `idle` when it has finished.
2. The main views are IDA View (graph, linear, and proximity view), Pseudocode, Hex View, Local Types and Functions. **Options → Show command palette…** lists every action with its hotkey.
| Key | Action | Key | Action |
|---|---|---|---|
| `Space` | Toggle graph / linear view | `F5` | Decompile to pseudocode |
| `Tab` | Jump between disassembly and pseudocode | `G` | Jump Anywhere (names, addresses, expressions) |
| `X` | Cross-references to the item | `N` | Rename |
| `:` / `;` | Regular / repeatable comment | `Esc` / `Ctrl+Enter` | Back / forward |
| `Y` | Set type (function, variable, data) | `Shift-F1` | Local Types |
| `Ctrl+Shift+F8` | Microcode view | `Ctrl+F6` | CTree view (new; may change) |
| `F2` / `F9` | Add breakpoint / start debugging | `Shift-F9` | Pathfinder (9.4) |
### Improve the pseudocode
The Hex-Rays primer comes down to one habit: **give the decompiler types.** Define the structure in Local Types (`Shift-F1`), put the cursor on the variable in pseudocode, press `Y`, and the casts disappear. When decompilation fails, work through these in order:
1. Function boundaries.
2. Stack-pointer values: show the column with Options, General, Stack pointer, and fix values with `Alt-K`.
3. Stack variables.
4. The function's own type.
5. The types of every callee.
Use a database created by the latest IDA. The full list of failure messages is in [[syntheses/troubleshooting]]. The **Microcode view** lets you step through maturity levels, delete an instruction, or use *Specify value…* to pin an operand. Those edits carry through to the final pseudocode.
### Batch decompilation
Pass `-Ohexrays` to the text or GUI binary. The format is `-Ohexrays:-option1:-option2...:outfile:func1:func2`, and `ALL` means every non-library, non-trivial function:
```
idat -Ohexrays:-errs:-mail=john@mail.com:outfile:ALL -A input
```
`-A` suppresses the startup dialogs. Useful options include `-nosave` (don't save the IDB), `-new` (only if the output doesn't already exist), `-libfuncs`, `-lumina`, and `-Dname=value` for a config directive. Leave out `-errs` unless you mean to upload problem databases to Hex-Rays. Prefix the output name with `+` to append to it.
### Library and metadata recognition
- **FLIRT** recognises standard library functions. To carry your own work forward between versions of a binary, use **Makesig**: in the old database, **File → Produce file → Create SIG file…** (untick *Valid, user-specified names only* to include `sub_` functions). Then in the new database, **File → Load file → FLIRT signature file…**.
- **Lumina** pushes and pulls function metadata keyed by an `md5` hash of each function: names, prototypes, comments, frames and operand representations. The binary itself is never uploaded, only the input filename, IDB name and the file's `md5`. Bulk pushes skip functions that still have dummy names or are under 32 bytes. Public Lumina is community-shared; Private Lumina is a self-hosted add-on. It is available for x86/x64, ARM, PPC, MIPS and RISC-V only. **Treat a push as disclosure** when the sample or your notes are sensitive.
### Debugging
**Debugger → Select debugger…**, set **Process options** (path, host, port, password), add breakpoints with `F2`, run with `F9`. For a remote target, start the platform's debug server there and point Process options at it. To start a session straight from the command line without first creating a database, use `ida -rdebname{params}:pass@hostname:port+pid`. Only debug code you are allowed to run, and only inside an isolated VM (see [[syntheses/dynamic-analysis-workflow]]).
### Headless with idalib
idalib runs IDA's engine without the GUI from Python or C++. From 9.4 the installer can activate it for you (*Use this installation as the default for idalib*). Otherwise install it with HCLI:
```bash
hcli login
hcli ida install --download-id ida-pro:latest --set-default --create-python-environment --accept-eula --yes
uv pip install idapro
```
You can also activate it by hand by running the install's `idalib/python/py-activate-idalib.py` once. In scripts, **`import idapro` must come before any other IDA module.** For CI and Docker, use an API key (`hcli auth key create --name my-key`, exported as `HCLI_API_KEY`) instead of `hcli login`.
IDAPython inside the GUI should use a virtual environment at `$IDAUSR/venv` whose Python major.minor matches the `libpython` that `idapyswitch` registered. Point `IDAPYTHON_VENV_EXECUTABLE` at it, or run `hcli ida python create-environment`.
### Agents and MCP
- **Hex-Rays IDA MCP Server** (first-party, open source, runs locally, bring your own model). It needs IDA 9.4+ with idalib, plus Git, `uv` and Python 3.11+. Install with `uvx ida-hcli mcp install`, which also installs the GUI plugin. It can attach to databases open in the GUI or start idalib workers headlessly, and it coordinates several databases and agents. Hex-Rays recommends disabling other IDA MCP servers first.
- **Community `ida-pro-mcp`** needs IDA Pro 8.3 or higher (9 recommended). **IDA Free is not supported.** Its headless mode is `uv run idalib-mcp --host 127.0.0.1 --port 8745 path/to/executable`.
- **Code mode:** let the agent write and run idalib scripts directly.
See [[syntheses/re-with-ai-agents-mcp]] for how these compare with REA and the JEB and Ghidra bridges.
## Related Entities
- [[entities/ghidra]] (the free alternative) and [[entities/rizin-and-cutter]]. [[syntheses/choosing-a-disassembler]] covers cost, formats and scripting side by side.
- [[concepts/decompilation-concepts]]: microcode, ctree, and fixing bad output.
- [[syntheses/malware-triage]]: the capa explorer plugin runs inside IDA Pro.
- [[entities/rea]]: drives IDA through an existing IDA MCP server.
## Sources
- `raw/web_community-9-4sp1-md.md`
- `raw/web_community-9-4-md.md`
- `raw/web_community-whats-new-md.md`
- `raw/web_community-basic-usage-md.md`
- `raw/web_community-get-started-md.md`
- `raw/web_community-microcode-view-md.md`
- `raw/web_community-ctree-view-md.md`
- `raw/web_community-failures-md.md`
- `raw/web_community-running-in-batch-mode-md.md`
- `raw/web_community-overview-md-2.md`
- `raw/web_community-makesig-md.md`
- `raw/web_community-overview-md-5.md`
- `raw/web_community-overview-md.md`
- `raw/web_community-overview-md-3.md`
- `raw/web_community-getting-started-md.md`
- `raw/web_community-idapython-environment-md.md`
- `raw/web_community-overview-md-4.md`
- `raw/web_community-getting-started-md-2.md`
- `raw/web_community-where-mcp-fits-md.md`
- `raw/web_community-agentic-ida-with-idalib-md.md`
- `raw/github_doc-readme-md-24.md`
- `raw/github_doc-readme-md-18.md`
- `raw/github_doc-docs-ida-provider-md.md`
---
title: "REA (rea-agents): One MCP for Reverse Engineering"
type: entity
tags: [rea, mcp, agents, evidence, providers, ghidra, ida, frida, cli]
created: 2026-10-16
updated: 2026-10-16
confidence: high
rea_version: "6.5.0"
sources: ["raw/github_doc-readme-md.md", "raw/github_doc-docs-installation-md.md", "raw/github_doc-docs-client-configuration-md.md", "raw/github_doc-docs-mcp-contracts-md.md", "raw/github_doc-docs-reverse-engineering-tools-md.md", "raw/github_doc-agents-skills-reverse-engineer-anything-references-evidence-.md", "raw/github_release-rea-agents-v6-5-0.md", "raw/github_doc-third-party-readme-md.md", "raw/github_doc-disclaimer-md.md", "raw/github_doc-docs-kuna-provider-md.md", "raw/github_doc-docs-ida-provider-md.md", "raw/github_doc-docs-mcp-prompts-md.md", "raw/github_doc-agents-skills-reverse-engineer-anything-skill-md.md", "raw/github_doc-docs-migration-6-2-md.md", "raw/github_doc-docs-cli-md.md", "raw/github_doc-docs-firmware-analysis-md.md"]
---
# REA (rea-agents): One MCP for Reverse Engineering
## Overview
REA ("Reverse Engineer Anything") is a single MCP server, published as the npm package `rea-agents` (6.5.0, 2026-10-13), that connects an AI agent to reverse-engineering tools across native binaries, applications and runtime behaviour. The same workflows are available from the terminal through the `rea` CLI. Analysis runs locally, and every result carries the evidence and limitations behind its conclusions.
> **Get REA only from official sources.** The README warns: install from [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), or the [`rea-agents` npm package](https://www.npmjs.com/package/rea-agents). A similar repository name or a copied README does not make a download official. In the README's words, REA is "for lawful research, education, interoperability, software preservation, and analysis of software you own or are authorized to examine"; see [[concepts/legal-and-ethics]].
REA is MIT-licensed, but it is a *bridge*: it does not install Ghidra, IDA, JEB or the other caller-supplied engines (setup can install Hopper, only with your approval) and grants no license to the commercial ones. You must legally obtain and be authorized to use IDA Pro/Hex-Rays, Hopper and JEB under their own terms.
## Characteristics
### Setup
```bash
npx rea-agents setup
```
Setup offers the supported agents in a multi-select, previews the exact paths and changes, and applies them only after approval, keeping backups (`.rea.backup`, and a verified `.rea.backup..recovery` when the current config differs). It registers REA's MCP server plus matching workflow instructions (omit with `--skill=false`). Restart the agents it names afterward. For the latest release use `npx rea-agents@latest setup`. npm's download approval and REA's setup approval are separate.
Scripted forms: `rea setup --client codex --client cursor --dry-run`, `rea setup --yes --all-detected --install-hopper --json`. Without a scope flag, `--yes` only touches existing REA-owned registrations. `rea doctor --json` is a read-only readiness check; `rea update` updates only the npm/Bun global install that owns the running CLI; `rea uninstall [--purge-data]` removes REA-owned registrations and skill files.
**Supported agents** (`--client` value): Claude Code (`claude_code`), Claude Desktop (`claude_desktop`), Codex (`codex`), Cursor (`cursor`), Gemini CLI (`gemini_cli`), Windsurf, Devin, OpenCode, Antigravity, GitHub Copilot CLI (`copilot_cli`), Command Code, Qwen Code (`qwen_code`), VS Code (`vscode`), Grok Build (`grok_build`), OMP, Pi, Hermes, Kimi Code (`kimi`), Qoder, Factory Droid (`droid`), Trae, WorkBuddy, ZCode, DeepSeek Harness (`dsh`). Seven of these — `droid`, `dsh`, `kimi`, `qoder`, `trae`, `workbuddy`, `zcode` — require `rea-agents@6.5.0` or later.
### Runtime requirements
REA needs Node.js 22.x (>=22.19), 24.x (>=24.11) or 26+, plus npm. On Linux, several workflows (Ghidra, JADX, firmware tools, static JavaScript/Electron) require a procps-compatible `ps` on REA's `PATH` for process-ownership checks. Additional tools depend on the target (below). Install Hopper with approval during setup; Ghidra and IDA use your existing installations.
### The engines REA drives
REA is a provider broker. Native binaries route to a deep provider, which must be selected once per session:
| Provider | What it is | Notes |
|---|---|---|
| Ghidra | Existing 12.1.x install + JDK 21+ | 26 read-only ops; `GHIDRA_INSTALL_DIR` |
| Hopper | Commercial; setup can install it (macOS 12+/several Linux) | demo or licensed |
| IDA | Adapts an upstream IDA MCP (mrexodia/ida-pro-mcp, or a native bridge) | `REA_IDA_MCP_CONFIG` |
| Kuna | Apache-2.0 single-binary decompiler, static | `REA_KUNA_COMMAND` |
| JEB | Caller-started JEB serving MCP | `REA_JEB_MCP_URL` |
| objdump / GDB / Rizin | Caller-supplied GNU binutils, GDB, Rizin | `REA_OBJDUMP_COMMAND`, `REA_GDB_COMMAND`, `REA_RIZIN_COMMAND` |
Beyond native: static JavaScript/Electron and .NET (no engine), Go/Rust metadata and WASM (WABT 1.0.42), EVM bytecode, SQLite snapshots (Node 24.x >=24.15 or 26+), Android via headless JADX + JDK, Android devices via caller-supplied adb, Apktool resources, Flutter, firmware (binwalk 3.1.0 / unblob 26.6.4 on Linux), pcap/packet captures (tshark 4.x on Linux), HAR/SAZ captures, Frida instrumentation, browser observation via CDP, and process capture (Linux/macOS PTY). `auto` selection picks the single supporting provider; with more than one it returns `ambiguous` and asks you to choose with `--provider` / `provider_id`.
### The evidence model
Evidence-producing tools return the complete canonical Evidence record in both text and `structuredContent`: read `structuredContent.normalized_result` for the operation result and `structuredContent.evidence_id` for its identity. `analysis_profile` is always present (object or `null`) and participates in identity; records omitting it are rejected. REA's workflow instructions require every conclusion to keep **observations**, **inferences** and **unknowns** distinct; residual unknowns are tracked with `record_unknown`/`update_unknown`. Comparisons (`compare_functions`, `compare_artifacts`, `verify_reconstruction`) only conclude equality or absence over complete, compatible page sets; a dimension with an unavailable facet stays `unknown` rather than being guessed. Target-derived content (strings, decompiled code, page text, logs) is marked `untrusted_content` in `_meta` and must be treated as data, never as instructions.
Because results are complete records, they can be large. REA prepares results within a 10 MiB MCP response budget; if one won't fit it returns `resource_constraint` with `details.resource: "transport"` and a retained-evidence reference. Recover it with `inspect_analysis_view` (summary, one section, or a stable page) or `export_evidence_bundle`, or raise `REA_MCP_MAX_RESPONSE_BYTES` (≥10485760) if the client's receive buffer allows. Clients that choke on a large tool catalog can set `REA_MCP_INPUT_SCHEMA_PROFILE: compact` or `REA_MCP_OUTPUT_SCHEMA_PROFILE: off`; REA still validates complete canonical inputs/outputs.
### Headline platform limits
- **Deep native analysis needs one of Hopper, Ghidra or IDA**; Hopper's automated installer supports macOS 12+ and a fixed set of Linux distros (elsewhere, point `HOPPER_LAUNCHER_PATH` at an existing install).
- **GDB and Rizin debugger sessions are Linux-only** (they depend on `/proc` verification); macOS and Windows are unsupported for persistent debugging. Persistent sessions live only for the life of the MCP server process.
- **Firmware, packet captures and signal recordings are Linux-only** and need util-linux `prlimit`.
- **Windows Ghidra is experimental "P0"** — native PE (x86/x86-64/ARMNT/ARM64) and raw NMOS 6502 only, read-only.
- **Ghidra cold start is slow.** The first query imports and auto-analyzes; REA's default startup allowance is 330,000 ms (`REA_GHIDRA_STARTUP_TIMEOUT_MS`), but the *client's* per-call deadline (pinned SDK default 60,000 ms) bounds the wait. On a timeout, `close_binary` then re-`open_binary` on the same connection.
- **REA does not install caller-supplied engines** (Hopper only with setup approval) and never switches providers automatically; a provider timeout, install failure or permission denial needs its own recovery.
- **Unrestricted consoles run with the user's privileges.** The GDB console and Rizin/Cutter commands execute shell commands, change target state and use the network in REA's process security context (see `## How to Use`).
## How to Use
Point your agent at a local target in natural language, for example: *"Understand how search works in the Notes app, show me the evidence, and build a similar feature for my project."* Or use the CLI:
```bash
# static JavaScript/Electron, no engine needed
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
# install the command for regular use
npm install --global rea-agents
rea --help
# native analysis: pick a provider
rea analyze /absolute/path/to/program --provider ghidra
```
Six guided MCP prompts (`investigate_feature`, `compare_application_versions`, `verify_reconstruction`, `trace_crash`, `audit_residual_unknowns`, `prepare_bounded_process_capture`) suggest starting tools but add no tools themselves.
**On the unrestricted consoles:** `rea debug-with-gdb` / `start_gdb_session` and `rea debug-with-rizin` / `start_rizin_debug_session` give an interactive console that runs with full user privileges and can run shell commands, read/write files, change the inferior and use the network — treat them like a shell running with your user account's privileges on whatever you're debugging, and only against targets you control. Both require Linux. The Rizin debugger and the one-shot `rea debug-with-gdb` CLI require a caller-selected durable `inferior_tty` (e.g. `/dev/pts/7`) that stays available after the debugger exits (it is optional for MCP `start_gdb_session`, but then you must detach or kill a live inferior yourself before closing), because closing a session can detach and resume a live inferior. Running an unrestricted Rizin/Cutter or GDB console, or closing a debugger, retires the active binary-analysis session; call `open_binary` again before continuing. More in [[syntheses/re-with-ai-agents-mcp]].
Keep original saved artifacts until replacements are verified; `always-bump-minor` versioning means a minor bump can still be a breaking release, so refresh cached MCP tool definitions after upgrading.
## Related Entities
- [[entities/ghidra]]: a free native provider (uses your existing 12.1.x install)
- [[entities/ida-pro]]: adapted through an upstream IDA MCP
- [[entities/gdb-and-pwndbg]]: the GDB console REA drives (Linux-only)
- [[entities/frida]]: REA's runtime instrumentation path
- [[syntheses/re-with-ai-agents-mcp]]: REA versus other RE MCP servers
- [[syntheses/firmware-analysis]]: REA's binwalk/unblob and raw-binary lanes
## Sources
- `raw/github_doc-readme-md.md`
- `raw/github_doc-docs-installation-md.md`
- `raw/github_doc-docs-client-configuration-md.md`
- `raw/github_doc-docs-mcp-contracts-md.md`
- `raw/github_doc-docs-reverse-engineering-tools-md.md`
- `raw/github_doc-agents-skills-reverse-engineer-anything-references-evidence-.md`
- `raw/github_release-rea-agents-v6-5-0.md`
- `raw/github_doc-third-party-readme-md.md`
- `raw/github_doc-disclaimer-md.md`
- `raw/github_doc-docs-kuna-provider-md.md`
- `raw/github_doc-docs-ida-provider-md.md`
- `raw/github_doc-docs-mcp-prompts-md.md`
- `raw/github_doc-agents-skills-reverse-engineer-anything-skill-md.md`
- `raw/github_doc-docs-migration-6-2-md.md`
- `raw/github_doc-docs-cli-md.md`
- `raw/github_doc-docs-firmware-analysis-md.md`
---
title: "Rizin and Cutter"
type: entity
tags: [rizin, cutter, rz-bin, rz-pipe, rz-ghidra, disassembler, cli, open-source, static-analysis]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
rizin_version: "0.9.1"
cutter_version: "2.5.0"
sources: ["raw/github_doc-src-introduction-overview-md.md", "raw/github_doc-src-introduction-getting-rizin-md.md", "raw/github_doc-src-first-steps-intro-md.md", "raw/github_doc-src-first-steps-commandline-options-md.md", "raw/github_doc-src-basic-commands-intro-md.md", "raw/github_doc-src-analysis-intro-md.md", "raw/github_doc-src-analysis-code-analysis-md.md", "raw/github_doc-src-disassembling-intro-md.md", "raw/github_doc-src-visual-mode-intro-md.md", "raw/github_doc-src-scripting-intro-md.md", "raw/github_doc-src-scripting-rz-pipe-md.md", "raw/github_doc-src-tools-intro-md.md", "raw/github_doc-src-tools-rz-bin-intro-md.md", "raw/github_doc-docs-source-index-rst.md", "raw/github_doc-docs-source-user-docs-features-rst.md", "raw/github_doc-docs-source-user-docs-menus-menu-bar-windows-menu-rst.md", "raw/github_doc-docs-source-user-docs-command-line-rst.md", "raw/github_doc-docs-source-user-docs-common-issues-rst.md", "raw/github_doc-docs-reverse-engineering-tools-md.md", "raw/github_doc-src-refcard-intro-md.md", "raw/github_release-rizin-v0-9-1.md", "raw/github_release-cutter-v2-5-0.md", "raw/github_doc-src-visual-mode-visual-panels-md.md"]
---
# Rizin and Cutter
## Overview
**Rizin** is a free, open-source reverse-engineering framework built around one terminal program, `rizin`, plus a set of small `rz-*` companion tools. `rizin` is a disassembler, hex editor, patcher, analyser and debugger, and it treats files, processes and devices alike as byte streams you seek through. **Cutter** is the Qt/C++ desktop GUI that runs on top of Rizin. Use Rizin when you want scripting, terminal and pipeline work; use Cutter when you want graphs and a clickable decompiler view.
Current releases are **Rizin 0.9.1** (2026-06-29) and **Cutter 2.5.0** (2026-06-30), and Cutter 2.5.0 is built on Rizin 0.9.1. Rizin 0.9.1 is a small patch release: visual-mode scrolling fixes for meta items, and string search now defaults to NUL as its newline character. Cutter 2.5.0 adds a Preferences tab for reading and setting Rizin evars, a searchable keybindings window, register and memory value tooltips while debugging or emulating, register-profile and RzRun-profile selection before a debug session, debuginfod support in the initial analysis options, optional docking, and a move to Qt 6.11.0.
## Characteristics
| Aspect | Detail |
|---|---|
| Cost / licence | Free and open source |
| Platforms | Binary packages for Windows, macOS and Linux; build from source elsewhere. Cutter ships as a `.dmg` (macOS), a `.zip` (Windows) and an `.AppImage` (Linux) |
| Formats | ELF, PE, Mach-O, Java CLASS and any format a plugin adds |
| Decompiler | `pdg`, the Ghidra decompiler plugin (rz-ghidra). Cutter is designed around decompiler plugins (Ghidra, RetDec, JSDec); RzGhidra is the one the Cutter team maintains |
| Scripting | Command chaining in the shell, plus `rz-pipe` bindings (Python, Rust, Haskell, OCaml, Ruby) over spawn, pipe, HTTP or TCP |
| Scale | Cutter's own docs warn that a few MB counts as large and over 100 MB as very large; default analysis may take minutes or freeze the UI |
### The companion tools
| Tool | Use it to… | Example |
|---|---|---|
| `rz-bin` | Dump headers, imports, exports, sections, strings, libraries and security flags | `rz-bin -I unknown.bin` (info), `-i` imports, `-z` strings, `-S` sections, `-l` libraries, `-j` JSON |
| `rz-asm` | Assemble or disassemble snippets | `rz-asm -a x86 -b 32 'mov eax, 33'` → `b821000000`; `rz-asm -a x86 -d '90'` → `nop` |
| `rz-hash` | Hash files or blocks | `rz-hash -a md5 file.bin` |
| `rz-diff` | Diff bytes, deltas or analysed code blocks | |
| `rz-find` | Search files for byte patterns and strings in many encodings | |
| `rz-ax` | Convert bases and evaluate expressions | `rz-ax 0x400000` → `4194304` |
| `rz-run` | Launch a program with set args, stdin, chdir or sockets (profiles in `.rrz` files) | `rz-run program=/bin/ls connect=localhost:9999` |
| `rz-gg` | Compile a tiny language into small x86, x86-64 or ARM binaries | |
`rz-bin -I` is a fast first look during triage. It reports arch, bits, class, compiler, `stripped`, `canary`, `PIE`, `NX` and `relro` (see [[syntheses/triage-an-unknown-binary]]).
## How to Use
### Open a file
| Command | What it does |
|---|---|
| `rizin -A file` | Open and run `aaa` (analyse all referenced code) |
| `rizin -d /bin/ls` | Debug a program, or a PID |
| `rizin -w file` | Open writable (the default is read-only) |
| `rizin -nw file` | Writable, without parsing format headers |
| `rizin -a ppc -b 32 ls.fat` | Force arch and bits |
| `rizin -B file` | Set the base address for PIE binaries |
| `rizin -qc 'iij' hi.bin > imports.json` | Run one command, print, quit; good for scripts |
| `rizin -p test.rzdb` | Reopen a saved project |
| `rizin -N file` | Skip user settings and scripts (`-NN` also skips plugins) |
| `rizin =` | Scratch 512-byte writable buffer |
Files open in virtual-addressing mode, positioned at the entry point. `-n` switches to raw file offsets.
### The command grammar
Commands are short mnemonics: the first letter is the family and later letters refine it (`p` print, `pd` print disassembly, `pdf` disassemble function). The general shape is:
```
[.][times][cmd][~grep][@[@iter]addr!size][|>pipe] ; ...
```
- `@ addr` runs one command at another address without moving the seek: `p8 10 @ 0x4010`.
- `@@` iterates over a list (flags, functions, hits): `p8 20 @@f:hit0_*`.
- `~` is the built-in grep, `>` writes to a file, `|` pipes to a shell command, `;` chains commands, and backticks substitute output.
- Help is built in: append `?` to any command, `??` for long help with examples, `?**` to search every command's description, `?**e` to search settings. `el~color` lists settings mentioning "color". Persist settings in `~/.rizinrc`.
### A first static pass
```
[0x00001100]> aa # analyse symbols and entry points ("aaa" for stripped binaries)
[0x00001100]> afl # list functions
[0x00001100]> s main # seek
[0x00001100]> pdf # disassemble the current function
[0x00001100]> pdg # decompile it (needs the rz-ghidra plugin)
[0x00001100]> axt 0x2004 # who references this address
[0x00001100]> iz # list strings (izz for more)
```
Deeper passes are `aab`, `aar` and `aac`, or `aaaa` (experimental). Fully automatic analysis can be wrong, so the `af` family lets you define functions by hand, and the `analysis.*` and `emu.*` settings tune it. Write with `w "text"`, `wx 90 90`, or `wa "jmp 0x8048140 ; nop"` once the file is open writable. Patch only copies you are allowed to modify.
### Visual modes
`V` enters visual mode: `hjkl` move, `p`/`P` rotate panels (hex, disassembly, debugger and more), `c` toggles the cursor, `:` runs a command, and `q` returns to the prompt. From the prompt, `v` opens the panels interface (split screens, menus and tabs); from inside visual mode, press `!`. In visual mode the debugger keys default to `F2` breakpoint, `F7` step, `F8` step over and `F9` continue.
### Scripting with rz-pipe
```shell
pip install rzpipe
```
```python
import rzpipe
rz = rzpipe.open("/bin/ls")
rz.cmd('aa')
print(rz.cmdj("aflj")) # JSON output parsed into Python objects
```
Most commands have a `j` variant (`aflj`, `ij`, `iij`) that returns machine-readable output. That is what makes Rizin practical to drive from scripts and agents.
### Cutter
Download a release and run it (on Linux, `chmod +x Cutter*.AppImage` first). Command-line options:
| Option | Meaning |
|---|---|
| `-A, --analysis ` | Analyse on open: `0` none, `1` `aaa`, `2` `aaaa` (experimental) |
| `-a` / `-b` / `-c` / `-o` / `-e` | Arch, bits, CPU, OS, endianness |
| `-F, --format`, `-B, --base`, `-m, --map` | Force a bin plugin, set the load base, set the map address |
| `-w, --writemode` | Open writable (with `-A`, no confirmation prompt) |
| `-P, --phymode` | Turn off virtual addressing |
| `-p, --project`, `-i ` | Load a project, run a script |
| `--no-plugins` (`--no-cutter-plugins`, `--no-rizin-plugins`) | Start clean when a plugin misbehaves |
The Decompiler window (Windows → Decompiler) stays in sync with the disassembly and supports renaming and retyping. **Common problems:**
- If the AppImage crashes at startup, empty `LD_LIBRARY_PATH`.
- Keyboard mix-ups usually mean a wrong Xorg layout; `setxkbmap us` typically fixes it.
- If analysis runs longer than 5–15 minutes, reopen the file and choose narrower options in the Load Options dialog's Advanced Analysis view.
### Driving Rizin from an agent
REA can call a Rizin you install yourself; set `REA_RIZIN_COMMAND`, otherwise it searches `PATH`. One-shot commands run with `-N -q -c`, so user settings and scripts are skipped but plugins stay loaded. **Persistent Rizin debugger sessions through REA are Linux-only**, because REA verifies them through `/proc`. Cutter is reached through REA's local bridge. Rizin commands run with your user's full privileges, so treat an agent's access to the Rizin console as unrestricted shell access (see [[syntheses/re-with-ai-agents-mcp]]).
## Related Entities
- [[entities/ghidra]]: supplies the decompiler behind `pdg` and Cutter's default decompiler.
- [[entities/ida-pro]]: the commercial alternative. [[syntheses/choosing-a-disassembler]] compares all of them.
- [[entities/gdb-and-pwndbg]]: the usual Linux debugger stack alongside Rizin.
- [[entities/rea]]: agent integration for Rizin and Cutter.
- [[syntheses/triage-an-unknown-binary]]: where `rz-bin` fits in a first pass.
## Sources
- `raw/github_doc-src-introduction-overview-md.md`
- `raw/github_doc-src-introduction-getting-rizin-md.md`
- `raw/github_doc-src-first-steps-intro-md.md`
- `raw/github_doc-src-first-steps-commandline-options-md.md`
- `raw/github_doc-src-basic-commands-intro-md.md`
- `raw/github_doc-src-analysis-intro-md.md`
- `raw/github_doc-src-analysis-code-analysis-md.md`
- `raw/github_doc-src-disassembling-intro-md.md`
- `raw/github_doc-src-visual-mode-intro-md.md`
- `raw/github_doc-src-scripting-intro-md.md`
- `raw/github_doc-src-scripting-rz-pipe-md.md`
- `raw/github_doc-src-tools-intro-md.md`
- `raw/github_doc-src-tools-rz-bin-intro-md.md`
- `raw/github_doc-docs-source-index-rst.md`
- `raw/github_doc-docs-source-user-docs-features-rst.md`
- `raw/github_doc-docs-source-user-docs-menus-menu-bar-windows-menu-rst.md`
- `raw/github_doc-docs-source-user-docs-command-line-rst.md`
- `raw/github_doc-docs-source-user-docs-common-issues-rst.md`
- `raw/github_doc-docs-reverse-engineering-tools-md.md`
- `raw/github_doc-src-refcard-intro-md.md`
- `raw/github_release-rizin-v0-9-1.md`
- `raw/github_release-cutter-v2-5-0.md`
- `raw/github_doc-src-visual-mode-visual-panels-md.md`
---
title: "Activity Log"
type: log
---
# Activity Log
Append-only record of all wiki changes.
## Format
Each entry follows this format:
```
### YYYY-MM-DD HH:MM — [Action Type]
- **Source/Trigger**: what initiated the action
- **Pages created**: list of new pages
- **Pages updated**: list of updated pages
- **Notes**: any contradictions flagged, decisions made
```
---
### 2026-10-16 — Initial build
- **Source/Trigger**: new topic wiki on reverse engineering, anchored on REA (`rea-agents` 6.5.0) and each tool's own documentation
- **Pages created**: 24 standard pages (7 concepts, 6 entities, 11 syntheses) and 8 XL reference pages; see [[index]]
- **Pages updated**: none
- **Notes**: Scope is legitimate analysis only (see [[concepts/legal-and-ethics]]). Debugging coverage is limited to debugging and inspection; exploit tooling is out of scope. Ghidra requirements follow the 12.1.4 release documentation; notes from the unreleased 12.2 are labelled as such. Not covered: Windows kernel/driver RE and WinDbg, commercial protector unpacking, and EU decompilation law (the EU directive text could not be retrieved).
---
title: "Android APK Analysis"
type: synthesis
tags: [android, apk, dex, jadx, apktool, adb, logcat, frida, mitmproxy, manifest, apk-signing, emulator]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
jadx_version: "1.5.6"
apktool_version: "3.0.3"
sources: ["raw/web_community-application-fundamentals-app-architecture-android-developers.md", "raw/web_community-app-manifest-overview-app-architecture-android-developers.md", "raw/web_community-app-signing-security-android-open-source-project.md", "raw/web_community-analyze-your-build-with-the-apk-analyzer-android-studio-andr.md", "raw/github_doc-wiki-the-basics-intro-mdx.md", "raw/github_doc-wiki-the-basics-decoding-mdx.md", "raw/github_doc-docs-cli-parameters-mdx.md", "raw/github_doc-docs-install-mdx.md", "raw/github_doc-docs-faq-mdx.md", "raw/github_doc-wiki-the-basics-frameworks-mdx.md", "raw/github_release-v3-0-3.md", "raw/github_doc-readme-md-5.md", "raw/github_release-1-5-6.md", "raw/web_community-android-debug-bridge-adb-android-studio-android-developers.md", "raw/web_community-logcat-command-line-tool-android-studio-android-developers.md", "raw/github_doc-i18n-en-docs-android-md.md", "raw/github_doc-i18n-en-docs-bridges-md.md", "raw/web_community-certificates.md", "raw/web_community-system-ca-on-android-emulator.md", "raw/github_doc-docs-android-analysis-md.md", "raw/github_doc-docs-apktool-resource-analysis-md.md", "raw/github_doc-docs-adb-device-analysis-md.md", "raw/github_doc-i18n-en-docs-frida-ps-md.md", "raw/github_doc-i18n-en-docs-frida-trace-md.md", "raw/github_doc-docs-unreleased-mdx.md"]
---
# Android APK Analysis
This page walks through inspecting an Android app you are authorized to examine: your own build, a test app, or an app under an agreed security assessment. It covers what is inside an APK, reading the code with JADX, decoding resources with apktool, pulling an installed app and its logs over ADB, observing it with Frida on a test device, and viewing its own HTTPS traffic through mitmproxy on an emulator. Running, instrumenting and intercepting an app each need their own authorization on top of the right to read it; see [[concepts/legal-and-ethics]].
## Comparison
| Task | Tool | Command to start | What you get |
|---|---|---|---|
| See the layout | `unzip`, Android Studio APK Analyzer | `unzip app.apk` | Raw entries: `AndroidManifest.xml` (binary), `classes.dex`, `resources.arsc`, `res/`, `META-INF/` |
| Read the code | JADX 1.5.6 | `jadx -d out app.apk` or `jadx-gui app.apk` | Java source from Dalvik bytecode, plus a decoded manifest and resources |
| Decode resources and manifest | apktool 3.0.3 | `apktool d app.apk -o app_decoded` | Readable `AndroidManifest.xml`, `res/` XML, strings, `apktool.yml`, and smali unless `-s` |
| Get the installed app | `adb` | `adb shell pm path ` then `adb pull` | The base APK and any split APKs |
| Read logs | `adb logcat` | `adb logcat -b crash` | System, app and crash log buffers |
| Watch it run | Frida | `frida-ps -Uai`, `frida-trace -U ...` | Live call traces on a rooted test device or emulator |
| See its traffic | mitmproxy | Proxy the emulator, trust the CA | Decrypted HTTP(S) requests the app makes |
JADX is the fastest route to readable logic; apktool is the more faithful route for resources and the manifest. Use both: JADX for "what does this code do", apktool for "what does the app declare".
## Analysis
### 1. Know what an APK contains
An APK is a ZIP archive holding everything the app needs at runtime. An Android App Bundle (`.aab`) is a publishing format that cannot be installed on a device; Google Play turns it into device-specific APKs. That is why an installed app often shows up as a base APK plus splits.
| Entry | What it is | Why you care |
|---|---|---|
| `AndroidManifest.xml` | Binary XML (unreadable until decoded) declaring the package, components, permissions and required features | Activities, services and content providers must be declared here or the system can't start them (broadcast receivers can also be registered in code), so it is your map of most entry points |
| `classes.dex` (and further `classes*.dex`) | Dalvik bytecode for the app's Kotlin/Java code | What JADX decompiles |
| `resources.arsc` | Compiled resource table | Strings, IDs and configuration-specific values |
| `res/`, `assets/`, `lib/` | Resources, raw assets, native libraries | Native `.so` files go to a native disassembler |
| `META-INF/` and the APK signing block | Signatures | Identify the signer |
**Signing schemes.** Every app must be signed; Android does no CA verification of app certificates, so self-signed is normal. The AOSP page describes v1 (JAR signing, which leaves ZIP metadata unprotected), v2 (added in Android 7.0, signs the whole file so any change invalidates it), and v3 (Android 9, same idea with extra data in the signing block). The signer certificate also decides which Linux user ID the app runs under, so apps signed with the same key can share a UID or signature-level permissions. Each app otherwise runs as its own Linux user in its own sandbox.
### 2. Read the code with JADX
JADX turns Dalvik bytecode into Java and also decodes `AndroidManifest.xml` and `resources.arsc`. It needs 64-bit Java 11 or later (the `with-jre` bundles include one). It accepts `.apk`, `.dex`, `.aab`, `.apks`, `.apkm`, `.xapk`, `.jar`, `.aar` and more.
```
jadx -d out app.apk # sources and resources to ./out
jadx -s -d out app.apk # resources only (--no-src)
jadx -r -d out app.apk # code only (--no-res)
jadx --deobf -d out app.apk # rename short/obfuscated identifiers
jadx --single-class com.example.Foo app.apk
jadx-gui app.apk # search, find usage, jump to declaration
```
JADX warns that it usually can't decompile 100% of the code. For methods that fail, `-m simple` or `-m fallback` shows lower-level output and `--show-bad-code` prints the inconsistent result instead of hiding it. `--mappings-path` loads a deobfuscation mapping if you have one (the `rename-mappings` plugin also reads ProGuard-format files). JADX 1.5.6 (2026-07-10) leads with security fixes: it rejects absolute or current-directory zip entry paths and sanitises values written into exported Gradle projects. Its zip-bomb and XML checks can be tuned with `JADX_ZIP_MAX_ENTRIES_COUNT` and related variables.
### 3. Decode resources with apktool
Unzipping only gives you compiled files; apktool decodes them back to readable form.
```
apktool d app.apk # decodes into ./app
apktool d app.apk -o out_dir # choose the output folder
apktool d -s app.apk # skip dex disassembly (leave dex untouched)
apktool d --only-manifest app.apk # just the manifest
apktool d -r app.apk # keep resources.arsc as-is
```
Useful decode options: `-f` overwrites the destination, `-j ` sets threads (default: CPU count, max 8), `--keep-broken-res` keeps going after `Invalid config flags detected. Dropping resources`, and `--res-resolve-mode default|greedy|lazy` controls how missing resources are handled. Apps that depend on OEM framework resources need that framework installed first with `apktool if framework-res.apk` (frameworks are cached under `$HOME/.local/share/apktool` on Linux, `$HOME/Library/apktool` on macOS, `$HOME/AppData/Local/apktool` on Windows). "Magic APKs" built with modified OEM tooling can't be decoded at all.
apktool's install guide says a minimum of Java 8 is required; building apktool from source needs a newer JDK (its unreleased-changes notes move the build requirement to JDK 17). On Windows, the guide asks you to adjust case sensitivity, because Windows paths are case-insensitive. The current release is **apktool 3.0.3** (2026-07-20).
This page covers decoding for inspection only.
### 4. Pull an installed app and its logs with ADB
Enable USB debugging in Developer options, then:
```
adb devices -l # confirm the device; note its serial
adb shell pm list packages -3 # third-party packages (-f adds the APK path)
adb shell pm path com.example.app # one line per APK: base plus any splits
adb pull ./acq/ # copy each listed path
```
With several devices attached, add `-s ` (e.g. `adb -s emulator-5554 shell`). Pull **every** path `pm path` prints; analysing only `base.apk` misses code and resources shipped in splits. Hash what you pulled before you analyse it.
Logs live in circular buffers kept by `logd` (`main`, `system`, `crash` and others):
```
adb logcat -b crash # crash buffer only
adb logcat ActivityManager:I MyApp:D *:S # filter by tag:priority, silence the rest
adb logcat -b all -v color -d # dump everything once and exit
```
Many logcat options need root, and release builds may have stripped debug logging at compile time.
### 5. Observe it with Frida on a test device
Use a rooted test device or an emulator, never a personal phone. Frida's docs recommend a Pixel or Nexus on current official software, or a Google Android 9 arm/arm64 emulator image. Push and start `frida-server` (steps in [[entities/frida]]), then:
```
frida-ps -Uai # installed apps and identifiers
frida-trace -U -i "Java_*" com.example.app # JNI entry points
frida-trace -U -f com.example.app -I "libnative-lib.so"
```
Use `-j` to trace Java methods. Under Frida 17, scripts you load from Python must pull in `frida-java-bridge` explicitly (compile with `frida-compile` or the bindings' `frida.Compiler`); the REPL and `frida-trace` include it already. Without root, Frida's options are embedding Gadget into your own build or using a debugger.
### 6. Inspect its traffic with mitmproxy on an emulator
mitmproxy can decrypt TLS only if the client trusts its CA. It generates a unique CA on first run in `~/.mitmproxy` (`mitmproxy-ca-cert.cer` is the copy for Android).
- **User CA.** Proxy the device, visit `mitm.it` and install the certificate. Since Android 7, apps ignore user-installed CAs unless they are configured to use them, so this mainly works for browsers and for your own builds configured to trust user certificates.
- **System CA on an emulator.** Use an AVD (Android Virtual Device) image that is not a "Google Play" production build, which blocks `adb root`. Name the certificate by its hash, then push it into the system store:
```
cd ~/.mitmproxy
hashed_name=`openssl x509 -inform PEM -subject_hash_old -in mitmproxy-ca-cert.cer | head -1` && cp mitmproxy-ca-cert.cer $hashed_name.0
emulator -avd -writable-system
adb root
adb shell avbctl disable-verification # API 29+ only
adb reboot
adb root
adb remount
adb push .0 /system/etc/security/cacerts
adb shell chmod 644 /system/etc/security/cacerts/.0
adb reboot
```
Always start that emulator with `-writable-system`, or it boots a clean system image without your CA. Point it at the proxy with `-http-proxy 127.0.0.1:8080`, and confirm the CA under Settings → Security → Advanced → Encryption & credentials → Trusted credentials. For a Google Play image, mitmproxy documents a Magisk-module route instead.
Some apps ignore the system store or pin certificates, and their connections will fail through the proxy. mitmproxy's `ignore_hosts` option lets those domains pass through untouched. Bypassing pinning is out of scope for this wiki. For capture concepts and TLS key logging, see [[syntheses/network-captures-and-protocols]].
### Doing it through REA
REA wraps the same tools for agents: `rea pull-adb-package --output-directory ` pulls the complete split set with a digest per file, `rea read-adb-logcat --count 200` reads a bounded dump, `rea inspect-android-package ` runs headless JADX, and `rea decode-android-resources ` runs apktool with `--no-src` (REA's apktool lane was verified with apktool 2.7.0). REA marks install, push, start and uninstall as device-mutating. See [[entities/rea]].
## Recommendations
1. Start static: hash the APK, `unzip -l` it, decode the manifest with apktool, and list components and permissions.
2. Open it in `jadx-gui`, start from the manifest's launcher activity and exported components, and search for strings that interest you.
3. Only then move to runtime, on an emulator or dedicated test device: logcat first, Frida tracing next, traffic last.
4. Pull every split, keep the hashes, and save trace and proxy output with the scenario you ran (see [[syntheses/dynamic-analysis-workflow]]).
5. If decompilation output is wrong or missing, try another JADX mode, then compare with apktool's smali, then see [[syntheses/troubleshooting]].
## Pages Compared
- [[entities/frida]]: Frida setup on Android, `frida-trace`, the Frida 17 bridge change.
- [[syntheses/dynamic-analysis-workflow]]: choosing tracing versus debugging, keeping evidence.
- [[concepts/executable-formats]]: dex format in the context of other executable formats.
- [[syntheses/network-captures-and-protocols]]: mitmproxy modes and packet capture.
- [[entities/rea]]: agent-driven ADB, JADX and apktool workflows.
- [[syntheses/choosing-a-disassembler]]: JEB and others for heavier Android work and native libraries.
## Sources
- `raw/web_community-application-fundamentals-app-architecture-android-developers.md`
- `raw/web_community-app-manifest-overview-app-architecture-android-developers.md`
- `raw/web_community-app-signing-security-android-open-source-project.md`
- `raw/web_community-analyze-your-build-with-the-apk-analyzer-android-studio-andr.md`
- `raw/github_doc-wiki-the-basics-intro-mdx.md`
- `raw/github_doc-wiki-the-basics-decoding-mdx.md`
- `raw/github_doc-docs-cli-parameters-mdx.md`
- `raw/github_doc-docs-install-mdx.md`
- `raw/github_doc-docs-faq-mdx.md`
- `raw/github_doc-wiki-the-basics-frameworks-mdx.md`
- `raw/github_release-v3-0-3.md`
- `raw/github_doc-readme-md-5.md`
- `raw/github_release-1-5-6.md`
- `raw/web_community-android-debug-bridge-adb-android-studio-android-developers.md`
- `raw/web_community-logcat-command-line-tool-android-studio-android-developers.md`
- `raw/github_doc-i18n-en-docs-android-md.md`
- `raw/github_doc-i18n-en-docs-bridges-md.md`
- `raw/web_community-certificates.md`
- `raw/web_community-system-ca-on-android-emulator.md`
- `raw/github_doc-docs-android-analysis-md.md`
- `raw/github_doc-docs-apktool-resource-analysis-md.md`
- `raw/github_doc-docs-adb-device-analysis-md.md`
- `raw/github_doc-i18n-en-docs-frida-ps-md.md`
- `raw/github_doc-i18n-en-docs-frida-trace-md.md`
- `raw/github_doc-docs-unreleased-mdx.md`
---
title: "Choosing a Disassembler"
type: synthesis
tags: [disassembler, decompiler, ghidra, ida-pro, jeb, hopper, rizin, cutter, objdump, mcp, comparison]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
sources: ["raw/github_tag-ghidra-12-1-4-ghidradocs-gettingstarted-md.md", "raw/github_doc-ghidra-features-base-src-main-help-help-topics-importerplugi.md", "raw/github_doc-docs-installation-md.md", "raw/github_doc-third-party-notices-md.md", "raw/github_doc-readme-md-23.md", "raw/github_doc-docs-ghidra-provider-md.md", "raw/web_community-9-4-md.md", "raw/web_community-basic-usage-md.md", "raw/web_community-overview-md-3.md", "raw/web_community-overview-md-4.md", "raw/web_community-running-in-batch-mode-md.md", "raw/github_doc-readme-md-24.md", "raw/github_doc-docs-ida-provider-md.md", "raw/web_community-jeb-decompiler.md", "raw/web_community-faq-jeb-decompiler.md", "raw/web_community-decompiling-jeb-decompiler.md", "raw/web_community-native-code-analysis-jeb-decompiler.md", "raw/web_community-android-analysis-jeb-decompiler.md", "raw/web_community-webassembly-analysis-jeb-decompiler.md", "raw/web_community-ethereum-analysis-jeb-decompiler.md", "raw/web_community-mcp-server-jeb-decompiler.md", "raw/github_doc-docs-jeb-analysis-md.md", "raw/web_community-hopper-tutorial.md", "raw/github_doc-docs-kuna-provider-md.md", "raw/github_doc-src-introduction-overview-md.md", "raw/github_doc-docs-source-user-docs-features-rst.md", "raw/github_doc-docs-source-user-docs-common-issues-rst.md", "raw/github_doc-docs-reverse-engineering-tools-md.md", "raw/web_community-objdump-gnu-binary-utilities.md", "raw/github_doc-readme-md-18.md", "raw/github_doc-doc-usage-md.md", "raw/github_doc-readme-md-21.md", "raw/github_doc-docs-run-md.md", "raw/github_doc-readme-md-20.md", "raw/web_community-9-4sp1-md.md", "raw/github_doc-src-introduction-getting-rizin-md.md", "raw/github_doc-docs-source-index-rst.md", "raw/github_release-rizin-v0-9-1.md", "raw/github_release-cutter-v2-5-0.md", "raw/web_community-whats-new-md.md", "raw/web_community-getting-started-md-2.md", "raw/web_community-agentic-ida-with-idalib-md.md", "raw/github_doc-src-scripting-rz-pipe-md.md", "raw/github_doc-src-first-steps-commandline-options-md.md", "raw/github_doc-src-disassembling-intro-md.md", "raw/github_doc-src-analysis-code-analysis-md.md", "raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi.md", "raw/github_doc-ghidradocs-ghidraclass-intermediate-versiontracking-html.md"]
---
# Choosing a Disassembler
## Comparison
Six tools, compared on what usually decides the choice. Versions: Ghidra 12.1.4, IDA 9.4sp1, Rizin 0.9.1 / Cutter 2.5.0; JEB as documented in its online manual (REA verifies against JEB 5.48.0).
| | Ghidra | IDA Pro | JEB | Hopper | Rizin / Cutter | GNU objdump |
|---|---|---|---|---|---|---|
| **Cost** | Free, Apache-2.0 | Commercial: Home, Pro (Essential/Expert/Ultimate), Classroom; available decompilers depend on plan | Commercial subscription, no perpetual licences; OEM licensed per server; 30% student discount for non-profit research | Commercial; free demo with vendor-set limits | Free, open source | Free (GNU binutils) |
| **Host** | Windows, Linux, macOS (prebuilt natives for Win x64, Win ARM64 via x86 emulation, and Linux x64) | Windows (incl. native ARM64 in 9.4), Linux, macOS | Windows, Linux, macOS on x64 and arm64; needs a JDK 21+ (21–23 recommended) | macOS and Linux (the platforms REA's Hopper installer supports) | Windows, macOS, Linux | Any binutils host |
| **Decompiler** | Yes, built in and free (works from each processor's SLEIGH/p-code model) | Hex-Rays (per-architecture, per-plan) | `dexdec` (Dalvik/Java) and `gendec` (x86, ARM, MIPS, RISC-V, wasm, EVM, Simatic S7, Nvidia SASS) | Pseudo-code mode | `pdg` via rz-ghidra; Cutter also takes RetDec/JSDec plugins | None |
| **Standout strengths** | Broad loader set (PE, ELF, Mach-O, DYLD cache, DEX/APK, raw); Version Tracking; team server | Mature interactive analysis; 9.4 Dyld Shared Cache workflow, Swift ABI, SVE2/SME, Hexagon; FLIRT and Lumina | Android end to end (APK, resources, certs, multi-DEX, Dalvik/native debugging); deobfuscating decompiler (control-flow unflattening, opaque predicates); wasm and EVM decompilers | Readable macOS-native workflow with CFG and pseudo-code views | Scriptable terminal workflow, `rz-bin` one-shot triage, JSON output (`j` suffix) on many commands | Already installed; exact, raw linear disassembly |
| **Scripting / headless** | Java and PyGhidra; `analyzeHeadless` | IDC, IDAPython, Domain API, C++ SDK; idalib headless; `-Ohexrays` batch decompile | Python client scripts and back-end plugins through the JEB API; command-line runners | Not covered here | `rz-pipe` (Python, Rust, Haskell, OCaml, Ruby); `rizin -qc` | Shell pipelines |
| **Agent / MCP** | Community GhidraMCP (targets Ghidra 12.1.4); REA provider | Official Hex-Rays IDA MCP Server (9.4+); community `ida-pro-mcp` (IDA Pro 8.3+, **not IDA Free**); REA adapter | Built-in MCP server (default port `8425`) and the VIBRE in-GUI assistant | Driven through REA | REA bridge (persistent debugger sessions Linux-only) | REA `inspect-with-objdump` |
## Analysis
### Format and architecture decide most cases
- **Android apps.** JEB is built for this: APK, encoded resources, legacy/v2/v3 certificates, multi-DEX decompilation, and seamless Dalvik↔native debugging. The free route is JADX plus apktool, covered in [[syntheses/android-apk-analysis]]. JEB also analyses the APK's native `.so` libraries; on the free route they go to Ghidra (or IDA).
- **Apple platforms.** IDA 9.4's rebuilt Dyld Shared Cache workflow loads cache images on demand and searches by address, symbol or string across unloaded parts. Its Swift ABI annotations (`__swiftself`, `__swiftthrows`, `__swiftasync`) make it the strongest documented choice for iOS and macOS system code. Ghidra also imports DYLD shared caches and Mach-O. Hopper's pseudo-code reads "more or less like an Objective-C method", which suits light macOS work.
- **WebAssembly and EVM.** JEB has dedicated decompilers for both. Its EVM analyser identifies public and private methods and tries to recover method and event names without an ABI, and its wasm output is C-like. See [[concepts/webassembly]] and [[concepts/evm-bytecode]] for the free toolchains.
- **Firmware and unusual CPUs.** Ghidra's SLEIGH processor set plus the Raw Binary loader with a chosen Language ID is the default free path ([[syntheses/firmware-analysis]]). IDA 9.4 added Hexagon (QDSP6) with a Qualcomm MBN loader, MCore, more TriCore and RISC-V extensions, and ARM SVE2/SME.
- **Quick looks and scripting.** `objdump -d` disassembles only the sections expected to hold code and won't cross a symbol boundary. `-D` disassembles every non-empty, non-bss section and drops that assumption, so the two can differ when data sits in code. `-M intel` switches x86 output to Intel syntax, and `-b -m ` forces format and architecture. `rz-bin -I` gives a one-screen security and format summary. Neither builds a function database.
### Budget and licensing
Ghidra and Rizin/Cutter cost nothing and have no seat limits. IDA's decompiler coverage depends on the product you buy. Embedding idalib in a product, SaaS or shared server needs an IDA Pro OEM licence. JEB keeps opening existing projects after a subscription lapses but won't create new ones. Its published EULA also restricts public or third-party disclosure of decompiled code and analysis reports unless your licence type allows it, which matters if you publish write-ups. Hopper's personal and computer licences have different user and installation rules. For IDA, having an API or a bridge explicitly doesn't grant hosting, service or extra-user rights; check each vendor's agreement the same way. See [[concepts/legal-and-ethics]].
### Scripting and automation
For large batch jobs: Ghidra's `analyzeHeadless` and IDA's idalib both import and analyse without a GUI, and IDA can batch-decompile with `idat -Ohexrays:...:outfile:ALL -A input`. Rizin is the lightest to script, since many commands take a `j` suffix for JSON output and `rz-pipe` parses it. JEB's back end runs separately from the GUI and is designed for analysis pipelines.
### Agent and MCP support
- **IDA**'s MCP server is first-party, open source and vendor-supported. It needs IDA 9.4+ with idalib, attaches to GUI databases or starts headless workers, and coordinates several databases.
- **JEB** ships MCP in the product. The manual registers it with `claude mcp add --transport http jeb http://localhost:8425/mcp` or `codex mcp add jeb --url http://localhost:8425/mcp`. The manual also lists `jeb_wincon.bat -c --mcp` as a command-line start. REA's testing found that JEB 5.48's `-c` headless mode does not accept `--mcp` and that headless instances don't offer `open_project`, so use the GUI or the programmatic `JebMcpServerInstance` route.
- **Ghidra** relies on community GhidraMCP or REA. REA imports each target into a throwaway Ghidra database: read-only analysis plus function-name and comment edits on Linux and macOS, read-only on Windows.
- REA can also use **Kuna**, an Apache-2.0 static decompiler, for ELF and thin Mach-O on x86-64, AArch64 and ARM. It doesn't support PE or Windows.
Details and safety notes are in [[syntheses/re-with-ai-agents-mcp]].
### Where triage tools sit before the disassembler
Don't start with a disassembler on a suspicious file. Run the pre-filters first and let them tell you where to look ([[syntheses/malware-triage]], [[syntheses/triage-an-unknown-binary]]):
1. **Detect It Easy** (`diec `) identifies format, compiler and packer or protector, and flags structural anomalies, without running the file. A packed result means unpack or run dynamically before deep static work.
2. **FLOSS** pulls static, stack, tight and decoded strings that plain `strings` misses.
3. **capa** reads a PE, ELF, .NET module, shellcode or sandbox report and lists capabilities mapped to ATT&CK. It plugs into the disassembler step directly. The **capa explorer** plugin runs inside **IDA Pro** and uses your IDB edits to extract better features. A **Ghidra** plugin shows results in the Ghidra UI, and Ghidra can serve as capa's feature-extraction backend through PyGhidra. capa also runs on Binary Ninja's backend.
Only then open the sample in Ghidra or IDA, starting from the functions capa and FLOSS pointed at.
## Recommendations
- **Default free choice:** Ghidra. Add Rizin for terminal and scripted work and `rz-bin` triage, and Cutter if you want a lighter GUI over Rizin.
- **Professional native work, Apple code, or vendor-supported agent integration:** IDA Pro 9.4sp1 with Hex-Rays. Check that your plan includes the decompilers for your target architectures.
- **Android-heavy, obfuscated Java/Dalvik, wasm or EVM:** JEB, if the licence and its disclosure terms fit. Otherwise JADX/apktool for Android and the free wasm and EVM tools.
- **Occasional macOS reversing on a budget:** Hopper's demo, or Ghidra.
- **Sanity checks and CI:** `objdump` and `rz-bin` give quick, reproducible output with nothing to configure.
- **Malware:** DIE → FLOSS → capa first, then a disassembler with capa explorer loaded. Never run samples on your host.
- **Whatever you pick:** results depend more on fixing prototypes and types than on the tool ([[concepts/decompilation-concepts]]).
## Pages Compared
- [[entities/ghidra]]
- [[entities/ida-pro]]
- [[entities/rizin-and-cutter]]
- [[concepts/decompilation-concepts]]
- [[syntheses/malware-triage]]
- [[syntheses/android-apk-analysis]]
- [[syntheses/re-with-ai-agents-mcp]]
- [[entities/rea]]
## Sources
- `raw/github_tag-ghidra-12-1-4-ghidradocs-gettingstarted-md.md`
- `raw/github_doc-ghidra-features-base-src-main-help-help-topics-importerplugi.md`
- `raw/github_doc-docs-installation-md.md`
- `raw/github_doc-third-party-notices-md.md`
- `raw/github_doc-readme-md-23.md`
- `raw/github_doc-docs-ghidra-provider-md.md`
- `raw/web_community-9-4-md.md`
- `raw/web_community-basic-usage-md.md`
- `raw/web_community-overview-md-3.md`
- `raw/web_community-overview-md-4.md`
- `raw/web_community-running-in-batch-mode-md.md`
- `raw/github_doc-readme-md-24.md`
- `raw/github_doc-docs-ida-provider-md.md`
- `raw/web_community-jeb-decompiler.md`
- `raw/web_community-faq-jeb-decompiler.md`
- `raw/web_community-decompiling-jeb-decompiler.md`
- `raw/web_community-native-code-analysis-jeb-decompiler.md`
- `raw/web_community-android-analysis-jeb-decompiler.md`
- `raw/web_community-webassembly-analysis-jeb-decompiler.md`
- `raw/web_community-ethereum-analysis-jeb-decompiler.md`
- `raw/web_community-mcp-server-jeb-decompiler.md`
- `raw/github_doc-docs-jeb-analysis-md.md`
- `raw/web_community-hopper-tutorial.md`
- `raw/github_doc-docs-kuna-provider-md.md`
- `raw/github_doc-src-introduction-overview-md.md`
- `raw/github_doc-docs-source-user-docs-features-rst.md`
- `raw/github_doc-docs-source-user-docs-common-issues-rst.md`
- `raw/github_doc-docs-reverse-engineering-tools-md.md`
- `raw/web_community-objdump-gnu-binary-utilities.md`
- `raw/github_doc-readme-md-18.md`
- `raw/github_doc-doc-usage-md.md`
- `raw/github_doc-readme-md-21.md`
- `raw/github_doc-docs-run-md.md`
- `raw/github_doc-readme-md-20.md`
- `raw/web_community-9-4sp1-md.md`
- `raw/github_doc-src-introduction-getting-rizin-md.md`
- `raw/github_doc-docs-source-index-rst.md`
- `raw/github_release-rizin-v0-9-1.md`
- `raw/github_release-cutter-v2-5-0.md`
- `raw/web_community-whats-new-md.md`
- `raw/web_community-getting-started-md-2.md`
- `raw/web_community-agentic-ida-with-idalib-md.md`
- `raw/github_doc-src-scripting-rz-pipe-md.md`
- `raw/github_doc-src-first-steps-commandline-options-md.md`
- `raw/github_doc-src-disassembling-intro-md.md`
- `raw/github_doc-src-analysis-code-analysis-md.md`
- `raw/github_doc-ghidra-features-decompiler-src-main-help-help-topics-decompi.md`
- `raw/github_doc-ghidradocs-ghidraclass-intermediate-versiontracking-html.md`
---
title: "Dynamic Analysis Workflow"
type: synthesis
tags: [dynamic-analysis, debugging, instrumentation, tracing, record-replay, checkpoints, process-capture, core-dumps, evidence, sandboxing, gdb, frida, rea]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
sources: ["raw/web_community-breakpoints-debugging-with-gdb.md", "raw/web_community-set-watchpoints-debugging-with-gdb.md", "raw/web_community-attach-debugging-with-gdb.md", "raw/web_community-process-record-and-replay-debugging-with-gdb.md", "raw/web_community-checkpoint-restart-debugging-with-gdb.md", "raw/web_community-core-file-generation-debugging-with-gdb.md", "raw/web_community-server-debugging-with-gdb.md", "raw/github_doc-i18n-en-docs-modes-md.md", "raw/github_doc-i18n-en-docs-index-md.md", "raw/github_doc-i18n-en-docs-frida-trace-md.md", "raw/github_doc-i18n-en-docs-gadget-md.md", "raw/github_doc-docs-frida-instrumentation-md.md", "raw/github_doc-docs-process-capture-md.md", "raw/github_doc-docs-recorded-crashes-md.md", "raw/github_doc-agents-skills-reverse-engineer-anything-references-runtime-o.md", "raw/github_doc-agents-skills-reverse-engineer-anything-references-evidence-.md", "raw/github_doc-docs-reverse-engineering-tools-md.md", "raw/github_doc-disclaimer-md.md"]
---
# Dynamic Analysis Workflow
Static analysis tells you what code *could* do; dynamic analysis shows what it *does* on a given run. This page helps you pick the right runtime technique for the question, run it without hurting your host, and keep results you can defend later. For tool detail see [[entities/gdb-and-pwndbg]] and [[entities/frida]].
Running a program is a separate act from reading it. The disclaimer of REA (rea-agents, an agent-driven reverse-engineering toolkit; see [[entities/rea]]) makes the point directly: permission to inspect an artifact does not necessarily cover executing it, intercepting its traffic or changing a device, and having a file or a reachable debugging endpoint does not by itself grant permission. Settle that first; see [[concepts/legal-and-ethics]].
## Comparison
| Technique | Best question it answers | How intrusive | Platform notes | What you keep |
|---|---|---|---|---|
| **Debugger** (GDB, pwndbg) | "What is the exact state at this instruction, and who wrote this value?" | Stops the process; software watchpoints single-step and run hundreds of times slower | GDB attach needs an OS with processes and permission to signal the target. REA's GDB sessions are Linux-only | Session notes, `gcore` snapshots, backtraces |
| **Instrumentation** (Frida scripts) | "Every time this function runs, what were its arguments and result?" | Code runs inside the target with the target's memory, file and network authority; the process keeps running | Injected mode needs frida-server (root/jailbreak on mobile); Gadget embedded in your own build on non-rooted devices | Message logs; REA records script path and SHA-256 |
| **Tracing** (`frida-trace`) | "Which of these APIs get called, and in what order?" | Light: auto-generated hooks that log and keep going | Same as Frida | `-o` writes the trace to a file |
| **Record/replay** (GDB `record`) | "How did we get here?" Step backwards through a recorded window | Heavy: `full` method records every instruction (default cap 200000) | GNU/Linux on x86, ARM, AArch64, PowerPC, RISC-V, S/390 and others; also via gdbserver | `record save` writes the log to a file |
| **Checkpoints** (GDB `checkpoint`) | "Let me retry from just before the bug." | Cheap: a snapshot copy of the process | GNU/Linux only | In-session only |
| **Process capture** (REA) | "Did two runs of the same scenario behave the same?" | Runs the command as you; no isolation | Capture on Linux or macOS; not native Windows | Evidence JSON with terminal output, exit, process tree, file before/after hashes |
| **Core inspection** (GDB, REA `inspect-recorded-crash`) | "What was the state when it crashed?" | None at analysis time; reads a file | REA reads Linux ELF64 cores, x86-64 and AArch64 | The core file plus its SHA-256 |
## Analysis
### Choosing a technique
Start from the question, not the tool.
1. **You don't yet know where to look.** Trace first. `frida-trace -i "recv*" -i "read*" ` or `-I ` gives a cheap call timeline that tells you which functions matter, and it does not stop the program. Tracing is usually also the least disruptive option for UI apps and services that time out when paused.
2. **You know the function and need the values.** Write a small Frida script that hooks it and reports arguments and return values back to the host, or set a GDB breakpoint there. Pick Frida when stopping would change behaviour (timeouts, watchdogs, network peers) or on mobile; pick GDB when you need to stop and look around: registers, stack, heap, nearby memory.
3. **You know the data and need the writer.** A GDB hardware watchpoint (`watch *(int *) 0xADDR`) stops at the exact instruction that changed it. If only software watchpoints are available it still works, just much more slowly, and it reports at the next statement instead.
4. **The bug happens long before the symptom.** Use record/replay: `start`, then `record` (the process must already be running), run to the failure, then step backwards or `record goto begin`. The `full` method stops at 200000 instructions by default (`set record full insn-number-max` changes it), so start recording close to the region you care about. Intel's `record btrace` (`bts` or `pt`) has lower overhead but records no data, so variables and registers are unavailable when you step backwards.
5. **You keep overshooting.** Drop a `checkpoint` before the suspect area and `restart ` instead of re-running. On Linux a checkpoint is an identical process copy, so addresses stay put even with ASLR, which keeps address breakpoints valid. It rewinds file positions but cannot un-write file contents or recall bytes already sent to a device.
6. **You need to compare two builds or two runs.** REA process capture runs one scenario (`rea capture-process ./scenario.json --json > capture.json`) and `rea compare-process-captures a.json b.json` reports where terminal output, exit, files or process trees differ. It refuses to compare captures taken under different working directories, environments or observation paths, so keep those identical.
7. **It already crashed.** Analyse the core rather than reproducing. In a live session `gcore` saves one on demand. REA's `inspect-recorded-crash` reads Linux ELF64 cores for x86-64 and AArch64 and can optionally add pwndbg memory-map context. It needs a caller-supplied Python environment with pinned parsing libraries, and it treats the PIDs recorded in a core as metadata only, never as a live process to attach to.
For browser and Electron targets, REA's runtime observation is passive by design: it attaches to a loopback DevTools (CDP) endpoint and never claims it clicked, navigated or ran page JavaScript unless you asked it to run a declared scenario. See [[syntheses/javascript-and-electron-apps]].
### Sandboxing untrusted targets
Every technique on this page except core inspection runs the target, and none of them contains it:
- **Process capture is explicitly not a sandbox.** The command runs with your user's permissions and inherits your environment; observation paths only choose what gets snapshotted, not what the process may touch.
- **Frida scripts** execute with the target process's memory, filesystem and network authority, so a hostile target is just as hostile while hooked.
- **REA's resource limits** on crash inspection are not a sandbox for the engine code either.
- **gdbserver has no authentication.** Whoever connects gets the privileges of the user running it, so never expose it on a public network. Frida's Gadget listens on `127.0.0.1` by default; keep it there, or set its `token` and `certificate` options.
REA's guidance is to "use an appropriate environment for untrusted targets" and to review agent-generated scripts before running them. For a suspicious sample, follow the safe-handling steps in [[syntheses/malware-triage]] before running anything. For mobile work, use a dedicated test device or an emulator rather than a personal phone.
### Keeping evidence
Dynamic results are only as good as your record of the run. Habits that hold up:
- **Hash inputs first.** Record the SHA-256 of the binary, script and any core file. REA does this automatically: Frida evidence stores the script's path and digest (not its source), and crash evidence stores the core's digest and size.
- **Save the run, not just your conclusion.** `frida-trace -o`, `record save ` (default name `gdb_record.`), `gcore` and REA's capture JSON all give you something a reviewer can re-examine. A saved record log can be loaded again with `record restore`.
- **Write down the scenario.** Arguments, environment overrides, working directory and timing all change behaviour. REA's capture commits these into the set of settings both captures must share, and inputs marked `sensitive` are stored only as a byte count.
- **Mark gaps honestly.** REA's evidence model records truncation and residual unknowns rather than treating missing data as absence: a filesystem snapshot is not a syscall trace and can miss short-lived changes, and a static/runtime correlation is a hypothesis, not proof of causality.
- **Protect what you captured.** Traces and captures can contain credentials and personal data; you are responsible for storing and sharing them appropriately.
## Recommendations
- **Default order:** trace, then hook, then break. Escalate only when the cheaper method can't answer the question.
- **On Linux desktop binaries:** GDB with pwndbg for state, `checkpoint` to iterate, `record` for short "how did we get here" windows, `gcore` before anything destructive.
- **On Android or iOS apps you are authorized to test:** Frida on a test device; see [[syntheses/android-apk-analysis]].
- **For regression or reconstruction checks:** REA process capture with identical scenarios on both sides; add `finalization_ms` if the target must write a final report before a timeout kills it.
- **Remote targets:** gdbserver over SSH (`target remote | ssh -T host gdbserver - prog`) avoids opening a listening port.
- **Agent-driven sessions:** REA's GDB sessions need Linux, and its process capture needs Linux or macOS; on Windows, run Linux REA inside WSL for Linux targets.
## Pages Compared
- [[entities/gdb-and-pwndbg]]: breakpoints, watchpoints, record/replay, checkpoints, gdbserver.
- [[entities/frida]]: injected, embedded and preloaded instrumentation; `frida-trace`.
- [[entities/rea]]: process capture, recorded crashes, Frida and GDB providers, evidence.
- [[syntheses/malware-triage]]: safe handling before execution.
- [[syntheses/triage-an-unknown-binary]]: static first look before running anything.
## Sources
- `raw/web_community-breakpoints-debugging-with-gdb.md`
- `raw/web_community-set-watchpoints-debugging-with-gdb.md`
- `raw/web_community-attach-debugging-with-gdb.md`
- `raw/web_community-process-record-and-replay-debugging-with-gdb.md`
- `raw/web_community-checkpoint-restart-debugging-with-gdb.md`
- `raw/web_community-core-file-generation-debugging-with-gdb.md`
- `raw/web_community-server-debugging-with-gdb.md`
- `raw/github_doc-i18n-en-docs-modes-md.md`
- `raw/github_doc-i18n-en-docs-index-md.md`
- `raw/github_doc-i18n-en-docs-frida-trace-md.md`
- `raw/github_doc-i18n-en-docs-gadget-md.md`
- `raw/github_doc-docs-frida-instrumentation-md.md`
- `raw/github_doc-docs-process-capture-md.md`
- `raw/github_doc-docs-recorded-crashes-md.md`
- `raw/github_doc-agents-skills-reverse-engineer-anything-references-runtime-o.md`
- `raw/github_doc-agents-skills-reverse-engineer-anything-references-evidence-.md`
- `raw/github_doc-docs-reverse-engineering-tools-md.md`
- `raw/github_doc-disclaimer-md.md`
---
title: "Firmware Analysis: binwalk, unblob and Loading Raw Images"
type: synthesis
tags: [firmware, binwalk, unblob, entropy, carving, ghidra, raw-binary, embedded]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
binwalk_version: "3.1.0"
unblob_version: "26.6.4"
sources: ["raw/github_doc-readme-md-16.md", "raw/web_community-cargo-installation-md.md", "raw/web_community-file-analysis-and-extraction-md.md", "raw/web_community-carving-file-data-md.md", "raw/web_community-generating-entropy-graphs-md.md", "raw/web_community-json-analysis-results-md.md", "raw/web_community-errors-and-debugging-md.md", "raw/web_community-limitations-md.md", "raw/web_community-speed-and-accuracy-md.md", "raw/github_doc-readme-md-15.md", "raw/github_doc-docs-guide-md.md", "raw/github_doc-docs-installation-md-2.md", "raw/github_doc-docs-index-md-3.md", "raw/github_doc-docs-firmware-analysis-md.md", "raw/github_doc-docs-raw-binary-md.md", "raw/github_doc-docs-ghidra-mips-md.md", "raw/github_doc-docs-ghidra-riscv-md.md", "raw/github_doc-docs-ghidra-ppc-md.md", "raw/github_doc-ghidra-features-base-src-main-help-help-topics-importerplugi.md", "raw/github_doc-ghidra-runtimescripts-support-analyzeheadlessreadme-md.md"]
---
# Firmware Analysis: binwalk, unblob and Loading Raw Images
A firmware image is usually a container of containers: a vendor header, a compressed kernel, one or more filesystems, and padding. The workflow is to map the regions first, extract what can be extracted, check entropy for what can't, and only then load individual executables (or a headerless blob with a known CPU and load address) into a disassembler. Work only on images you own or are authorized to examine; see [[concepts/legal-and-ethics]].
## Comparison
| Need | binwalk v3 | unblob |
|---|---|---|
| Quick "what is in here?" map with offsets | Primary use: `binwalk image.bin` | Possible via `--report`, but extraction-first |
| Recursive extraction | `-Me` | Default behaviour, depth 10 |
| Carve unknown regions to disk | `--carve` (known and unknown sections) | Unknown chunks carved automatically; null/`0xFF` padding identified |
| Entropy | `--entropy` PNG graph (0–8 scale) | Shannon entropy and chi-square per unknown chunk (`-n`, `-vvv` draws a chart) |
| Machine-readable output | `--log=results.json` | `--report report.json` |
| Confidence signal | Colour: green high, yellow medium, red magic-bytes only | Start and end offsets computed from the format standard |
| Implementation | Rust rewrite with custom parsers | Python with Rust and Hyperscan; external extractors |
| Install | `cargo install binwalk` (does not install external extraction dependencies) or Docker (bundles them) | `pip install unblob` plus extractors, Docker, Kali `apt install unblob`, Nix |
| Privileges | Not stated in sources | Runs as a regular user |
REA pins **binwalk 3.1.0** and **unblob 26.6.4** as its verified parser versions; other 3.1.x / 26.6.x builds are accepted but flagged as unverified.
## Analysis
### Step 1: map regions with binwalk
```bash
binwalk /tmp/firmware.bin # signature scan
binwalk -e /tmp/firmware.bin # scan and extract
binwalk -Me /tmp/firmware.bin # scan and recursively extract
binwalk --exclude=jpeg,png,gif /tmp/firmware.bin
binwalk --log=results.json /tmp/firmware.bin
```
Signature names for `--include`/`--exclude` come from the "Signature Name" column of the supported-signatures list. Treat red (low-confidence) hits as leads, not facts: only the magic bytes matched. Version 3 parsers validate structure and learn each embedded file's size, so it skips over (for example) a gzip body instead of reporting false hits inside it. The project's own comparison shows v2 reporting MySQL and Zlib signatures inside a plain `.tgz` where v3 reports a single gzip entry.
The JSON log records, per hit, `offset`, `size`, `name`, `confidence` and `description`, plus an `extractions` object when extraction was requested. That is the format to feed into scripts.
### Step 2: check entropy for what binwalk can't name
```bash
binwalk --entropy firmware.bin # PNG saved to the current directory
binwalk --entropy --log=results.json firmware.bin # per-block entropy in JSON
```
Add `--png` to write the graph to a file of your choosing instead of the current directory.
Both compression and encryption push entropy toward 8. A long flat high-entropy region with no recognised signature is a candidate for encrypted or unknown-compressed data; low or zero blocks are usually padding. Entropy alone can't tell compression from encryption.
### Step 3: extract with unblob
```bash
pip install unblob
unblob --show-external-dependencies # confirm every extractor is present
unblob firmware.bin # output in firmware.bin_extract/
unblob -e /tmp/output firmware.bin
unblob --report report.json firmware.bin
unblob -d 5 -n 2 firmware.bin # depth 5, entropy for two levels
unblob --skip-magic "POSIX tar archive" firmware.bin
```
Key defaults from the CLI help: `-d/--depth` 10, `-n/--entropy-depth` 1 (0 turns it off), `-p/--process-num` CPU count. The default skip-magic list already ignores formats unlikely to hold more firmware (JPEG, PNG, SQLite, PDF, fonts, and similar). unblob's distinguishing idea is that it computes both start and end offsets from each format's specification, so the space between known chunks becomes explicit "unknown chunks" rather than silent gaps.
The Python package does **not** include the extractors. On Ubuntu/Debian the docs install them with:
```bash
sudo apt install android-sdk-libsparse-utils e2fsprogs p7zip-full unar zlib1g-dev liblzo2-dev lzop lziprecover libhyperscan-dev zstd lz4
```
SquashFS additionally needs the project's `sasquatch` package. The Docker image (`ghcr.io/onekey-sec/unblob:latest`) bundles everything. The host directories you mount must share the container's `uid:gid`, so add `-u $UID:$GID` on multi-user hosts.
### Step 4: load executables, or the raw blob, in Ghidra
Extracted ELF binaries import normally (see [[entities/ghidra]]). For a headerless image such as a bootloader, ECU (engine control unit) dump or ROM, Ghidra's **Raw Binary** format is always offered in the import dialog. Its options are Block Name (default: the default address space, usually `ram`), Base Address (default 0), File Offset (default 0) and Length (default: whole file). You must pick the language yourself. The headless equivalent:
```bash
analyzeHeadless /path/projects MyProject -import image.bin -loader BinaryLoader \
-loader-baseAddr 0x1000 -loader-blockName MyBlock -processor x86:LE:32:default
```
Getting the base address and processor right is the whole game: a wrong base breaks every absolute pointer and cross-reference.
### Doing the same through REA
[[entities/rea]] wraps both engines without installing them. Supply absolute paths and run on Linux; REA's firmware support is verified on Linux x64 only:
```sh
export REA_BINWALK_COMMAND=/absolute/path/to/binwalk
export REA_UNBLOB_COMMAND=/absolute/path/to/unblob
rea inspect-firmware-regions /path/to/firmware.bin
rea extract-firmware /path/to/firmware.bin /path/to/new-output --offset 64 --length 4096 --max-depth 3
```
Its limits are worth knowing even if you run the tools directly:
| Limit | Value |
|---|---|
| Input size | 128 MiB |
| Deadline | 120 seconds; 1 GiB address space and 120 CPU seconds per process via `prlimit` |
| Staging budget | 256 MiB and 10,000 entries (`--max-output-bytes`, `--max-output-files`) |
| Depth | default 3, range 1–10 |
| Output directory | must be absent, absolute, with an existing parent; never overwritten |
It also needs util-linux `prlimit` (override with `REA_FIRMWARE_PRLIMIT_COMMAND`) and a procps-compatible `ps` on `PATH`. It keeps unblob's Landlock sandbox on and never retries with `--no-sandbox`.
For headerless code REA requires an explicit CPU and entry address, and never guesses either from a `.bin` suffix:
```bash
rea inspect /absolute/path/to/ecu.bin --target-format raw-binary --architecture arm --entry-point 0x8000000 --provider ghidra --json
```
| `--architecture` | Ghidra language | Alignment |
|---|---|---|
| `x86` / `x86_64` | `x86:LE:32:default` / `x86:LE:64:default` (compiler `gcc`) | byte |
| `arm` | `ARM:LE:32:v7` | 4-byte |
| `arm64` | `AARCH64:LE:64:v8A` | 4-byte |
| `6502` | `6502:LE:16:default` | byte; image must fit in `0x0000..0xffff` |
| `m68k` | `68000:BE:32:default` | 2-byte |
Big-endian ARM, Thumb-only and Cortex-M are not supported for raw binaries, and Hopper and IDA refuse raw targets. For extracted ELF children, REA's MIPS, RISC-V and PowerPC ELF support pins languages because Ghidra's header auto-selection can be wrong. A plain `EM_PPC` header gets `PowerPC:BE:32:e500:default`, which misreads ordinary 750-family code. Recordless legacy MIPS32 executables get a 64-bit language unless pinned to `MIPS::32:default`. RISC-V admits little-endian `ET_EXEC` with the double-float ABI only. Ghidra 12.1.4 ships no big-endian RISC-V language.
## Recommendations
- **Start with binwalk for the map, then use unblob to extract.** binwalk is the fast survey; unblob's end-offset accuracy and unknown-chunk reporting make it the better extractor when you need to account for every byte.
- **Run `unblob --show-external-dependencies` before trusting an empty result.** A missing extractor (for example `debugfs` for ext filesystems, or `sasquatch` for SquashFS) leaves those chunks unextracted with only partial results, which is easy to mistake for "nothing there".
- **Read red binwalk hits and lone magic matches skeptically**, and confirm them with entropy and a second tool.
- **Size your machine:** binwalk needs at least as much free memory as the file being analysed.
- **Debug noisy runs with `RUST_LOG`**: `RUST_LOG=off`, `RUST_LOG=info` or `RUST_LOG=debug binwalk -Me file.bin`. Logs go to stderr.
- **Record base address and language** alongside any finding from a raw import, because the same bytes at a different base are a different program.
- **Not covered here:** reset-vector inference, memory-mapped I/O, device trees, firmware emulation and encrypted-image recovery. None of the sources provide procedures for them, and REA explicitly excludes most of them (reset vectors, memory-mapped I/O, device-tree semantics, emulation).
## Pages Compared
- [[entities/ghidra]]: raw import, language selection and auto-analysis
- [[syntheses/triage-an-unknown-binary]]: what to do with each extracted ELF
- [[concepts/executable-formats]]: ELF headers, `e_flags` and machine types
- [[entities/rea]]: agent-driven firmware inspection and its limits
- [[syntheses/choosing-a-disassembler]]: which tool handles the extracted architecture
## Sources
- `raw/github_doc-readme-md-16.md`
- `raw/web_community-cargo-installation-md.md`
- `raw/web_community-file-analysis-and-extraction-md.md`
- `raw/web_community-carving-file-data-md.md`
- `raw/web_community-generating-entropy-graphs-md.md`
- `raw/web_community-json-analysis-results-md.md`
- `raw/web_community-errors-and-debugging-md.md`
- `raw/web_community-limitations-md.md`
- `raw/web_community-speed-and-accuracy-md.md`
- `raw/github_doc-readme-md-15.md`
- `raw/github_doc-docs-guide-md.md`
- `raw/github_doc-docs-installation-md-2.md`
- `raw/github_doc-docs-index-md-3.md`
- `raw/github_doc-docs-firmware-analysis-md.md`
- `raw/github_doc-docs-raw-binary-md.md`
- `raw/github_doc-docs-ghidra-mips-md.md`
- `raw/github_doc-docs-ghidra-riscv-md.md`
- `raw/github_doc-docs-ghidra-ppc-md.md`
- `raw/github_doc-ghidra-features-base-src-main-help-help-topics-importerplugi.md`
- `raw/github_doc-ghidra-runtimescripts-support-analyzeheadlessreadme-md.md`
---
title: "JavaScript Bundles and Electron Apps"
type: synthesis
tags: [javascript, electron, asar, fuses, source-maps, wakaru, bundles, minified, cdp, inspector]
created: 2026-10-16
updated: 2026-10-16
confidence: medium
sources: ["raw/github_doc-readme-md-13.md", "raw/github_doc-docs-cli-md-2.md", "raw/web_community-source-map-format-specification.md", "raw/github_doc-readme-md-12.md", "raw/web_community-asar-archives-electron.md", "raw/web_community-electron-fuses-electron.md", "raw/github_doc-docs-electron-fuses-md.md", "raw/github_doc-docs-electron-observation-md.md", "raw/github_doc-docs-javascript-runtime-observation-md.md", "raw/github_doc-docs-javascript-recovery-md.md", "raw/github_doc-docs-javascript-artifact-reconstruction-md.md", "raw/github_doc-docs-javascript-runtime-reconciliation-md.md"]
---
# JavaScript Bundles and Electron Apps
Shipped JavaScript is often not obfuscated in the reverse-engineering sense. Much of it has only been bundled, transpiled and minified. Recovering it is mostly a matter of applying the right tool in the right order: look for source maps, unpack the bundle into modules, restore modern syntax, then add runtime observation if you need it. Electron apps add a packaging layer (the ASAR archive) and package-time switches (fuses) that decide which observation techniques will work at all.
## Comparison
| Technique | Input | What you get | Runs target code? | Main limits |
|---|---|---|---|---|
| **Source map recovery** | `.map` file referenced by the bundle | Original file names, and original sources when `sourcesContent` is embedded | No | Only works if a map shipped or is reachable |
| **wakaru unpack/decompile** | Bundle, chunk set, directory, Bun single-file executable, Google Tag Manager (GTM) container | Per-module files with modern syntax (ESM, `async`/`await`, `??`, `?.`) | No | Minified local names stay short. Heavy control-flow or VM obfuscation is out of scope |
| **ASAR extraction** | `app.asar` (+ `app.asar.unpacked/`) | The packaged app tree | No | Unpacked companions must be collected separately |
| **Static app graph (REA, see [[entities/rea]])** | ASAR or extracted directory | Entry points, preload, `contextBridge` APIs, IPC channels/handlers, endpoints, native add-on requests | No | Inferences from syntax. Dynamic values stay unresolved |
| **Fuse read** | Electron executable / framework binary | Which runtime switches (Inspector, `ELECTRON_RUN_AS_NODE`, ASAR integrity) are honored | No | Code can still enable debugging another way |
| **Passive runtime observation** | Running Node/Electron with a loopback Inspector or Chrome DevTools Protocol (CDP) endpoint | Which scripts actually loaded, execution contexts, workers, page structure | Target is already running | Depends on fuses and on attach timing |
| **Active Electron scenario (REA)** | Electron executable + entry point | Window/WebContents timeline, IPC channel and argument-shape metadata | Yes, launches it | Not a sandbox. Needs Inspector-capable fuses |
## Analysis
### 1. Find the code
Electron normally bundles the app's source into an ASAR archive. With default fuses, Electron looks for code in `app.asar`, then `app`, then `default_app.asar`. ASAR is a flat, uncompressed, tar-like format: a Pickle-encoded 8-byte `header_size`, a JSON header, then the file bytes. Each header entry records `offset` (a UINT64 stored as a string, relative to the end of the header), `size`, an optional `executable` flag, and `integrity` (a SHA256 `hash` of the whole file plus per-block hashes at a stated `blockSize`). Files with identical contents are stored once, and several entries can point at the same offset. Files excluded at pack time with `--unpack-dir` live on disk in an `.asar.unpacked` companion, not inside the archive.
```bash
npm install --engine-strict @electron/asar # needs Node 22.12.0+
asar list app.asar
asar extract app.asar extracted/
asar extract-file app.asar package.json
```
Inside a running app, Electron patches Node's `fs` and `require` so they treat archives as read-only virtual directories. A few APIs (`child_process.execFile`, `fs.open`, `process.dlopen` for native modules) get the needed file extracted to a temporary path first. `fs.stat` results for archive members are guessed, so only size and file type are trustworthy. To read the archive as a plain file from Node, use the `original-fs` module or set `process.noAsar = true`.
### 2. Check for source maps before decompiling
A bundle can point to its map through a `//# sourceMappingURL=` comment, or through a `sourcemap:` HTTP header when served over HTTP. The older `x-sourcemap` header is deprecated, and the HTTP header wins over the comment if both are present. The map is JSON with `"version": 3`, `sources`, `names`, `mappings`, an optional `sourcesContent` array of original source text, and an optional `ignoreList` marking third-party sources (some browsers still read the deprecated `x_google_ignoreList`). If `sourcesContent` is populated, you can recover the original files directly:
```bash
wakaru extract input.js.map -o src/
```
### 3. Unpack and unminify with wakaru
wakaru (Apache-2.0) splits bundles into modules and rewrites the AST to recover modern syntax. It removes recognized Babel/TypeScript/SWC helpers and converts CommonJS to ESM.
```bash
npx wakaru input.js -o output.js # single file
wakaru bundle.js --unpack -o out/ # unpack + decompile
wakaru entry.js chunk.js --unpack -o out/ # code-split chunks together
wakaru dist/ --unpack -o out/ # scan a build directory
wakaru bundle.js --unpack=strict -o out/ # structural detection only
wakaru bundle.js --unpack=inspect -o out/ # finer boundaries, not runnable
wakaru bundle.js --unpack --provenance -o out/ # provenance.json byte ranges
wakaru bun extract ./compiled-app -o extracted/ # every file in a Bun executable
```
| Option | Use it when |
|---|---|
| `--level minimal` / `standard` (default) / `aggressive` | Minimal for auditing or diffing where behavioural fidelity matters. Aggressive when you only need to read the code and can accept edge-case changes |
| `--raw` | You want the split without the readability pipeline (`--level` doesn't apply) |
| `--source-map