Click here to Skip to main content
Click here to Skip to main content

How to Read in Memory Code of .NET Application Protected with Anti-Reflector

, 5 Aug 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Technique to obtain in memory code of running .NET application (even wrapped into unmanaged code for binary files anti-reflection) is presented and implemented with workable sample

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.

  1. 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 chars. Utility File2Chars.exe taken from [1] does this job being called in post-build event of CodeReaderLib project.
  2. 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.
  3. 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

License

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

Share

About the Author

Igor Ladnik
Architect
Israel Israel


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

Follow on   LinkedIn

Comments and Discussions

 
Questionhow prevent form this(read my code application from memory)? Pinmembermohsen.keshavarzi10-Dec-14 2:13 
AnswerRe: how prevent form this(read my code application from memory)? PinprofessionalIgor Ladnik10-Dec-14 2:50 
GeneralMy vote of 5 PinprofessionalSibeesh KV23-Sep-14 18:59 
GeneralRe: My vote of 5 PinprofessionalIgor Ladnik23-Sep-14 19:20 
GeneralRe: My vote of 5 PinprofessionalSibeesh KV23-Sep-14 19:27 
GeneralMy vote of 5 PinprofessionalChristian Amado12-Sep-14 4:31 
GeneralRe: My vote of 5 PinprofessionalIgor Ladnik12-Sep-14 4:53 
GeneralMy vote of 5 PinmemberR. Hoffmann11-Aug-14 0:05 
GeneralRe: My vote of 5 PinprofessionalIgor Ladnik11-Aug-14 0:41 
QuestionTo be continue... PinmemberPirks16-Aug-14 1:58 
AnswerRe: To be continue... PinprofessionalIgor Ladnik6-Aug-14 2:16 
AnswerRe: To be continue... PinmemberPirks16-Aug-14 2:20 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150123.1 | Last Updated 5 Aug 2014
Article Copyright 2014 by Igor Ladnik
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid