7 种 .NET 代码防反编译的方法






3.09/5 (10投票s)
如果您即将面向国际市场发布用 .NET 编写的软件,那么您应该考虑保护您的代码和知识产权。本文概述了 7 种方法,用于保护您的 .NET 代码免受反编译和其他恶意攻击。
引言
如果您要将软件提供给国际用户,并且您的软件是用 .NET 编写的(.NET 代码尤其容易反编译),那么您应该考虑保护您的代码。您可以通过多种方式保护您的代码,包括混淆、修剪、资源加密和字符串编码。在本文中,我们将向您展示如何仅使用一个工具——SmartAssembly——来保护您的知识产权免受反编译、盗窃和修改,共 7 种不同的方法。
混淆
混淆是一种经典的用于使代码难以阅读的代码保护技术。混淆会将您的类和方法的名称更改为不可读或无意义的字符,从而增加他人理解您代码的难度。
SmartAssembly 提供类型/方法名称篡改和字段名称篡改两种选择。
混淆类型和方法名称
SmartAssembly 为类型名称和方法名称提供三种混淆级别。
ASCII 字符 | 使用 ASCII 字符重命名类型和方法。例如,类型 FullScreenSplash 可能会被混淆为 #dc。 注意:如果您希望在混淆后能够解码堆栈跟踪,则必须选择此选项。 |
Unicode 不可打印字符 | 使用 Unicode 不可打印字符重命名类型和方法。例如,类型 FullScreenSplash 可能会被混淆为 U+1D11E(这是一个不可打印的字符)。 |
Unicode 不可打印字符和高级重命名算法 | 使用 Unicode 不可打印字符重命名类型和方法,并对多个同名项进行重命名。例如,类型 FullScreenSplash 和 FontStyle 都会被混淆为 U+017D。 |
以下示例显示了名称篡改前后的代码
混淆前
public ClientLicence GetLicence(ItemType type);
public bool IsFileInProject(string fileName);
public void ResolveAssemblyReferences();
public void Save();
private void SetMemento(Properties memento);
public void Start(bool withDebugging);
private void StartBuild(ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink);
混淆后
无法显示不可打印字符。在此示例中,不可打印字符用星号 (*) 表示。
public ‑.‑ *(‑.‑);
public bool *(string);
public void *();
public void *();
private void *(‑.‑);
public void *(bool);
private void *(‑.‑,‑.‑);
要检查您的代码是否已正确混淆,您可以使用 Red Gate 的 .NET Reflector(一个用于反编译 .NET 可执行文件的工具)。
混淆字段名称
同样,为了让您更精确地控制混淆的强度,SmartAssembly 为重命名字段名称提供了三种不同的方案。
一对一重命名方案 | 更改所有类中的所有字段,使其具有不同的名称。字段名称在一个程序集中是唯一的。 注意:如果您希望能够从堆栈跟踪中检索原始字段名称,则必须选择此选项。 |
标准重命名 | 更改字段名称,使其在一个类中是唯一的;但是,在程序集中其他类中经常使用相同的名称。例如,#a:string,#b:boolean,#c:string,#d:boolean。 |
高级重命名 | 更改字段名称,但它在一个类中不是唯一的,并且会被重复使用。不同类型的字段将具有相同的名称。例如,#a:string,#a:boolean,#b:string,#b:boolean。 |
控制流混淆
除了重命名代码的关键部分之外,您还可以使用控制流混淆。这项技术会更改方法内部的代码,将其转换为复杂的、纠缠不清的控制结构(意大利面条式代码)。在进行控制流混淆后,您可以像以前一样执行应用程序,但其他人分析您的代码的难度大大增加,反编译器几乎不可能重建原始源代码。
控制流混淆可防止大多数程序反编译代码。例如,尝试使用 .NET Reflector 反编译控制流混淆的代码会得到
private static void Main()
{
// This item is obfuscated and cannot be translated.
}
字符串编码
托管软件将所有字符串存储在一个地方,并以清晰的结构进行存储。通过跟踪对这些字符串的引用,即使在混淆之后,也有可能了解您代码的用途。这些字符串可能会泄露重要信息,例如密码、SQL 查询、序列号或登录信息。字符串编码通过编码来保护这些字符串。
下面的示例显示了对程序集中三个字符串进行编码的效果
字符串编码前
"Fred.gate@hotmail.com"
"p@ssw0rd1"
"licensingUrl=http://licensing.red-gate.com/licensingactivation.asmx"
字符串编码后
"FNRFk1TTJZM016VTPQ==.bmV1dSBzcGVjaWZpZWQ9sbGVjdGlvbi4=L"
引用动态代理
如果您在程序集外部进行调用,这些调用在字符串编码和控制流混淆之后仍然会在您的代码中可见。例如,如果您调用一个消息框,即使启用了字符串编码和控制流混淆,仍然可以在反编译的应用程序中找到该调用。通过跟踪此类外部调用,他人仍有可能理解您代码的某些部分。
引用动态代理是 SmartAssembly 提供的一项独特的保护功能,它允许您通过将对外部方法、属性或字段的调用替换为对代理的调用来隐藏代码中的大多数此类调用。使用这种类型的保护,您可以极大地增加他人找到类似试用期过期对话框等调用的难度,而这些对话框可能会无意中提供入侵的线索。
修剪
修剪会自动扫描您的软件,并移除在运行时永远不会执行的任何代码。这包括移除非元数据,例如事件、属性和方法参数的名称,这些名称会给他人提供有关特定类型和方法用途的线索。
资源压缩与加密
此功能会压缩和加密您的托管资源。这可以减小程序集的大小,并增加代码的理解难度。
依赖项合并
依赖项合并允许您选择特定的 DLL,然后将它们合并到一个可执行文件中,从而带来性能优势,因为您不必加载大量 DLL 文件。合并后,依赖项与主程序集不可分割。
依赖项合并本身并不是一项保护功能,但是,如果您只有一个程序集而不是几个较小的 DLL,那么混淆和修剪代码会更容易。(如果您想在不合并的情况下混淆多个程序集,则需要创建多个 SmartAssembly 项目。)
结论
在本文中,我们展示了如何使用 SmartAssembly 通过混淆代码、隐藏大多数对外部方法的调用、编码字符串、移除不必要的代码以及压缩和加密资源来保护您的知识产权。
要尝试在您自己的软件上使用 SmartAssembly,只需从 Red Gate 的网站下载 14 天免费试用版。