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:
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():
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.
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:
After loading the embedded references, the actual libraries are loaded in ILSpy, including all references in the binary that requires the libraries:
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:
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:
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.