在软件开发领域,代码保护是一个重要的议题。开发者经常需要保护自己的代码不被恶意分析或修改。一种常见的方法是使用反射工具来恢复原始代码。然而,有些开发者希望保护自己的代码不被反射工具读取。为此,他们采用了一些反反射代码保护机制,这些机制基于将加密的托管代码放入非托管二进制文件中,然后在内存中解密.NET模块。本文将介绍一种解决方案,即使用远程线程技术将代码注入到目标应用程序中,以读取其内存中的托管代码。
本文介绍的方法是在2013年10月在CodeProject上发表的一篇文章的基础上进行的。那篇文章受到了读者的热烈欢迎,它解决了如何保护托管可执行文件不被反射的问题。然而,一些读者合理地假设,尽管代码不能直接从二进制文件中读取,但可能仍然可以访问在内存中运行的托管代码。尽管如此,目前还没有提出任何实际的实现方法。本文和附带的代码将展示如何读取内存中的托管代码。
设计
系统操作的流程如下:
- 首先,启动目标应用程序。
- 然后,使用远程线程技术将代码注入到目标应用程序中。
- 注入的代码是一个非托管DLL。
- 该DLL的DllMain方法在DLL_PROCESS_ATTACH情况下启动一个.NETCLRCOM对象,实现ICorRuntimeHost接口。
- 然后,CLR对象加载一个能够访问运行中目标代码的反射器托管DLL,并将其结果作为Microsoft Intermediate Language (MSIL)代码提交。
实现步骤
为了实现上述目标,需要执行以下步骤:
- 编写一个能够反射.NET代码并获取其MSIL的Managed CodeReaderLib.dll。
- 使用File2Chars.exe工具将CodeReaderLib.dll加载到非托管进程内存中。
- 设计一个非托管的TargetPlugin.dll,它激活CLRCOM对象,并加载CodeReaderLib.dll。
- 编写一个注入器进程MemoryReflectorApp.exe,它首先启动目标进程,然后注入TargetPlugin.dll。
代码示例
代码示例MemoryReflection.sln包括以下项目:
- MemoryReflectorApp - 托管代码,启动目标应用程序ReflectProtectedApp.exe并执行TargetPlugin.dll注入。
- Injector - 实际执行TargetPlugin.dll注入到目标进程的进程内COM。
- TargetPlugin - 注入到目标进程的非托管DLL,它创建CLRCOM对象并加载托管的CodeReaderLib程序集。
- CodeReaderLib - 托管DLL,实际反射目标应用程序代码。在它的后构建事件处理程序中,使用File2Chars.exe工具将其转换为字符数组。
- File2Chars - 管理工具,将文件(在本例中为CodeReaderLib.dll)转换为字符数组。
演示
演示示例解析了反射保护的WPF应用程序ReflectProtectedApp.exe。这个目标应用程序是反反射保护技术的成果,它引用了几个自定义保护的DLL(带有dllx扩展名的文件)和标准的System.Windows.Interactivity.dll。要读取目标应用程序的代码,首先需要使用regsvr32.exe工具注册Injector.dll进程内COM对象,然后运行MemoryReflectorApp.exe应用程序。批处理文件_MemoryReflectorApp_RunMe.bat执行这两个操作。运行此文件后,将生成WpfDirectoryTreeApp_MSIL.txt文件,其中包含WpfDirectoryTreeApp程序集的所有类型的方法的MSIL代码。
讨论
- Igor Ladnik. Anti-Reflector .NET Code Protection. CodeProject
- Igor Ladnik. Automating Windows Applications. CodeProject
- Sacha Barber. 200% Reflective Class Diagram Creation Tool. CodeProject
- Sorin Serban. Parsing the IL of a Method Body. CodeProject