Breaking

Reversing and Patching .NET Binaries with Embedded References

Lately I’ve been analyzing a .NET binary that was quite interesting. It was a portable binary that shipped without any third-party dependencies. I started looking at the .NET assembly with ILSpy and noticed that there was not that much code that ILSpy found and there were a lot of references to classes/methods that were neither in the classes identified by ILSpy nor were they part of the .NET framework.

At some point I was going through everything that ILSpy displayed about the binary, including the resources which were looking very interesting:

Embedded references displayed in ILSpy

There were several resources with a naming pattern like: “costura.[NAME].dll.compressed”. After some research on the Internet I stumbled across Costura, an add-in for Fody that allows to “Embed references as resources”. The project provides an infrastructure to embed references (e.g. DLLs required by a binary) in a binary’s resources as a method of merging assemblies. It’s an interesting approach that has previously been described by Jeffrey Richter in this blog post. The actual implementation of utilizing embedded references is done with the Mono Cecil library that is used to inject some trampoline-like code in module initializers. Looking at the module initializer with ILSpy I saw that it indeed calls an interesting method called AssemblyLoader.Attach():

Module initializer added by Costura

The AssemblyLoader.Attach() method (shown in the screenshot below) is basically a replication of the code that Jeffrey Richter has shown in his blog post (linked above). AssemblyLoader.ResolveAssembly() is the callback function added by Costura that handles resolving of assemblies that are not available at the time of calling other methods that are part of a dependency.

Attach method with ResolveAssembly callback

Whenever the binary calls a method that is not implemented in the binary itself, the AssemblyLoader.ResolveAssembly() function will be called. It checks whether the required assembly is available (e.g. as part of the .NET framework) and if not, it will try to find it in the resources and loads it if it’s available. One step of resolving embedded resources is also decompression of the deflated DLL files. This is a step that ILSpy does not do by default and therefore the embedded resources are not displayable. In order to decompile the code one would have to extract and save the resource in ILSpy, decompress it manually and add the resulting DLL file back to ILSpy.

In order to make reversing binaries with embedded resources in ILSpy easier I’ve written a simple plugin that extracts, decompresses and adds embedded DLLs by Costura to ILSpy:

Loading embedded resources with CosturaPlugin for ILSpy

After loading the embedded references, the actual libraries are loaded in ILSpy, including all references in the binary that requires the libraries:

Embedded resources in ILSpy after loading

Now it is easy to reverse the binary with all the third party dependencies that have been included as embedded references. However, at some point it might be required to patch the binary, or even some methods in the embedded references. After loading the embedded resources that is possible in ILSpy with the Reflexil plugin, but in order to use them in the binary it is required to remove the module initializer so that the embedded references will be ignored when running the binary. This can be achieved with the “Remove Costura Module Initializer” feature:

Removing Costura module initializer

This feature will remove the AssemblyLoader.Attach() call in the module initializer and the resulting assembly will be saved to a new file. After loading the new file in ILSpy, the module initializer will be empty again:

Patched module initializer

The code of the CosturaPlugin and a pre-compiled DLL is available on GitHub. Feedback and suggestions for improvement are welcome. This is the first time I’ve written a plugin for ILSpy but is was quite easy. Using the TestPlugin as a base is a good starting point. There is not much documentation available so reading the code for ILSpy and Mono Cecil is the best way to get started. Cecil is actually a quite decent library and ILSpy is basically just a frontend for Cecil, so that made it pretty straight forward to implement a plugin.

Comments

  1. It does not work in ILspy 4.0.0.0.

    Error (s) loading plugin: PU2018.Plugin
    System.IO.FileLoadException: Unable to load the file or assembly ‘PU2018.Plugin’ or one of its dependencies. The assembly manifest definition does not match the reference to the assembly. (Exception from HRESULT: 0x80131040)
    Filename ‘xxxx.Plugin’

  2. Luckily ive found this. thanks for that & appreciated!
    The normal packer/unpacker ive tried didnt succedd, so i was looking for a solution that didnt require writing own c# code to decompress.

    Suggestion: dnspy could need such addon/functionality too (and mho its way more comfortable to use than ilspy) , maybe the plugin can be ported?

    Since i didnt see any other tool, adding support for that format to open source packers would be cool too. Or, a plugin for totalcommander? 🙂

  3. You are welcome, good to see that it’s useful for other’s. Implementing it for other tools should be fairly simple because the plugin isn’t that fancy. I haven’t heard of dnSpy yet, thanks for the hint! I may look into it to see if I can easily port the plugin.

Comments are closed.