Reverse engineer .NET assemblies with no extra tools
Quick facts
| Dependencies | None. Static analysis only — no Ghidra, Hopper or IDA. |
| What REA returns | Metadata, CIL instructions, declared native dependencies and build comparisons |
| Input | A .dll or .exe that actually contains PE/CLI metadata |
| Requirement | Node.js 22 (22.19+), 24 (24.11+) or 26+, then npx rea-agents@latest setup |
| Official guide | docs/managed-code-analysis.md |
Official wording for this target type, verbatim from the project README:
Metadata, CIL instructions, declared native dependencies and build comparisons
and from the analysis doc:
REA inspects .NET PE/CLI artifacts without loading or executing their code. It can identify an assembly, inspect metadata and CIL, compare members across builds, and connect declared native calls to supplied native-analysis evidence.
Why this matters for a .NET page specifically: the managed-code path is the one target family that needs nothing installed. No decompiler licence, no native provider, no engine bridge — you point it at a file you already have.
The seven tools
| Task | MCP tool |
|---|---|
| Identify an artifact and its managed deployment form | inspect_managed_artifact |
| Inspect types, signatures, method bodies, calls, and field access | inspect_managed_members |
| List declared native calls and native implementation indicators | inspect_managed_native_boundaries |
| Compare members across builds and map build-local tokens | compare_managed_members |
| Check native call declarations against supplied export or function evidence | verify_managed_native_boundaries |
| Import decompiled code against verified static member identities | import_managed_reconstruction |
| Add managed findings to an application graph | project_managed_application_graph |
(Table above is verbatim from the official doc.)
Step 1: Identify the artifact
Start by asking what the file actually is, not by opening it in a decompiler. inspect_managed_artifact answers the questions that decide every later step: is this really a managed assembly, what is its managed deployment form, and are its type tables intact or mostly forwarded elsewhere?
Two fields are worth understanding before you read anyone's output:
exported_types— type-forwarding surface. An assembly that looks thin isn't empty; it may be exporting types that live in another assembly. If you skip this, you conclude "this file has nothing in it" when it's actually a facade.- CIL fingerprints —
il_sha256andnormalized_il_sha256. The first is the bytes; the second is the bytes with build-local noise normalised out. Use the pair when two builds "look the same" but the raw hashes disagree: normalised hashes are what make an unchanged method recognisable between builds. #-pointer tables — metadata streams that carry pointers rather than inline data are handled; don't read a pointer-table stream as if it were inline data.
Step 2: Inspect members
inspect_managed_members returns types, signatures, method bodies, calls and field access.
Three behaviours to know before you scale up:
--typetakes an exactfull_nameas reported by the member inspection — or a declaring type name you already know. Repeat the flag to select several types.- Top-level type names are namespace-qualified (
Example.Parser). Nested types currently report bare names, which can match more than one type. - The result keeps your requested names in
selection.type_namesand reports unmatched names separately — a typo shows up as an unmatched entry, not as a silently empty result. - Selection restricts decoding to those types, but member references stay an assembly-wide inventory. Narrowing your selection does not guarantee a small output.
Step 3: Read the evidence like an analyst
Static reads are cheap; they are also bounded. Two limits worth knowing before a large run:
- Input files up to 128 MiB are admitted before their bytes are retained. Reads stay bounded if a file grows afterwards.
- Metadata, CIL decoding and projected records share a conservative 64 MiB representation budget. Overflow reports
resource-limitwith unavailable or partial coverage.
So: a resource-limit result is a scope answer, not a crash. Read coverage, issues and limitations before drawing a conclusion — the doc says so explicitly, and it's the difference between "this assembly calls into native code" and "this assembly declares a native call that nobody supplied evidence for".
Step 4: The walkthrough — Newtonsoft.Json 13.0.4
Object: Newtonsoft.Json 13.0.4 — MIT-licensed, 11.3k stars, released as a NuGet package whose nuspec carries multiple target frameworks (net6.0, .NETStandard2.0, and the .NETFramework generations). Source commit 4e13299d4b0ec96bd4df9954ef646bd2d1b5bf2a. It's a good first target precisely because the licence is clean and the internals are public — so you can check every conclusion against the source.
Suggested walkthrough sequence:
- Identify the artifact —
inspect_managed_artifacton theNewtonsoft.Json.dllfrom thenet6.0target; note its managed deployment form and theexported_typesforwarding surface. - Inspect a known type —
inspect_managed_members --type Newtonsoft.Json.JsonSerializer(exactfull_name); read types, signatures and method bodies. - Fingerprint a method — record both
il_sha256andnormalized_il_sha256; repeat on a second build and compare withcompare_managed_members. - Check native boundaries —
inspect_managed_native_boundaries; for a pure-managed library expect no native calls, and understand thatverify_managed_native_boundariesonly checks declarations against evidence you supply. - The licence point — every claim above is checkable against source you are entitled to read.
The point of the walkthrough isn't "look what you can read". It's that every claim on this page is checkable against a licence you're entitled to read. A page that demonstrates the workflow on a proprietary binary and a page that demonstrates it on an MIT assembly teach very different things about what's allowed.
Command reference
The official managed-code guide documents two CLI entry points for this family. They are the only CLI spellings the guide gives, so they are quoted verbatim.
Identify the artifact — metadata, CIL, declared native dependencies and managed deployment form:
rea inspect-managed-artifact /absolute/path/Assembly.dll --jsonInspect members — types, signatures, method bodies, calls and field access:
rea inspect-managed-members /absolute/path/Assembly.dll --jsonRestrict to one type (exact full_name, e.g. Example.Parser):
rea inspect-managed-members /absolute/path/Assembly.dll --type Example.Parser(The three commands above are per the official docs/managed-code-analysis.md — the inspect-managed-artifact and inspect-managed-members entry points are exactly what the guide documents. The other five tools in the seven-tool table — native-boundaries, compare, verify, import, and project graph — are invoked through your agent as MCP tools; the guide does not publish separate terminal verbs for them, so none are invented here.)
The rea binary is available after a global install (npm install --global rea-agents); without it, prefix with npx -y rea-agents@latest. All paths are absolute, exactly as in the guide.
What REA will not do here
Four boundaries, all from the official doc:
- Single-file host packages are not unpacked by this path; the host bundle is a different target.
- IL2CPP / NativeAOT output isn't managed code any more — that's a native analysis route, not this one.
- Native implementation evidence has to be supplied by you:
verify_managed_native_boundarieschecks declarations against evidence you provide, it doesn't produce it. - Reconstructed code is not licensed code — see the authorized-use block below.
Last verified: 2026-10-14 · REA 6.4.0 · commands quoted per the official docs/managed-code-analysis.md.