dnlib

repository·master·Indexed 25 days ago

https://github.com/0xd4d/dnlib

A high-performance .NET module and assembly reader and writer library designed for assembly manipulation, obfuscation, and metadata analysis. It provides deep access to IL code and metadata tables, supporting tasks such as strong name signing, PDB file configuration, managed method exporting (DllExport), and reference resolution via ModuleContext.

Tokens
1.8K
Snippets
6
Records
9
Agent score
32%

What's inside dnlib

  1. Perform enhanced strong name signing

    master

    Enhanced strong naming allows for more complex signing scenarios. You can perform this without key migration or with key migration.

    Without key migration: Requires a StrongNameKey and a StrongNamePublicKey. With key migration: Requires a StrongNameKey and StrongNamePublicKey for both the signature and the identity, plus an identityKey and identityPubKey.

  2. Strong name sign an assembly

    master

    To sign an assembly with a strong name, use ModuleWriterOptions.InitializeStrongNameSigning. This requires a ModuleDef and a StrongNameKey (pointing to your .snk file).

    using dnlib.DotNet.Writer;
    
    ModuleDef mod = ModuleDefMD.Load(...);
    var opts = new ModuleWriterOptions(mod);
    var signatureKey = new StrongNameKey(@"c:\my\file.snk");
    
    // Initializes required properties for signing
    opts.InitializeStrongNameSigning(mod, signatureKey);
    
    mod.Write(@"C:\out\file.dll", opts);
  3. Open a .NET assembly or module

    master

    Use ModuleDefMD.Load() to open a .NET module. It is highly recommended to create a ModuleContext using ModuleDef.CreateModuleContext() and pass it to the Load method to manage assembly and type resolution.

    If you are working with obfuscated Unity/Mono assemblies, you must create a ModuleCreationOptions instance, set ModuleCreationOptions.Runtime to CLRRuntimeKind.Mono, and pass it to the Load method.

    Note: If you are working with .NET Core assemblies, you may need to disable GAC loading and manually add .NET Core reference assembly search paths to your resolver.

  4. Configure PDB file saving

    master

    By default, dnlib reads PDB files from disk. To save a PDB file along with your assembly, use ModuleWriterOptions (or NativeModuleWriterOptions) and set the WritePdb property to true.

    You can specify a custom filename via PdbFileName or provide a custom stream via PdbStream. If using PdbStream, you must also initialize PdbFileName so the name is correctly written into the PE file.

    ModuleContext modCtx = ModuleDef.CreateModuleContext();
    var mod = ModuleDefMD.Load(@"C:\myfile.dll", modCtx);
    
    var wopts = new dnlib.DotNet.Writer.ModuleWriterOptions(mod);
    wopts.WritePdb = true;
    // wopts.PdbFileName = @"C:\out2.pdb"; // Optional: set custom filename
    
    mod.Write(@"C:\out.dll", wopts);
  5. Save a .NET assembly or module

    master

    Use module.Write() to save an assembly to a file or a Stream.

    For C++/CLI assemblies (which contain native code), use module.NativeWrite() instead. You can detect if an assembly contains native code at runtime by checking the IsILOnly property.

    // Standard IL assembly
    module.Write(@"C:\saved-assembly.dll");
    
    // C++/CLI or native-containing assembly
    if (module.IsILOnly) {
        module.Write(@"C:\saved-assembly.dll");
    } else {
        module.NativeWrite(@"C:\saved-assembly.dll");
    }
  6. Resolve references using ModuleContext

    master

    To resolve TypeRef or MemberRef objects, use their .Resolve() methods. These rely on the module.Context.Resolver.

    It is critical to share a single ModuleContext across all modules you open to ensure consistent resolution. You should also add any modules you open to the AssemblyResolver cache to speed up subsequent lookups.

  7. Export managed methods (DllExport)

    master

    You can export managed methods so they can be called by native code. To do this, set the ExportInfo property on a MethodDef.

    Requirements for successful export:

    1. Calling Convention: The method's calling convention must be changed to stdcall, cdecl, thiscall, or fastcall by adding an optional modifier to MethodDef.MethodSig.RetType.
    2. Platform: The assembly must target a specific platform (x86, x64, IA-64, or ARM). AnyCPU is not supported.
    3. Cor20 Header: The IL Only bit in ModuleWriterOptions.Cor20HeaderOptions.Flags must be cleared.
    4. File Type: The file must be a DLL (not an EXE).
    5. Generics: Exported methods must not be generic.
    6. Limit: A maximum of 65,536 methods can be exported due to PE limitations.
    // Example: Changing calling convention to Cdecl
    var type = method.MethodSig.RetType;
    type = new CModOptSig(module.CorLibTypes.GetTypeRef("System.Runtime.CompilerServices", "CallConvCdecl"), type);
    method.MethodSig.RetType = type;
  8. Import runtime types, methods, and fields

    master

    To use standard .NET types (like System.Console) within a module you are building, use the Importer class. This creates a TypeRef, MemberRef, or FieldRef in your module that points to the original assembly, rather than duplicating the actual definition.

    ITypeDefOrRef consoleRef = importer.Import(typeof(System.Console));
    IMethod writeLine = importer.Import(typeof(System.Console).GetMethod("WriteLine"));
  9. Compare types, methods, and fields

    master

    Use the SigComparer class to compare different metadata entities (types, methods, fields, etc.). It can compare entities against each other or against standard .NET types like System.Type or System.Reflection.MethodBase.

    For use in collections like Dictionary<TKey, TValue>, use the provided pre-created equality comparers such as TypeEqualityComparer.Instance or FieldEqualityComparer.Instance.

    // Compare two types
    TypeRef type1 = ...;
    TypeDef type2 = ...;
    if (new SigComparer().Equals(type1, type2)) {
        Console.WriteLine("They're equal");
    }
    
    // Use the type equality comparer in a Dictionary
    Dictionary<IType, int> dict = new Dictionary<IType, int>(TypeEqualityComparer.Instance);
    TypeDef type1 = ...;
    dict.Add(type1, 10);
    
    // Compare a TypeRef with a System.Type
    TypeRef type1 = ...;
    if (new SigComparer().Equals(type1, typeof(int))) {
        Console.WriteLine("They're equal");
    }