Click here to Skip to main content
15,867,756 members
Articles / Programming Languages / C#

Anti-Reflector .NET Code Protection

Rate me:
Please Sign up or sign in to vote.
4.89/5 (122 votes)
26 Oct 2013CPOL10 min read 334.6K   17K   251   189
Article presents a technique allowing developers to protect their .NET code from reverse engineering by reflection of executable files

Introduction

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.

Background

The first .NET book I read back in year 2002 (Applied Microsoft .NET Framework Programming by Jeffry Richter [1] – 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.

Design

The processing flow is depicted here:

Image 1

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.

  1. 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.
  2. 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.

  3. 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.
  4. 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.

Code Sample

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.

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.App and WpfDirectoryTreeApp.MainWindow) from its string argument
  • 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 private method Launcher.LaunchUsefulApp()).

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

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.

Discussion

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 [4]). 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.

Conclusions

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.

References

  • [1] Jeffry Richter. Applied Microsoft .NET Framework Programming, 2002.
  • [2] Richard Grimes. Developing Applications with Visual Studio.NET, 2002.
  • [3] Ranjeet Chakraborty. Creating a host application for the .NET Common Language Runtime. CodeProject.
  • [4] SteveLi-Cellbi. A Simple Way to Pack your .NET Code into a Single Executable. CodeProject.
  • [5] Mattias Högström. CLR Hosting - Customizing the CLR. CodeProject.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Israel Israel


  • Nov 2010: Code Project Contests - Windows Azure Apps - Winner
  • Feb 2011: Code Project Contests - Windows Azure Apps - Grand Prize Winner



Comments and Discussions

 
Question5 Pin
peterkmx8-Nov-13 4:12
professionalpeterkmx8-Nov-13 4:12 
AnswerRe: 5 Pin
Igor Ladnik8-Nov-13 4:18
professionalIgor Ladnik8-Nov-13 4:18 
QuestionJust an idea which is not working in reality. Pin
Member 38100502-Nov-13 5:15
Member 38100502-Nov-13 5:15 
AnswerRe: Just an idea which is not working in reality. PinPopular
Igor Ladnik2-Nov-13 5:57
professionalIgor Ladnik2-Nov-13 5:57 
GeneralVery interesting article. Pin
AnatolyMay29-Oct-13 16:05
AnatolyMay29-Oct-13 16:05 
GeneralRe: Very interesting article. Pin
Igor Ladnik29-Oct-13 18:00
professionalIgor Ladnik29-Oct-13 18:00 
QuestionSee also... Pin
Petr T.29-Oct-13 8:05
Petr T.29-Oct-13 8:05 
AnswerRe: See also... Pin
Igor Ladnik29-Oct-13 18:00
professionalIgor Ladnik29-Oct-13 18:00 
Thank you for your good words about the article.
QuestionMy vote of 5 Pin
liron.levi29-Oct-13 5:12
professionalliron.levi29-Oct-13 5:12 
AnswerRe: My vote of 5 Pin
Igor Ladnik29-Oct-13 7:21
professionalIgor Ladnik29-Oct-13 7:21 
QuestionMay I ask you a question? Pin
Member 979673228-Oct-13 19:40
Member 979673228-Oct-13 19:40 
AnswerRe: May I ask you a question? Pin
Igor Ladnik28-Oct-13 20:28
professionalIgor Ladnik28-Oct-13 20:28 
GeneralRe: May I ask you a question? Pin
Member 979673228-Oct-13 21:41
Member 979673228-Oct-13 21:41 
QuestionI am using VS2010 Pin
Mike 200528-Oct-13 1:49
Mike 200528-Oct-13 1:49 
AnswerRe: I am using VS2010 Pin
Member 979673228-Oct-13 19:45
Member 979673228-Oct-13 19:45 
AnswerRe: I am using VS2010 Pin
Member 979673228-Oct-13 19:49
Member 979673228-Oct-13 19:49 
Questioncould not found the source file _ArrayContainer Pin
knightluffy27-Oct-13 2:13
knightluffy27-Oct-13 2:13 
AnswerRe: could not found the source file _ArrayContainer Pin
Igor Ladnik27-Oct-13 2:28
professionalIgor Ladnik27-Oct-13 2:28 
General... And I can't download anything at all Pin
RAND 45586628-Oct-13 3:08
RAND 45586628-Oct-13 3:08 
GeneralIf you do not have the source to remove the link ... Pin
RAND 45586630-Oct-13 7:00
RAND 45586630-Oct-13 7:00 
GeneralRe: If you do not have the source to remove the link ... Pin
Igor Ladnik30-Oct-13 8:00
professionalIgor Ladnik30-Oct-13 8:00 
GeneralRe: If you do not have the source to remove the link ... Pin
RAND 45586631-Oct-13 7:55
RAND 45586631-Oct-13 7:55 
GeneralMy vote of 5 Pin
Florian Rosmann26-Oct-13 23:31
Florian Rosmann26-Oct-13 23:31 
GeneralRe: My vote of 5 Pin
Igor Ladnik27-Oct-13 0:54
professionalIgor Ladnik27-Oct-13 0:54 
QuestionIt is very helpful!!! Pin
sskorokh26-Oct-13 22:40
sskorokh26-Oct-13 22:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.