AsmResolver

repository·master·Indexed 22 days ago

https://github.com/washi1337/asmresolver

A powerful library for reading, modifying, and reconstructing Portable Executable (PE) files. It provides specialized support for native Windows binaries and .NET managed metadata, including AppHost/SingleFileHost bundles and ReadyToRun (R2R) binaries. Key features include PE manipulation, an intuitive .NET metadata API, cross-platform PDB and PortablePdb symbol support, and Win32 resource handling. It is compatible with .NET Framework 3.5+, .NET Standard 2.0, .NET Core, and Mono.

Tokens
59.2K
Snippets
183
Records
211
Agent score
75%

What's inside AsmResolver

  1. Overview of AsmResolver

    master
    AsmResolver is a set of libraries designed for .NET programmers to read, modify, and write executable files. It supports both .NET assemblies and native images. The library provides a dual-layer approach: it exposes high-level representations of the Portable Executable (PE) format for ease of use, while still providing access to low-level structures for fine-grained manipulation.
  2. Overview of AsmResolver features

    master

    AsmResolver is a library designed for reading, modifying, and reconstructing Portable Executable (PE) files. It supports both native Windows PE images and images containing managed (.NET) metadata.

    Key Capabilities:

    • PE Manipulation: Create, read, modify, write, and patch PE files. This includes full access to sections, data directories, and Import Address Table (IAT) reconstruction/trampolining. You have full control over the final PE file layout.
    • .NET Metadata Support: Provides an intuitive API similar to System.Reflection. It supports managed, native, and dynamic method bodies, metadata importing/cloning, and managed resource file serialization/deserialization. It also supports AppHost/SingleFileHost bundles and ReadyToRun (R2R) binaries.
    • Symbol Support: Rich, fully managed cross-platform read support for PDB and PortablePdb symbols without requiring DIA dependencies.
    • Win32 Resources: Support for various resource types such as Icons and VersionInfo.
    • Robustness: Designed to be robust against malformed or obfuscated binaries.
    • Cross-Platform: Compatible with Windows and *nix, supporting .NET Framework 3.5+, .NET Standard 2.0, .NET Core, and Mono.
  3. Work with .NET AppHost and SingleFileHost Bundles

    master

    AsmResolver provides support for handling .NET single-file deployment binaries (AppHost / SingleFileHost bundles). These binaries run natively via a platform-specific bootstrapper and do not contain traditional .NET metadata headers.

    You can use AsmResolver to:

    • Extract embedded files from existing bundles.
    • Construct new bundles using .NET SDK templates or existing binaries as templates.
    • Modify, add, or remove files within a bundle.
    • Load assemblies directly from a bundle into a RuntimeContext.

    All relevant functionality is located in the AsmResolver.DotNet.Bundles namespace.

  4. Work with RT_VERSIONINFO resources

    master

    The RT_VERSIONINFO resource type stores metadata like product names, version numbers, and copyright holders for a Portable Executable (PE) file. In AsmResolver, this is represented by the VersionInfoResource class.

    To use these features, include the following namespace:

    using AsmResolver.PE.Win32Resources.Version;
  5. What is a RuntimeContext and how does it work?

    master

    A RuntimeContext mimics the lifetime of a .NET process by implementing assembly resolution and management logic similar to an AppDomain or AssemblyLoadContext. It maintains metadata caches to allow for fast lookup and traversal of external references (e.g., DLLs referenced by an assembly).

    Key characteristics:

    • It acts as a container for loaded assemblies.
    • It facilitates the resolution of metadata references (like TypeReference) into actual definitions (like TypeDefinition).
    • Any AsmResolver functionality requiring metadata resolution must be provided with a RuntimeContext.
    • Once an AssemblyDefinition is added to a context, it cannot be removed.
  6. Compare Types using SignatureComparer in v6.0

    master

    In v6.0, SignatureComparer.Default does not resolve forwarded types (e.g., types using the TypeForwardedTo attribute). This means types that are logically the same but reside in different assemblies (like System.Object in System.Runtime vs System.Private.CoreLib) will be treated as distinct.

    To restore the v5.x behavior where forwarded types are treated as equal, you must initialize a SignatureComparer with a RuntimeContext.

    // v6.0: Default behavior (returns false for forwarded types)
    bool equal = SignatureComparer.Default.Equals(t1, t2);
    
    // v6.0: Behavior with RuntimeContext (returns true for forwarded types)
    var context = new RuntimeContext(DotNetRuntimeInfo.NetCoreApp(10, 0));
    var comparer = context.SignatureComparer; // or new SignatureComparer(context)
    bool equal = comparer.Equals(t1, t2);
  7. Use the PE file layer for raw PE file access

    master

    The PE file layer provides the lowest level of abstraction for the Portable Executable (PE) format. Use this layer when you need to read or write raw executable files directly from or to the disk.

    Key capabilities include:

    • Accessing raw top-level PE headers (DOS header, COFF file header, and optional header) via the PEFile class.
    • Accessing section headers and raw section contents.
    • Reading raw section data using a BinaryStreamReader instance.

    Note on Abstraction: This layer is designed for raw data access and leaves data interpretation to the user. It does not provide high-level models for complex structures like the import directory. For interpreted models (e.g., parsing imports), use the PEImage class in the layer above.

    // Use PEFile for raw header and section access
    // Use PEImage for interpreted data like imports
  8. Traverse type signatures

    master

    There are two primary ways to inspect or traverse a TypeSignature:

    1. BaseType Property: Use the .BaseType property to get the immediate underlying type. For example, if you have an array signature, .BaseType returns the element type.
    2. Visitor Pattern: For complex or deep signatures, implement the ITypeSignatureVisitor<TResult> interface. This allows you to perform strongly-typed traversals and handle specific signature types (like ArrayTypeSignature or BoxedTypeSignature) in a type-safe manner.
    // 1. Simple traversal
    var arrayElementType = arrayTypeSig.BaseType; // returns System.Int32
    
    // 2. Visitor pattern traversal
    public class MyVisitor : ITypeSignatureVisitor<TResult>
    {
        public TResult VisitArrayType(ArrayTypeSignature signature)
        {
            /* ... handle array types ... */
            return signature.AcceptVisitor(this);
        }
    
        public TResult VisitBoxedType(BoxedTypeSignature signature)
        {
            /* ... handle boxed types ... */
            return signature.AcceptVisitor(this);
        }
        // ... other methods ...
    }
    
    TypeSignature signature = ...;
    var result = signature.AcceptVisitor(new MyVisitor());
  9. Handle different types of debug data segments

    master

    The Contents property of a DebugDataEntry implements IDebugDataSegment. The specific implementation depends on the type of debug data:

    • Supported Formats: Modeled using specific implementations (e.g., CodeViewDataSegment).
    • Unsupported/Unrecognized Formats: Modeled using CustomDebugDataSegment, which exposes the raw contents as an ISegment.

    Currently, AsmResolver has rich support specifically for CodeView debug data.

  10. Automatic Metadata Reference Importing in v6.0

    master

    In v5.x, referencing external metadata required explicit calls to a ReferenceImporter (e.g., .ImportWith(module.DefaultImporter)). In v6.0, most importing is performed automatically at build-time.

    While calling ImportWith is still valid, it is often redundant and can cause unnecessary allocations. You only need to manually import if you are using custom ReferenceImporter logic (like MemberCloner).

    // v6.0: Importing is now automatic; no explicit call needed
    var method = module.CorLibFactory.CorLibScope
        .CreateTypeReference("System", "Console")
        .CreateMemberReference("WriteLine", MethodSignature.CreateStatic(factory.Void, [factory.String]));
  11. Resolve Metadata using RuntimeContext in v6.0

    master

    Metadata resolution in v6.0 requires a RuntimeContext to manage assembly and metadata caches. The IMemberDescriptor.Resolve() method now accepts this context as a parameter.

    You can obtain a RuntimeContext from an existing ModuleDefinition or create one manually to specify environment parameters like the .NET runtime version.

    // Using context from an existing module
    RuntimeContext context = module.RuntimeContext;
    TypeDefinition definition = reference.Resolve(context);
    
    // Creating a manual context
    var context = new RuntimeContext(DotNetRuntimeInfo.NetCoreApp(3, 1));
    TypeDefinition definition = reference.Resolve(context);