Advanced Module Reading

Note

The documentation has a new home: Check it out!

Advanced users might need to configure AsmResolver’s module reader. For example, instead of letting the module reader throw exceptions upon reading invalid data, errors should be ignored and recovered from. Other uses might include changing the way the underlying PE or method bodies are read. These kinds of settings can be configured using the ModuleReaderParameters class.

var parameters = new ModuleReaderParameters();

These parameters can then be passed on to any of the ModuleDefinition.FromXXX methods.

var module = ModuleDefinition.FromFile(@"C:\Path\To\File.exe", parameters);

PE image reading parameters

.NET modules are stored in a normal PE file. To customize the way AsmResolver reads the underlying PE image before it is being interpreted as a .NET image, ModuleReaderParameters provides a PEReaderParameters property that can be modified or replaced completely.

parameters.PEReaderParameters = new PEReaderParameters
{
    ...
};

For example, this can be in particular useful if you want to let AsmResolver ignore and recover from invalid data in the input file:

parameters.PEReaderParameters.ErrorListener = EmptyErrorListener.Instance;

Alternatively, this property can also be set through the constructor of the ModuleReaderParameters class directly:

var parameters = new ModuleReaderParameters(EmptyErrorListener.Instance);

For more information on customizing the underlying PE image reading process, see Advanced PE Image Reading.

Changing working directory

Modules often depend on other assemblies. These assemblies often are placed in the same directory as the original module. However, should this not be the case, it is possible to change the path of the working directory of the resolvers.

parameters.WorkingDirectory = @"C:\Path\To\Different\Folder";

Alternatively, this property can also be set through the constructor of the ModuleReaderParameters class directly:

var parameters = new ModuleReaderParameters(@"C:\Path\To\Different\Folder");

Custom .netmodule resolvers

For multi-module assemblies, AsmResolver looks into the path stored in WorkingDirectory for files with the .netmodule extension by default. If it is necessary to change this behaviour, it is possible to provide a custom implementation of the INetModuleResolver interface.

public class CustomNetModuleResolver : INetModuleResolver
{
    public ModuleDefinition Resolve(string name)
    {
        // ...
    }
}

To let the reader use this implementation of the INetModuleResolver, set the NetModuleResolver property of the reader parameters.

parameters.NetModuleResolver = new CustomNetModuleResolver();

Custom method body readers

Some .NET obfuscators store the implementation of method definitions in an encrypted form, use native method bodies, or use a custom format that is interpreted at runtime by the means of JIT hooking. To change the way of how method bodies are being read, it is possible to provide a custom implementation of the IMethodBodyReader interface, or extend the default implementation.

Below an example of how to add support for reading simple x86 method bodies:

public class CustomMethodBodyReader : DefaultMethodBodyReader
{
    public override MethodBody ReadMethodBody(
        ModuleReaderContext context,
        MethodDefinition owner,
        in MethodDefinitionRow row)
    {
        if (owner.IsNative && row.Body.CanRead)
        {
            // Create raw binary reader if method is native.
            var reader = row.Body.CreateReader();

            // Read until the first occurrence of a ret instruction (opcode 0xC3).
            // Note: This is for demonstration purposes only, and is by no means
            // a very accurate heuristic for finding the boundaries of native
            // method bodies.

            var code = reader.ReadBytesUntil(0xC3);

            // Create native method body.
            return new NativeMethodBody(owner, code);
        }

        // Off-load to default implementation.
        return base.ReadMethodBody(context, owner, row);
    }
}

To let the reader use this implementation of the IMethodBodyReader, set the MethodBodyReader property of the reader parameters.

parameters.MethodBodyReader = new CustomMethodBodyReader();

Custom Field RVA reading

By default, the field RVA data storing the initial binary value of a field is interpreted as raw byte blobs, and are turned into instances of the DataSegment class. To adjust this behaviour, it is possible to provide a custom implementation of the IFieldRvaDataReader interface.

public class CustomFieldRvaDataReader : FieldRvaDataReader
{
    public override ISegment ResolveFieldData(
        IErrorListener listener,
        Platform platform,
        IDotNetDirectory directory,
        in FieldRvaRow fieldRvaRow)
    {
        // ...
    }
}

To let the reader use this implementation of the IFieldRvaDataReader, set the FieldRvaDataReader property of the reader parameters.

parameters.FieldRvaDataReader = new CustomFieldRvaDataReader();