精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
锐英源精品开源心得,转载请注明:“锐英源www.wisestudy.cn,孙老师作品,电话13803810136。”需要全文内容也请联系孙老师。
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.
从.NET的早期开始,托管二进制文件就很容易成为通过反射进行逆向工程的目标,这已成为一个常见的地方。的确,Lutz Roeder的Reflector及其后面的许多反射器破解了.NET可执行文件,礼貌地询问用户他/她想用哪种语言来获取原始代码。实际上,大多数开发人员已经接受了这样一种情况,即每个人都可以毫不费力地通过其二进制代码显示原始代码,这是使用托管代码的好处的必然代价。有人认为,大多数托管可执行文件都可能放在服务器端,而客户无法触及。但是,如今放置在公共云上的代码也可能需要保护。其他人尝试使用一些姑息手段来保护代码,例如混淆器。
本文提供了一种通过代码示例说明的方法,该方法允许开发人员阻止通过反射其可执行文件来揭示其托管代码的直接尝试。
这项工作是基于以前的文章和其他作者的代码。本文末尾引用了其中一些。但是此列表绝对不是详尽无遗的-使用了更多文章和帖子(尤其是在Stack Overflow网站上发布)。
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.
我在2002年阅读的第一本.NET书籍(Jeffry Richter [1]所应用的Microsoft .NET Framework编程–很棒!)已经为更好地保护托管代码提供了很好的提示。在标题为CLR托管,AppDomains和反射的第20章中,作者解释说.NET公共语言运行库(CLR)构成COM对象,因此可以由任何Windows非托管应用程序托管。这一事实提出了一种保护托管应用程序免受反射器攻击的方法。这种方法使.NET开发人员可以获得与非托管应用程序相当的代码保护级别。
这项工作的主要思想如下:为了欺骗反射器,托管代码是通过将CLR对象嵌入到非托管应用程序中来启动的。实际上,整个托管代码都在非托管主机进程中运行,该主机进程无法使用反射器读取二进制文件。在本文中,我想介绍整个防反射代码保护算法及其可能的实现,重点是WPF应用程序保护。
重要的是要注意,保护处理根本不影响有用的.NET应用程序。但是,从托管代码中实现诸如正确启动应用程序(尤其是WPF应用程序),加载和解密引用的DLL等功能要容易得多。因此,托管的LauncherLib DLL用作有用的应用程序程序集的包装和启动器。
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.在开发了有用的.NET应用程序及其DLL之后,应执行以下步骤,以保护其代码免于使用反射器进行直接反向工程。
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.
托管的LauncherLib.dll已生成。在此构建过程中,将使用File2Chars 实用程序对预构建事件中的初始EXE和有用的受管应用程序的本地复制引用DLL文件进行处理,以生成字节数组。EXE文件的字节放入只读变量中,该变量是作为LauncherLib.dll的一部分编译的新创建的_UsefulAppBytes.cs文件。如果客户想加密引用的本地复制的DLL(通常与有用的EXE文件一起开发),则可以配置实用程序来这样做。在我们的示例中,加密的DLL文件扩展名更改为DLLX。DLLX文件复制到ReflectProtectedFile2Chars 该目录是受保护应用程序的工作目录。但是,如果客户希望使用不加密的DLL(通常是标准DLL),则将DLL不变地复制到ReflectProtected目录。配置文件[UsefulApplicationName] .exe.config也被复制到ReflectProtected目录,重命名为[ProtectedApplicationName] .exe.config。
在生成后事件上,File2Chars.exe实用程序将处理新建的LauncherLib.dll,并将其作为加密的字节数组放入_ArrayContainer文件的只读变量中。因此,此变量包含加密的LauncherLib.dll,而后者包含有用的应用程序EXE文件作为字节数组。
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.因此,有用的应用程序EXE文件经过以下转换。首先,将其作为字节数组放入托管的启动程序库中LauncherLib,并将启动程序库内置到LauncherLib.dll中。该二进制文件被转换为字节数组,被加密并放入非托管代码中,该代码最终构建为最终受保护的可执行文件。对于引用的DLL文件,如果决定保护它们,则将它们加密并复制到受保护的工作目录中。如果DLL文件未受保护(标准文件),则将它们原样复制到受保护的工作目录中。有用的应用程序LauncherLib.dll的托管包装负责引用的DLL的加载。对于当前的应用程序域,它为AppDomain.CurrentDomain.AssemblyResolve 事件。处理程序查找,解密(如有必要)并加载适当的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).在我们的示例中,上述步骤是通过构建Protection.sln解决方案执行的。让我们详细检查代码示例的整个流程。在我们的示例中有用的应用程序是WpfDirectoryTreeApp WPF应用程序。该应用程序及其引用的DLL放置在。\ bin目录中。System.Windows.Interactivity.dll是引用的标准本地复制DLL。WpfDirectoryTreeApp 在Windows安装中显示磁盘的目录树。它执行简单的操作。单击树面板中的目录或文件项可切换主面板中的控件,然后双击并在日志面板中写入适当的消息以展开树中的特定文件夹。该应用程序还具有一个简单的菜单和工具栏,允许用户交换树和主面板(定义树面板位置的变量的初始值存储在应用程序配置文件中)。
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.在示例实用程序中,File2Chars.exe是可配置的(要了解如何配置,请在不带参数的情况下运行它)。第一个参数指定要处理的二进制文件(EXE或DLL)。参数/e表明输出已加密。这里的加密非常简单:在输出的字节数组中,每个连续的n个字节都会被接下来的n个字节交换,其中n 是实用程序命令行中的最后一个参数。当然,在现实生活中,可以使用更复杂的加密方法。参数/ r应用于引用的DLL。如果使用此参数,则该实用程序将输出字节数组放入ReflectProtect输出目录中带有DLLX扩展名的单独文件中。File2Chars.exe的用法实用程序可以在项目的预构建和构建事件命令行中看到LauncherLib 。
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:
LauncherLib 项目包含负责加密/解密操作的ClsPrepare 类(与共享File2Chars),UsefulAppBytes 包含static readonly byte[] bts 变量的类(是“ File2Chars.exe WpfDirectoryTreeApp.exe ”预构建事件命令的输出),以及从非托管代码中调用Launcher 该方法的类Launcher.Run() 将启动在内存中有用的应用程序。后一种方法是应用程序启动的重点。该方法执行以下操作:
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.
非托管应用程序ReflectProtectedApp.exe包含文件_ArrayContainer,该文件包含LauncherLib.dll作为加密的字节数组(LauncherLib项目的构建后生成的事件命令“File2Chars.exe LauncherLib.dll /e 11”的输出 )。ProcessingLib.lib静态链接到ReflectProtectedApp.exe。非托管ProcessingLib.lib的Run() 方法(文件Processing.cpp)将CLR COM对象加载到内存中,并在其帮助下Launcher.Run() 从非托管进程运行托管方法。该方法由ReflectProtectedApp 主函数调用。Run()方法 支持所有版本的.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.要生成受保护的应用程序和DLLX,我们需要构建Protection.sln解决方案。为了成功构建,请检查以下内容:Protection目录的路径不应包含任何空格,并且文件mscorlib.tlb位于其通常位置,即C:\ Windows \ Microsoft.NET \ Framework \ v4.0.30319目录。如果不满足最后一个条件,则更改ProcessingLib项目的Processing.cpp文件中的文件路径。
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.解决方案构建的结果将放置在。\ bin \ ReflectProtected目录中。文件ReflectProtectedApp.exe构成受保护的可执行文件,而* .dllx文件代表加密的引用DLL。未加密System.Windows.Interactivity.dll,因为这是标准的Microsoft DLL。受保护的ReflectProtectedApp.exe应用程序外观(图标除外-我专门向受保护的应用程序分配了不同的图标),其行为与未受保护的模拟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.演示ZIP文件包含在Release模式下构建Protection.sln解决方案之后的。\ bin \ ReflectProtected目录文件。我们可以使用反射器检查那些EXE和DLLX文件,并查看反射器无法显示其原始代码。
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.提出的技术提供了一定程度的防反射托管代码保护。尽管即使使用了这种保护,.NET二进制文件仍然比普通的非托管代码更容易受到攻击,但是这种保护使反向工程更加困难。可以改进此技术,例如,可以使用更复杂的加密算法,并且托管启动器程序集可以使用更棘手的方式来加载EXE和引用的DLL,例如,将其从网络中删除。可以使用类似的方法将所有自定义EXE,DLL和资源文件(如果有)打包到一个非托管EXE文件中(例如,参见[4])。LauncherLib 可能还提供支持某些许可机制的设施。
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.
与有用的应用程序的原始大小相比,受保护的EXE文件的大小不太高。托管启动器程序集和相对较小的非托管部分会增加受保护的EXE文件的大小。无论有用的应用程序大小如何,这两个单元的大小都几乎固定。
有可能设计一种实用程序,以在有用的托管应用程序周围生成非托管的防反射保护。但是我认为这不是一个好主意。通常,有应用的作者对防反射保护感兴趣。因此,他/她可以轻松地维护具有多个项目的专用解决方案,并且可以进行临时调整和调整它们,而无需使用“死板的”实用程序。
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.
本文介绍了保护.NET可执行程序集以防止反射逆向工程的方法。通过在非托管进程中创建.NET CLR COM对象并从CLR对象调用托管方法来实现保护。该技术可防止反射器直接显示托管程序集的原始代码。这种保护机制绝不会影响有用的托管应用程序。连同代码保护目的一起,提出的技术可用于优化应用程序的打包,部署和许可。