From the very early days of .NET, it had become a common place that managed binaries were easy target for reverse engineering via reflection. And indeed, Lutz Roeder’s Reflector and many reflectors that followed, crack .NET executables, politely asking user in what language he/she'd like to get original code. In fact, most of the developers have accepted the situation when everybody can effortlessly reveal original code by its binaries, as inevitable toll for benefits of managed code usage. Some people argue that most of the managed executables may be placed on server side, out of customer reach. However, nowadays code placed on public cloud may require protection also. Other people try some palliative means for code protection, like e.g., obfuscators. But obfuscation is a problematic solution since in this case code is normally readable with reflectors, application logic is preserved intact and, even more important, may disrupt reflection usage by the application itself.
The article presents an approach illustrated with code sample, that allows developers to thwart direct attempts to reveal their managed code by reflection of their executable files.
This work is based on previous articles and code of other authors. Some of them are referenced at the end of the article. But this list is definitely not exhaustive - many more articles and posts (especially published on Stack Overflow site) were used.
The first .NET book I read back in year 2002 (Applied Microsoft .NET Framework Programming by Jeffry Richter  – an excellent one!) already provides a good hint to a better protection of managed code. In chapter 20 entitled CLR Hosting, AppDomains, and Reflection the author explains that .NET Common Language Runtime (CLR) constitutes a COM object and as such may be hosted by any Windows unmanaged application. This fact suggests a way for protection of managed application against reflectors. This approach allows .NET developers to achieve level of their code protection comparable with such for unmanaged application.
The main idea of this work is following: in order to deceive reflector, managed code is started with CLR object embedded to unmanaged application. Actually the entire managed code runs in unmanaged host process which binary file cannot be read with reflectors. In this article, I’d like to present the entire anti-reflect code protection algorithm and its possible implementation with emphasis on WPF applications protection.
It is important to note that protection processing does not affect useful .NET application at all. But it is much easier to implement functions like proper start of application (particularly for WPF application), load and decrypt referenced DLLs and so, from managed code. For that reason, managed LauncherLib DLL is used as a wrapper and launcher for useful application assemblies.
The processing flow is depicted here:
After useful .NET application and its DLLs have been developed, the following steps should be carried out in order to protect their code from direct reverse engineering with reflectors.
- Utility File2Chars.exe is built. This utility converts files (in our case, managed executable) to array of bytes, encrypts them if required, and creates code files with the array of bytes as read-only variable. The utility is configurable with arguments in its command line.
File2Chars utility is shown in the flow diagram as the Utility blue rectangle.
- Managed LauncherLib.dll is built. During this built, on pre-built event initial EXE and locally copied referenced DLL files of useful managed application are processed with
File2Chars utility to array of bytes. Bytes of EXE file are put to read-only variable to a newly created _UsefulAppBytes.cs file compiled as a part of LauncherLib.dll. If customer would like to encrypt referenced locally copied DLLs (usually when developed along with the useful EXE file) then
File2Chars utility is configured to do so. In our sample extension of encrypted DLL files is changed to DLLX. The DLLX files are copied to ReflectProtected directory which is working directory for the protected application. If however customer would like to use DLL without encryption (usual case for standard DLLs) then the DLL is copied to ReflectProtected directory unchanged. Configuration file [UsefulApplicationName].exe.config is also copied to ReflectProtected directory renamed to [ProtectedApplicationName].exe.config.
On post-built event File2Chars.exe utility processes newly built LauncherLib.dll and puts it as encrypted array of bytes to read-only variable to _ArrayContainer file. Thus, this variable contains encrypted LauncherLib.dll containing in its turn useful application EXE file as byte array.
- Unmanaged ProcessingLib.lib is built as a static library. It contains code responsible for creating CLR COM object and executing method of managed LauncherLib.dll.
- Finally, unmanaged ReflectProtectedApp.exe statically linked with ProcessingLib.lib, is built and placed into ReflectProtected directory. File _ArrayContainer containing byte array data is included to the built.
So, useful application EXE file undergoes the following transformations. First, it is placed as array of bytes into managed launcher library
LauncherLib, and the launcher library is built into LauncherLib.dll. This binary file is transformed to array of bytes, encrypted and placed into unmanaged code that is eventually built to final protected executable file. As for referenced DLL files, if it is decided to protect them then they are encrypted and copied to protected working directory. If DLL files are not protected (standard ones), then they are copied to protected working directory unchanged. Managed wrapper of useful application LauncherLib.dll takes care of referenced DLLs loading. For current application domain, it implements handler for
AppDomain.CurrentDomain.AssemblyResolve event. The handler finds, decrypts (if necessary) and loads the appropriate DLL.
In our sample, the above steps are performed by building Protection.sln solution. Let’s examine the entire flow of the code sample in details. Useful application in our sample is
WpfDirectoryTreeApp WPF application. The application and its referenced DLLs are placed to .\bin directory. System.Windows.Interactivity.dll is a standard referenced locally copied DLL.
WpfDirectoryTreeApp displays directory tree of a disk with Windows installation. It performs simple operations. Click on directory or file item in the tree panel switches control in main panel, and double click unfolds specific folder in the tree with writing appropriate message in log panel. The application also has a simple menu and tool bar allowing user to swap the tree and the main panels (initial value of variable defining position of tree panel is stored in the application configuration file).
In the sample utility, File2Chars.exe is configurable (to see how to configure please run it without arguments). The first argument specifies binary file (EXE or DLL) to be processed. Argument
/e indicates that output is encrypted. Here encryption is rather simple: in output array of bytes every successive n bytes are swopped with the next n ones, where
n is the last argument in the utility command line. Of course in real life more sophisticated methods of encryption may be used. Argument /r is applied to referenced DLLs. If this argument is used, then the utility placed output array of bytes into a separate file with DLLX extension in ReflectProtect output directory. Usage of File2Chars.exe utility may be seen in pre- and post-built event command lines of
LauncherLib project contains
ClsPrepare class (shared with
File2Chars) responsible for encryption/decryption operations,
UsefulAppBytes class containing
static readonly byte bts variable (that is output of “File2Chars.exe WpfDirectoryTreeApp.exe” pre-built event command), and
Launcher class which method
Launcher.Run() been called from unmanaged code, will start the useful application in memory. The latter method is the focal point of application start. The method performs the following operations:
- Extract useful application pattern (in our case this is WPF) and its main types (
WpfDirectoryTreeApp.MainWindow) from its
- Assign appropriate handler to
AppDomain.CurrentDomain.AssemblyResolve event, and
- Load main assembly of useful application from array of bytes and launch its appropriate methods (by calling
Unmanaged application ReflectProtectedApp.exe includes file _ArrayContainer containing LauncherLib.dll as encrypted array of bytes (output of post-built event command “
File2Chars.exe LauncherLib.dll /e 11” of building of
LauncherLib project). ProcessingLib.lib is statically linked to ReflectProtectedApp.exe. Method
Run() of unmanaged ProcessingLib.lib (file Processing.cpp) loads CLR COM object into memory and with its help runs managed
Launcher.Run() method from unmanaged process. The method is called by
ReflectProtectedApp main function. Method
Run() supports all version of .NET Framework.
To generate protected application and DLLXs, we need to built Protection.sln solution. For successful build please check the following: path to Protection directory should not include any blanks, and file mscorlib.tlb locates in its usual place, namely C:\Windows\Microsoft.NET\Framework\v4.0.30319 directory. If the last condition is not met then change the file's path in Processing.cpp file of ProcessingLib project.
Result of solution build will be placed in .\bin\ReflectProtected directory. File ReflectProtectedApp.exe constitutes protected executable file, whereas *.dllx files stand for encrypted referenced DLLs. System.Windows.Interactivity.dll is not encrypted since this is standard Microsoft DLL. Protected ReflectProtectedApp.exe application looks (except for icon - I intentionally assigned different icon to the protected application) and behaves exactly the same way as its unprotected analog WpfDirectoryTreeApp.exe.
Demo ZIP file contains files of .\bin\ReflectProtected directory after the build of Protection.sln solution in Release mode. We can inspect those EXE and DLLX files with a reflector and see that reflector is unable to reveal their original code.
Presented technique provides a certain level of anti-reflect managed code protection. Although .NET binaries even after this protection are still more vulnerable than normal unmanaged code, the protection makes reverse engineering is much more difficult. This technique may be improved, e.g., more sophisticated encryption algorithm may be used, and managed launcher assembly may use more tricky way to load EXE and referenced DLLs, say, to bring them from network. Similar approach may be used to pack all custom EXE, DLL and resource files (if any) to one unmanaged EXE file (see for example ).
LauncherLib may also provide facilities to support some licensing mechanism.
Size of protected EXE file is not too high compared to the original size of useful application. Managed launcher assembly and relatively small unmanaged portion increases size of protected EXE file. These two units have almost fixed size irrespectively on size of useful application.
It is probably possible to design a utility for generation of unmanaged anti-reflect protection around useful managed application. But I do not think that this is a very good idea. Normally, the author of useful application is interested in anti-reflect protection. So he/she can easily maintain dedicated solution with several projects with possibility to ad hoc adjust and tune them rather than have an "inflexible" utility.
The approach to protection of .NET executable assemblies against reflection reverse engineering is presented in the article. Protection is achieved with creation of .NET CLR COM object in unmanaged process and invocation of managed method from the CLR object. The technique prevents reflectors from direct revealing managed assemblies' original code. This protection mechanism in no way affects useful managed application. Along with code protection purposes presented technique may be used to optimized application packaging, deployment and licensing.
-  Jeffry Richter. Applied Microsoft .NET Framework Programming, 2002.
-  Richard Grimes. Developing Applications with Visual Studio.NET, 2002.
-  Ranjeet Chakraborty. Creating a host application for the .NET Common Language Runtime. CodeProject.
-  SteveLi-Cellbi. A Simple Way to Pack your .NET Code into a Single Executable. CodeProject.
-  Mattias Högström. CLR Hosting - Customizing the CLR. CodeProject.