Introduction
Every .NET developer is familiar with reflector tools allowing us to restore original code by its managed executables. But sometimes developers want to protect their code from being read with reflectors. Some anti-reflector code protection mechanisms are based on the idea to place encrypted managed code into unmanaged binaries and then decrypt .NET modules in memory. I also published article on this topic at CodeProject in October 2013 [1]. The article was warmly received by fellow CodeProjecteers (thanks to all of you!) This approach solves the stated problem: it protects managed executable files from being reflected over. However, several readers of this article reasonably assumed that although the code was not readable directly from binary files, it probably would be possible to access managed code running in memory. But no practical implementation of such an approach has been suggested so far. This article and accompanying code present a solution to the problem, i.e., read managed code in memory.
Before I proceed with anti-(anti-reflector) stuff, I'd make an important note. The anti-reflector protection which we are going to "break" here, is still useful and should not be underestimated. To obtain code from memory, one should somehow start application and place module to be reflected into memory (start of the application does not guarantee that code of interest will be loaded into memory since code may be designed to prevent some modules from loading into memory without passing certain security barriers). This may bring additional complexity to protection breaking.
Background
In short, the solution of the problem is the following: after the start of target application, another application injects a piece of code into the target one using remote thread technique [2]. The injected code constitutes unmanaged DLL. Its DllMain()
method on DLL_PROCESS_ATTACH
case ("reason") starts a .NET CLR COM object implementing interface ICorRuntimeHost
[1]. Then the CLR object loads reflector managed DLL capable of accessing running target code, reflects it over and submits result as Microsoft Intermediate Language (MSIL) code. The reflector DLL is loaded as char (byte) array. After loading reflector, the CLR object invokes method of the loaded managed DLL which actually retrieves MSIL code of the target application with reflection.
Design
The system operations are depicted in the diagram below:
Running unmanaged target process contains protected managed .NET application. The goal of the system is to read code of the protected .NET application. To attain this, the following steps are carried out.
- Managed CodeReaderLib.dll is able to reflect over .NET code and retrieve its MSIL calling method
CodeReaderLib.CodeReader.Reflect()
. Transition from MethodInfo
object to MSIL is preformed using types AutoDiagrammer.ILInstruction
and AutoDiagrammer.MethodBodyReader
taken from [3]. CodeReaderLib.dll should be loaded to the unmanaged process memory as array of char
s. Utility File2Chars.exe taken from [1] does this job being called in post-build event of CodeReaderLib
project. - Unmanaged TargetPlugin.dll is design to activate CLR COM object with
ICorRuntimeHost
interface. Then CLR object loads reflector managed CodeReaderLib.dll and invokes its CodeReaderLib.CodeReader.Reflect()
method performing actual reflection and MSIL code output. These operations are performed in method PluginProc()
called by DllMain()
in DLL_PROCESS_ATTACHED
reason. - An injector process MemoryReflectorApp.exe first starts target process. In our case, this is ReflectProtectedApp.exe with its *.dllx protected DLL files described in [1]. ReflectProtectedApp.exe protects WPF application from binary files reflection. Then the injector process injects TargetPlugin.dll into target process using in-proc COM Injector.dll. The injection is performed using remote thread injection technique. Design of Injector.dll is described in details in [2].
So, the workflow is the following: the injector process with Injector COM object injects TargetPlugin.dll into target ReflectProtectedApp.exe process, then DllMain()
of TargetPlugin.dll creates CLR COM object and using it, invokes managed code reader reflector within .NET memory area. The reflector does its job and when it exits, the remote thread dies.
There is a note regarding usage of injection technique for this task. In [2] injected object continuously operates inside target process during the entire target's lifetime, dealing with its windows. To ensure this, we bothered to create injected object (either unmanaged in-proc COM or managed one in COM wrapper) subclassing target application windows and equipping injected object with inter-process communications means. For the purpose of this article, such an approach seems to be an overkill since here we need to merely reflect managed objects once, produce MSIL code and write it to a file. After this was done, we would not need to access injected object again. Of course, when protected managed application loads some of its modules on-the-fly injected reflector should be kept active during the entire life cycle of target application. In this case, more sophisticated injection technique with creation of the reflector object in the target application main thread described in [2] is required.
Code Sample
Code sample MemoryReflection.sln consists of the following projects:
- MemoryReflectorApp - Managed code starting target application ReflectProtectedApp.exe and performing TargetPlugin.dll injection to it using Injector COM object,
- Injector - In-proc COM which actually performs injection of TargetPlugin.dll into target process,
- TargetPlugin - unmanaged DLL injected into target process where it creates CLR COM object with
ICorRuntimeHost
and loads managed CodeReaderLib
assembly as array of chars to it,
- CodeReaderLib - Managed DLL which actually reflects over target application code. In its post-build event handler, it is converted to array of chars with File2Chars.exe utility, and
- File2Chars - managed utility converting a file (in our case, CodeReaderLib.dll) to an array of chars.
The solution should be built and run with MemoryReflectorApp start-up project. Result file WpfDirectoryTreeApp_MSIL.txt is generated in output directory containing MSIL code of all methods of all types of WpfDirectoryTreeApp
assembly. Of course, this is just a sample providing the methods of just one assembly.
Injection of code in a foreign process is rather sensitive operation and often is thwarted (or at least causes alert) by protection software of your computer. So please be aware of such possibility when running code sample or demo for this article. In order to test the sample successfully, you should temporary disable this protection.
Demo
The demo sample parses reflect-protected WPF application ReflectProtectedApp.exe. This target application is a result of anti-reflect protection technique described in [1]. It refers to several custom protected DLLs (files with extensions dllx) and standard System.Windows.Interactivity.dll. ReflectProtectedApp.exe uses ReflectProtectedApp.exe.config configuration file. To read code of the target application, we have to first register Injector.dll in-proc COM object with regsvr32.exe utility and then run MemoryReflectorApp.exe application. Batch file _MemoryReflectorApp_RunMe.bat performs both operations. This file should be run to operate demo. File WpfDirectoryTreeApp_MSIL.txt is generated as a result. It contains MSIL code of all methods of all types of WpfDirectoryTreeApp
assembly.
Discussion
The presented technique provides universal solution to the reflection-in-memory problem. It does not depend on the way of binary files encryption since in memory an ordinary managed code is running. To make the picture complete, it would be nice to generate code in some widely used .NET programming language, like C# or VB.NET. This is not a simple task, but it is solved in any well-known reflector (e.g. by Lutz Roeder's Reflector and ILSpy) and therefore left out the article's scope.
Conclusions
The article presents obtaining MSIL code of the running managed application. The described approach is particularly effective when the useful managed modules are "wrapped" by unmanaged code in order prevent them from being read as binary files by reflector.
References
[1] Igor Ladnik. Anti-Reflector .NET Code Protection. CodeProject
[2] Igor Ladnik. Automating Windows Applications. CodeProject
[3] Sacha Barber. 200% Reflective Class Diagram Creation Tool. CodeProject
[4] Sorin Serban. Parsing the IL of a Method Body. CodeProject