Click here to Skip to main content
15,868,141 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.7K   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

 
QuestionApplying to a non WPF Application Pin
deo cabral23-Jun-15 0:34
professionaldeo cabral23-Jun-15 0:34 
AnswerRe: Applying to a non WPF Application Pin
Igor Ladnik23-Jun-15 1:45
professionalIgor Ladnik23-Jun-15 1:45 
QuestionThanks a lot Pin
Ahmed Safan30-May-15 0:07
professionalAhmed Safan30-May-15 0:07 
AnswerRe: Thanks a lot Pin
Igor Ladnik1-Jun-15 20:10
professionalIgor Ladnik1-Jun-15 20:10 
QuestionIs it possible to be implemented with vs2010 under winXP? Pin
Member 114161227-May-15 5:43
Member 114161227-May-15 5:43 
AnswerRe: Is it possible to be implemented with vs2010 under winXP? Pin
Igor Ladnik11-May-15 8:01
professionalIgor Ladnik11-May-15 8:01 
GeneralRe: Is it possible to be implemented with vs2010 under winXP? Pin
Member 1141612211-May-15 16:59
Member 1141612211-May-15 16:59 
QuestionProblem applying it to own code Pin
Lucas MDO2-May-15 5:22
Lucas MDO2-May-15 5:22 
Hi Igor, I'm not sure if this article is still alive, but it's worth a try.

First of all, congrats. That`s a nice work you did there. Thumbs Up | :thumbsup:

Although it seems a good technique, I'm having problems to apply it to my own code.
I'm using VS2013 (I also tried on VS2012) running as admin with Windows 7. Your source and demo code is working as a charm. When I try to do the same with my own code, it throws an exception.
While I was trying to debug the code, I tracked it down to the AppDomain.CurrentDomain.AssemblyResolve event. For an unknown reason to me, the try statement is skipped and instead it jumps to the MessageBox inside the catch. For this reason, the assm.GetType returns null.
I can send you the exception message if it would be of any help.
Could you provide me some help? If it suits you, you can reply to my email lucas@cemi.eng.br

Regards, Lucas.
AnswerRe: Problem applying it to own code Pin
Igor Ladnik11-May-15 7:59
professionalIgor Ladnik11-May-15 7:59 
Questionproblem in windows 8.1 and vs2013 Pin
Member 797770226-Mar-15 17:58
Member 797770226-Mar-15 17:58 
AnswerRe: problem in windows 8.1 and vs2013 Pin
Igor Ladnik26-Mar-15 18:08
professionalIgor Ladnik26-Mar-15 18:08 
GeneralRe: problem in windows 8.1 and vs2013 Pin
Member 797770226-Mar-15 18:25
Member 797770226-Mar-15 18:25 
GeneralRe: problem in windows 8.1 and vs2013 Pin
Igor Ladnik26-Mar-15 19:03
professionalIgor Ladnik26-Mar-15 19:03 
GeneralNot Great Protection Pin
User 126615717-Jan-15 22:22
User 126615717-Jan-15 22:22 
GeneralRe: Not Great Protection Pin
Igor Ladnik18-Jan-15 0:07
professionalIgor Ladnik18-Jan-15 0:07 
QuestionI vote for 5. And I have a question... Pin
Shenjun Wu14-Jan-15 7:59
Shenjun Wu14-Jan-15 7:59 
AnswerRe: I vote for 5. And I have a question... Pin
Igor Ladnik15-Jan-15 2:10
professionalIgor Ladnik15-Jan-15 2:10 
Questionhow can I build your source code and use it ? Pin
mohsen.keshavarzi12-Nov-14 21:31
mohsen.keshavarzi12-Nov-14 21:31 
GeneralMy vote of 5 Pin
mohsen.keshavarzi11-Nov-14 0:20
mohsen.keshavarzi11-Nov-14 0:20 
GeneralRe: My vote of 5 Pin
Igor Ladnik11-Nov-14 19:44
professionalIgor Ladnik11-Nov-14 19:44 
Questionhelp question Pin
mohsen.keshavarzi10-Nov-14 23:26
mohsen.keshavarzi10-Nov-14 23:26 
AnswerRe: help question Pin
Igor Ladnik11-Nov-14 19:43
professionalIgor Ladnik11-Nov-14 19:43 
QuestionCompatibility issues with Win 8.1 Pin
David Jackil6-Oct-14 23:27
David Jackil6-Oct-14 23:27 
AnswerRe: Compatibility issues with Win 8.1 Pin
Igor Ladnik11-Nov-14 19:50
professionalIgor Ladnik11-Nov-14 19:50 
GeneralMy vote of 5 Pin
Agent__00714-Sep-14 20:35
professionalAgent__00714-Sep-14 20:35 

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.