[GH-ISSUE #2299] [Feature] Return optional decompiled C# for selected .NET members as reconstruction inference #761

Open
opened 2026-10-15 18:28:04 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 12, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2299

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Native analysis or decompilation

Current limitation

REA's seven managed tools read .NET metadata and CIL with REA's own parser, and none of them emits C#. The managed static provider says it never "decompiles C#". ilspycmd runs only in the optional ILSpy oracle of npm run verify:managed, and only on a source-owned fixture (--disable-updatecheck --type Fixture.Program, 30 s timeout, 256 KiB stdout). Outside that lane, REA_ILSPY_CMD_PATH is read only by rea doctor for a version check.

To see C# for a method in their own assembly, an analyst runs ilspycmd by hand, then builds an import_managed_reconstruction input by copying each method's token, signature.raw_sha256 and body.normalized_il_sha256 out of inspect_managed_members Evidence, along with the decompiler's version, executable digest and options. An agent working only through MCP cannot do this, because no tool runs the decompiler.

Use cases

  • Read the C# of a method found through inspect_managed_members, compare_managed_members or the application graph, without leaving REA, and keep that text in the investigation's Evidence trail.
  • Review a changed method between two builds as C#. Each reconstruction stays bound to its own build's SHA-256, MVID, token and CIL digest.
  • See literals, branching and member usage in context. #2158 handles the narrower ldstr case and leaves decompilation out of scope.

Desired outcome and success criteria

  • A caller supplies an assembly path and its inspect_managed_members Evidence, and selects methods by token or by declaring type full_name (the existing selection semantics). When a decompiler is configured, REA returns C# for each method. Each result carries the same locks, decompiler identity and canonical_observation: false / confidence_floor: "inference" labels that import_managed_reconstruction records today, and is retained as session Evidence.
  • The CLI and MCP return equivalent results on a source-owned fixture.
  • If the file at the path no longer matches the Evidence's artifact SHA-256, REA refuses the request and does not run the decompiler.
  • Each selected method gets its own outcome. A method with an absent or malformed CIL body is reported as not lockable, and decompiler errors, timeouts and oversized output are reported per method. Partial text is never presented as complete.
  • With no decompiler configured, capability discovery and the tool report the operation as unavailable and name the setting to configure. No process is launched.
  • The decompiler runs with its update check disabled and .NET telemetry turned off, under a time limit, an output limit and caller cancellation. It leaves no temporary files.
  • verify:managed with REA_ILSPY_CMD_PATH set exercises the new operation end to end and prints digests only. The default lane passes with an injected decompiler. The managed guide and the generated catalog document the operation, its optional prerequisite and its inference label.

Expected interaction with targets

Read existing files or captured data

Target and scope details (optional)

Targets are ordinary CIL assemblies (PE/CLI) that REA's managed parser already admits. REA would launch a user-installed decompiler as a local subprocess that reads the selected assembly. REA does not load or execute the assembly, and neither REA nor the decompiler writes to it.

Out of scope: NativeAOT, which has no CIL bodies to decompile and is covered by #744 and the native route; IL2CPP; ReadyToRun or C++/CLI native bodies; single-file host unpacking; project export, PDB generation and BAML/resource decompilation; deobfuscation or protection removal; making C# canonical; and installing or bundling ILSpy or a .NET runtime.

This would reverse the boundary set in #282, which added the BYO ILSpy oracle "without adding ... a public decompile tool". It is a new provider capability, so it needs an explicit maintainer scope decision before implementation.

Verification available so far

I can suggest a sample or manual check

Verification details (optional)

Suggested check: run the same source-owned fixture the ILSpy oracle uses, select its pinned method by token, and confirm that the returned text and the decompiler identity match a manual ilspycmd --disable-updatecheck -m <token> <fixture> run, and that the locks match inspect_managed_members. The README in the ilspycmd 11.1.0.9782 package documents -m|--member <doc-id-or-token>, which accepts a metadata token such as 0x06000005. I have not run a prototype.

Alternatives considered

  • Make import_managed_reconstruction run the decompiler when no text is supplied. Not preferred: that tool states it does not decompile or execute anything and has no process-launch effect, and docs/tool-design.md advises against mixing execution into an unrelated tool mode.
  • Document a manual ilspycmd -m and import recipe. This adds no authority, so it is the fallback if the provider scope is declined. It still leaves MCP-only agents without C#, and copying locks by hand is error-prone.
  • Embed ICSharpCode.Decompiler through a bundled .NET bridge. This conflicts with the documented package boundary, which ships no .NET runtime, SDK or ILSpy.
  • Use dnSpyEx. It is GPL-3.0 and a debugger and assembly editor, which is broader than read-only decompilation.

Possible direction

Add a separate managed tool (and its CLI command) that runs the configured BYO ilspycmd for each selected method token and returns the result through the existing reconstruction contract, so imported C# and REA-produced C# share one lock format. Reuse REA_ILSPY_CMD_PATH and its doctor check, and keep the contract engine-neutral so other decompiler families can be added under their own scope review. The bounds that verify:managed uses today (update check off, telemetry opt-out, timeout, output cap) are a starting point.

References

Originally created by @coygeek on GitHub (Oct 12, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2299 ### Before submitting - [x] I searched the open and closed issues and pull requests for a related proposal. - [x] I understand the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope) must be reviewed before integration; open questions are welcome here. - [x] This request does not include credentials, private samples, or sensitive target data. ### Area Native analysis or decompilation ### Current limitation REA's seven managed tools read .NET metadata and CIL with REA's own parser, and none of them emits C#. The managed static provider says it never "decompiles C#". `ilspycmd` runs only in the optional ILSpy oracle of `npm run verify:managed`, and only on a source-owned fixture (`--disable-updatecheck --type Fixture.Program`, 30 s timeout, 256 KiB stdout). Outside that lane, `REA_ILSPY_CMD_PATH` is read only by `rea doctor` for a version check. To see C# for a method in their own assembly, an analyst runs `ilspycmd` by hand, then builds an `import_managed_reconstruction` input by copying each method's token, `signature.raw_sha256` and `body.normalized_il_sha256` out of `inspect_managed_members` Evidence, along with the decompiler's version, executable digest and options. An agent working only through MCP cannot do this, because no tool runs the decompiler. ### Use cases - Read the C# of a method found through `inspect_managed_members`, `compare_managed_members` or the application graph, without leaving REA, and keep that text in the investigation's Evidence trail. - Review a changed method between two builds as C#. Each reconstruction stays bound to its own build's SHA-256, MVID, token and CIL digest. - See literals, branching and member usage in context. #2158 handles the narrower `ldstr` case and leaves decompilation out of scope. ### Desired outcome and success criteria - A caller supplies an assembly path and its `inspect_managed_members` Evidence, and selects methods by token or by declaring type `full_name` (the existing selection semantics). When a decompiler is configured, REA returns C# for each method. Each result carries the same locks, decompiler identity and `canonical_observation: false` / `confidence_floor: "inference"` labels that `import_managed_reconstruction` records today, and is retained as session Evidence. - The CLI and MCP return equivalent results on a source-owned fixture. - If the file at the path no longer matches the Evidence's artifact SHA-256, REA refuses the request and does not run the decompiler. - Each selected method gets its own outcome. A method with an absent or malformed CIL body is reported as not lockable, and decompiler errors, timeouts and oversized output are reported per method. Partial text is never presented as complete. - With no decompiler configured, capability discovery and the tool report the operation as unavailable and name the setting to configure. No process is launched. - The decompiler runs with its update check disabled and .NET telemetry turned off, under a time limit, an output limit and caller cancellation. It leaves no temporary files. - `verify:managed` with `REA_ILSPY_CMD_PATH` set exercises the new operation end to end and prints digests only. The default lane passes with an injected decompiler. The managed guide and the generated catalog document the operation, its optional prerequisite and its inference label. ### Expected interaction with targets Read existing files or captured data ### Target and scope details (optional) Targets are ordinary CIL assemblies (PE/CLI) that REA's managed parser already admits. REA would launch a user-installed decompiler as a local subprocess that reads the selected assembly. REA does not load or execute the assembly, and neither REA nor the decompiler writes to it. Out of scope: NativeAOT, which has no CIL bodies to decompile and is covered by #744 and the native route; IL2CPP; ReadyToRun or C++/CLI native bodies; single-file host unpacking; project export, PDB generation and BAML/resource decompilation; deobfuscation or protection removal; making C# canonical; and installing or bundling ILSpy or a .NET runtime. This would reverse the boundary set in #282, which added the BYO ILSpy oracle "without adding ... a public decompile tool". It is a new provider capability, so it needs an explicit maintainer scope decision before implementation. ### Verification available so far I can suggest a sample or manual check ### Verification details (optional) Suggested check: run the same source-owned fixture the ILSpy oracle uses, select its pinned method by token, and confirm that the returned text and the decompiler identity match a manual `ilspycmd --disable-updatecheck -m <token> <fixture>` run, and that the locks match `inspect_managed_members`. The README in the `ilspycmd` 11.1.0.9782 package documents `-m|--member <doc-id-or-token>`, which accepts a metadata token such as `0x06000005`. I have not run a prototype. ### Alternatives considered - Make `import_managed_reconstruction` run the decompiler when no text is supplied. Not preferred: that tool states it does not decompile or execute anything and has no process-launch effect, and docs/tool-design.md advises against mixing execution into an unrelated tool mode. - Document a manual `ilspycmd -m` and import recipe. This adds no authority, so it is the fallback if the provider scope is declined. It still leaves MCP-only agents without C#, and copying locks by hand is error-prone. - Embed ICSharpCode.Decompiler through a bundled .NET bridge. This conflicts with the documented package boundary, which ships no .NET runtime, SDK or ILSpy. - Use dnSpyEx. It is GPL-3.0 and a debugger and assembly editor, which is broader than read-only decompilation. ### Possible direction Add a separate managed tool (and its CLI command) that runs the configured BYO `ilspycmd` for each selected method token and returns the result through the existing reconstruction contract, so imported C# and REA-produced C# share one lock format. Reuse `REA_ILSPY_CMD_PATH` and its doctor check, and keep the contract engine-neutral so other decompiler families can be added under their own scope review. The bounds that `verify:managed` uses today (update check off, telemetry opt-out, timeout, output cap) are a starting point. ### References - #2258 and the decompiler mapping comment: https://github.com/morluto/rea/issues/2258#issuecomment-6130226337 - Managed guide (tool table, package boundary, ILSpy oracle): https://github.com/morluto/rea/blob/aeee5074d124414239b6dd440dc79c26bcbd58a4/docs/managed-code-analysis.md - ILSpy oracle invocation: https://github.com/morluto/rea/blob/aeee5074d124414239b6dd440dc79c26bcbd58a4/scripts/verify/managed/oracles.mjs - Reconstruction import contract and inference labels: https://github.com/morluto/rea/blob/aeee5074d124414239b6dd440dc79c26bcbd58a4/src/domain/managed/managedReconstruction.ts - Provider statement that it does not decompile C#: https://github.com/morluto/rea/blob/aeee5074d124414239b6dd440dc79c26bcbd58a4/src/dotnet/ManagedStaticProviderMetadata.ts - Tool design guidance: https://github.com/morluto/rea/blob/aeee5074d124414239b6dd440dc79c26bcbd58a4/docs/tool-design.md - History: #42 (listed `decompile_managed_function`), #273 (reconstruction import), #282 (BYO ILSpy oracle, no public decompile tool). Related: #2158, #744, #743. - ILSpy (MIT, v11.1 released 2026-09-23): https://github.com/icsharpcode/ILSpy - `ilspycmd` 11.1.0.9782 (MIT, `net10.0` dotnet tool): https://www.nuget.org/packages/ilspycmd/11.1.0.9782 - `ilspycmd` options: https://github.com/icsharpcode/ILSpy/blob/master/ICSharpCode.ILSpyCmd/README.md
Author
Owner

@matbrgz commented on GitHub (Oct 12, 2026):

Design & Epistemic Labeling Review

Allowing optional C# decompilation via user-provided ilspycmd is a great capability for analysts, provided REA's strict distinction between observations and inferences is maintained:

  1. Epistemic Classification: Decompiled C# must be explicitly returned under an inference field (e.g. reconstructed_csharp_inference) and tagged with source: "ilspycmd", keeping it distinct from ground-truth CIL instruction bytes and metadata observations.
  2. Bring-Your-Own Authority: In accordance with AGENTS.md, REA must not auto-install or bundle .NET SDKs or global dotnet tools. It should discover existing ilspycmd via REA_ILSPY_CMD_PATH / PATH and fail closed with capability_unavailable if absent.
<!-- gh-comment-id:6134289559 --> @matbrgz commented on GitHub (Oct 12, 2026): ### Design & Epistemic Labeling Review Allowing optional C# decompilation via user-provided `ilspycmd` is a great capability for analysts, provided REA's strict distinction between observations and inferences is maintained: 1. **Epistemic Classification:** Decompiled C# must be explicitly returned under an `inference` field (e.g. `reconstructed_csharp_inference`) and tagged with `source: "ilspycmd"`, keeping it distinct from ground-truth CIL instruction bytes and metadata observations. 2. **Bring-Your-Own Authority:** In accordance with `AGENTS.md`, REA must not auto-install or bundle .NET SDKs or global dotnet tools. It should discover existing `ilspycmd` via `REA_ILSPY_CMD_PATH` / PATH and fail closed with `capability_unavailable` if absent.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#761
No description provided.