C# 编译生成的 `.exe` 或 `.dll` 包含元数据和 IL 代码,如果不做处理,任何人都可以使用工具(如 dnSpy)看到你的源代码。
本页讲解如何保护代码(混淆/加密)以及如何还原代码(反编译/脱壳)。
由于无法将 .NET 完全编译为不可逆的机器码(除非使用 Native AOT,但有局限性),最主流的保护方式是混淆。
| 工具名称 | 类型 | 说明 | 推荐指数 |
| ConfuserEx | 开源/免费 | 功能强大,支持重命名、控制流混淆、字符串加密。虽然原作者已停更,但社区版(ConfuserEx 2)很活跃。 | ★★★★★ |
| Dotfuscator | 商业/免费版 | Visual Studio 自带社区版。重命名功能稳定,但高级功能收费。 | ★★★ |
| .NET Reactor | 商业 | 强大的加壳和混淆工具,支持原生代码生成。 | ★★★★ |
| VMProtect | 商业 | 极其昂贵,将 IL 转为虚拟指令集,极难破解,但会影响性能。 | ★★★★ |
在使用工具(如 ConfuserEx)时,通常会应用以下策略:
这是一个 ConfuserEx 的配置文件示例,用于保护 `MyApp.exe`。
<project outputDir="Confused" baseDir="." xmlns="http://confuser.codeplex.com"> <rule pattern="true" preset="maximum"> <!-- 强制启用字符串加密 --> <protection id="resources" /> <protection id="anti tamper" /> <protection id="constants" /> <protection id="ctrl flow" /> <protection id="anti dump" /> <protection id="anti debug" /> <protection id="invalid metadata" /> <protection id="ref proxy" /> <protection id="rename" /> </rule> <module path="MyApp.exe" /> </project>
“解密”在代码层面通常指反编译(查看源码)和去混淆(还原被混淆的代码)。
| 工具名称 | 作用 | 说明 |
| dnSpy | 查看/调试 | 神器。可以直接查看源码、断点调试编译好的程序,甚至直接修改 IL 代码并保存。 |
| ILSpy | 查看 | 官方推荐的开源反编译工具,查看效果好,不支持调试。 |
| de4dot | 去混淆 | 专门用于对抗混淆工具。它可以自动识别 ConfuserEx、Dotfuscator 等,并尝试还原字符串和类名。 |
如果代码被 ConfuserEx 混淆过,dnSpy 看到的代码会是一团乱麻。此时需要先用 de4dot 清洗。
命令行操作步骤:
# 假设 de4dot.exe 和 被混淆的程序 confused.exe 在同一目录 # 1. 自动检测混淆类型并清洗 de4dot.exe confused.exe # 2. 执行后,会生成一个 confused-cleaned.exe # 3. 将 confused-cleaned.exe 拖入 dnSpy,代码将变得可读
为了直观理解,以下展示一段代码在源码、混淆后、反编译后的样子。
public class Auth { public bool CheckPassword(string input) { string secret = "SuperSecret123"; if (input == secret) { Console.WriteLine("Login Success"); return true; } return false; } }
经过 ConfuserEx 处理后,变量名消失,字符串被加密,流程被打乱。
// 类名和方法名被修改 public class A { public bool b(string a) { // 字符串 "SuperSecret123" 变成了动态解密函数调用 string text = <Module>.c(12345); // 控制流混淆,正常的 if 变成了 switch 循环 int num = 0; while(true) { switch(num) { case 0: if (a == text) { num = 2; continue; } num = 3; continue; case 2: // "Login Success" 也被加密 Console.WriteLine(<Module>.c(12346)); return true; case 3: return false; } break; } return false; } }
虽然变量名无法恢复成原始的 `secret` (因为编译时已丢失),但流程控制和字符串会被还原。
public class Class0 { public bool Method0(string string_0) { string text = "SuperSecret123"; // 字符串已还原 if (string_0 == text) // 流程已还原为正常的 if { Console.WriteLine("Login Success"); return true; } return false; } }