ILSpy .NET Assembly Browser and Decompiler

repository·master·Indexed 12 days ago

https://github.com/icsharpcode/ilspy

A .NET assembly browser and decompiler featuring ilspycmd for command-line automation, a PowerShell module for programmatic interaction, and a slot-based C# Abstract Syntax Tree (AST) generated via Roslyn source generators.

Tokens
39.5K
Snippets
77
Records
162
Agent score
98%

What's inside ILSpy

  1. Generate an interactive HTML diagrammer

    master

    You can generate an interactive HTML diagrammer app from selected types in the target assembly. By default, it is saved to the --outputdir or in a diagrammer folder next to the assembly.

    Filtering Types

    Use regular expressions to control which types are included or excluded from the diagram:

    • --generate-diagrammer-include <regex>: Whitelist types matching this Type.FullName pattern.
    • --generate-diagrammer-exclude <regex>: Blacklist types matching this Type.FullName pattern.
    • --generate-diagrammer-report-excluded: Outputs a report of types that were excluded (either by default, by explicit exclusion, or by implicit inclusion rules) to help debug your regex.

    Documentation and Namespaces

    • --generate-diagrammer-docs <path|uri>: Provide a path or file:// URI to an XML file containing documentation comments to annotate the diagrams.
    • --generate-diagrammer-strip-namespaces <names>: Provide space-separated namespace names to remove from XML documentation comments for brevity (e.g., remove System.Collections before System).
    # Generate a HTML diagrammer containing all type info into a folder next to the input assembly
    ilspycmd sample.dll --generate-diagrammer
    
    # Generate a HTML diagrammer with filtered type info
    ilspycmd sample.dll --generate-diagrammer -o c:\diagrammer --generate-diagrammer-include LightJson\..+ --generate-diagrammer-exclude LightJson\.Serialization\..+
  2. Access AST nodes via Slots and Kinds

    master

    Nodes can be queried using Per-node slots (specific to a type, e.g., BinaryOperatorExpression.LeftSlot) or Canonical kinds (shared across the AST, found in the Slots class).

    Canonical kinds are matched by reference identity. You can use them for typed access or polymorphic checks:

    // Typed access (T is inferred from the Slots constant)
    node.GetChild(Slots.Left)              
    
    // Collection access
    node.GetChildren(Slots.Parameter)      
    
    // Polymorphic check: "Is this node in an Initializer-kind slot?"
    node.Slot.Kind == Slots.Initializer    
  3. How ILSpy plugins work

    master

    ILSpy uses the Managed Extensibility Framework (MEF) for its plugin architecture. To implement a plugin, you must export specific types for designated extension points.

    Deployment Requirements:

    • Plugins must be placed in the same directory as ILSpy.exe.
    • Plugin assemblies must follow the naming convention *.Plugin.dll.

    Development Setup: To develop a plugin, your project must include references to:

    1. ILSpy.exe
    2. System.Composition.AttributedModel
    3. Other libraries shipped with ILSpy (depending on your plugin's specific functionality).
  4. Understand ILAst Pattern Matching instructions

    master

    In the ILAst representation, certain IL instructions are classified as "patterns". These instructions are used to represent C# pattern matching logic (like is expressions).

    Key characteristics of pattern instructions (match.*):

    • TestedOperand: Specifies the operand being matched against the pattern.
    • Variable: Stores the value of the TestedOperand (potentially converted to the matched type).
      • If used outside the match.* node, it corresponds to a C# single_variable_designation.
      • Otherwise, it is a temporary variable with VariableKind.PatternVar.
    • Evaluation Result: The instruction evaluates to StackType.I4: 0 if the pattern matched, 1 otherwise.

    Nested patterns in instructions with a nestedPatterns body must satisfy the following:

    • Each nested pattern must return true when passed to IsPattern().
    • The testedOperand of a nested pattern must be a member (field, property, or deconstruction.result) of the parent pattern's Variable.
    • Exception: For match.and and match.or instructions, the testedOperand must be exactly the Variable of the parent pattern.
  5. How the Mermaid Diagrammer works

    master

    The Mermaid Diagrammer generates Mermaid class diagrams from assembly type information through a three-step process:

    1. Extraction: ILSpy side-loads the source assembly and all its dependencies to extract type information.
    2. Serialization: The extracted info is structured into a JSON model optimized for HTML rendering. This model combines Mermaid class diagram syntax with metadata regarding relations, inheritance, and documentation comments.
    3. Assembly & Rendering: The JSON data is injected into a template.html file. The HTML diagrammer app then re-assembles this data based on user-selected types and rendering options to generate Mermaid class diagrams.

    Key Implementation Detail: The JSON type info is baked directly into the template.html file (alongside script.js). This allows the diagrammer to function locally from a file system without a web server, bypassing CORS restrictions while still being able to import the Mermaid module from a CDN.

  6. Understand the `ImageListStreamer` on-disk format

    master

    The ImageListStreamer format is a three-layer nested structure used to serialize WinForms ImageList data. When implementing or debugging a decoder (like ImageListDecoder.cs), you must process the data in this specific order:

    1. Layer 1: NRBF Envelope — A .NET Remoting Binary Format (NRBF) payload containing a System.Windows.Forms.ImageListStreamer class record with a Data byte array member.
    2. Layer 2: MSFt RLE Wrapper — An optional Run-Length Encoding (RLE) layer. If the data starts with the magic bytes MSFt (0x4D 0x53 0x46 0x74), it must be decompressed.
    3. Layer 3: ILHEAD + DIBs — The raw Win32 ImageList_Write stream, consisting of an ILHEAD structure followed by color and (optionally) mask Device Independent Bitmaps (DIBs).
  7. How the C# AST and its source generator work

    master

    The C# Abstract Syntax Tree (AST) is composed of slot-based tree nodes. Instead of manual implementation, most mechanical code—including visitor plumbing, structural pattern matching, child-index APIs, constructors, and slot metadata—is automatically generated by a Roslyn source generator (DecompilerSyntaxTreeGenerator.cs).

    To define a node, you declare a partial class and use attributes to define its structure. The generator then fills in the remaining implementation details.

    [DecompilerAstNode]
    public sealed partial class BinaryOperatorExpression : Expression
    {
        [Slot("Left")]
        public partial Expression? Left { get; set; }     // a child slot (optional: nullable)
    
        public BinaryOperatorType Operator { get; set; }  // a scalar -- a plain auto-property
    
        [Slot("Right")]
        public partial Expression? Right { get; set; }    // another child slot
    }
  8. Optimize diagrammer scope and performance

    master

    Large assemblies can lead to long build times, massive file sizes, and Mermaid rendering failures if the type selection is too large. To manage this:

    1. Analyze first: Use the ILSpy GUI to identify specific subdomains worth diagramming.
    2. Limit scope: Use --generate-diagrammer-include and --generate-diagrammer-exclude to restrict the diagrammer to specific namespaces or patterns.
    3. Split diagrammers: Instead of one massive diagrammer, generate multiple smaller ones for different subdomains.
  9. Understand ILSpy event trace providers

    master

    ILSpy emits performance trace events through two distinct EventSource providers. Tracing is disabled by default and only incurs performance costs when a collection session is actively attached. Events are emitted as Start/Stop pairs, allowing trace viewers to calculate durations and nesting levels based on timestamps.

    Available Providers

    • ICSharpCode.Decompiler: Instruments the decompilation pipeline, including per-type/per-member decompilation, type system initialization, assembly resolution, whole-project decompilation, and per-transform timing.
    • ICSharpCode.ILSpyX: Instruments frontend support, including assembly loading, reference resolution, search, analyzers, bundle/zip extraction, and PDB loading.
  10. Decompress the Layer 2 `MSFt` RLE wrapper

    master

    The second layer uses a specific Run-Length Encoding (RLE) format.

    Magic Bytes: The layer is identified by the 4-byte header MSFt (0x4D 0x53 0x46 0x74). If these bytes are missing, treat the buffer as uncompressed and skip to Layer 3.

    RLE Algorithm:

    • The format consists of (uint8 count, uint8 value) pairs.
    • count (1..255) is the number of times the value repeats.
    • There is no end marker; the length is determined by the NRBF byte array size.
    • Long runs are split into multiple (0xFF, v) pairs followed by a remainder pair.
    // Decoding logic summary
    while (reader.TryRead(out byte count))
    {
        reader.TryRead(out byte value);
        writer.TryWriteCount(count, value);
    }
  11. Develop and test the HTML diagrammer

    master

    To edit the HTML, JS, or CSS for the HTML diagrammer, open the ICSharpCode.ILSpyX/MermaidDiagrammer/html folder in Visual Studio Code. The development workflow relies on VS Code tasks to manage the build pipeline for testing the diagrammer in a development environment.

    To successfully develop and test, you must follow this sequence of tasks:

    1. Generate a model: Use the current Debug build of ilspycmd to generate a model.json. This file is a prerequisite for task 3.
    2. Transpile styles: Convert .less files into .css files. These CSS files are tracked by source control and are eventually embedded into ILSpyX.
    3. Generate development diagrammer: Create a testable diagrammer by combining template.html with the model.json generated in step 1.
    4. Auto-rebuild: Enable automatic rebuilding of the development diagrammer by running task 2 or task 3 whenever their respective source files change.