AsmResolver
repository·master·Indexed 22 days ago
https://github.com/washi1337/asmresolverA 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.
What's inside AsmResolver
- 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.
Overview of AsmResolver features
masterAsmResolver 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.
Work with .NET AppHost and SingleFileHost Bundles
masterAsmResolver 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.Bundlesnamespace.Work with RT_VERSIONINFO resources
masterThe
RT_VERSIONINFOresource type stores metadata like product names, version numbers, and copyright holders for a Portable Executable (PE) file. In AsmResolver, this is represented by theVersionInfoResourceclass.To use these features, include the following namespace:
using AsmResolver.PE.Win32Resources.Version;What is a RuntimeContext and how does it work?
masterA
RuntimeContextmimics the lifetime of a .NET process by implementing assembly resolution and management logic similar to anAppDomainorAssemblyLoadContext. 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 (likeTypeDefinition). - Any AsmResolver functionality requiring metadata resolution must be provided with a
RuntimeContext. - Once an
AssemblyDefinitionis added to a context, it cannot be removed.
Compare Types using SignatureComparer in v6.0
masterIn v6.0,
SignatureComparer.Defaultdoes not resolve forwarded types (e.g., types using theTypeForwardedToattribute). This means types that are logically the same but reside in different assemblies (likeSystem.ObjectinSystem.RuntimevsSystem.Private.CoreLib) will be treated as distinct.To restore the v5.x behavior where forwarded types are treated as equal, you must initialize a
SignatureComparerwith aRuntimeContext.// 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);Use the PE file layer for raw PE file access
masterThe 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
PEFileclass. - Accessing section headers and raw section contents.
- Reading raw section data using a
BinaryStreamReaderinstance.
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
PEImageclass in the layer above.// Use PEFile for raw header and section access // Use PEImage for interpreted data like imports- Accessing raw top-level PE headers (DOS header, COFF file header, and optional header) via the
Traverse type signatures
masterThere are two primary ways to inspect or traverse a
TypeSignature:- BaseType Property: Use the
.BaseTypeproperty to get the immediate underlying type. For example, if you have an array signature,.BaseTypereturns the element type. - 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 (likeArrayTypeSignatureorBoxedTypeSignature) 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());- BaseType Property: Use the
Handle different types of debug data segments
masterThe
Contentsproperty of aDebugDataEntryimplementsIDebugDataSegment. 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 anISegment.
Currently, AsmResolver has rich support specifically for
CodeViewdebug data.- Supported Formats: Modeled using specific implementations (e.g.,
Understand the structure of a CilMethodBody
masterA
CilMethodBodyobject is composed of three primary building blocks:Instructions: The sequence of CIL instructions to be executed.LocalVariables: The collection of local variables defined within the method.ExceptionHandlers: A collection of regions protected by exception handling logic.
Automatic Metadata Reference Importing in v6.0
masterIn 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
ImportWithis still valid, it is often redundant and can cause unnecessary allocations. You only need to manually import if you are using customReferenceImporterlogic (likeMemberCloner).// 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]));Resolve Metadata using RuntimeContext in v6.0
masterMetadata resolution in v6.0 requires a
RuntimeContextto manage assembly and metadata caches. TheIMemberDescriptor.Resolve()method now accepts this context as a parameter.You can obtain a
RuntimeContextfrom an existingModuleDefinitionor 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);