技术文档

.NET 加固推荐方案

.NET Framework 与 .NET 程序集的方法体加密、IL 混淆、反调试组合,以及 AOT/单文件 等发布形态的兼容性建议。

最后更新于 2025-06-06预计阅读 6 分钟

1. 适用范围

本文适用于 托管 .NET 程序集:

运行时架构典型产物
.NET Framework 4.xx86 / x64 / AnyCPU.exe、.dll
.NET 5 - 10x86 / x64 / AnyCPU.exe、.dll
ASP.NET Corex64Web 应用与依赖程序集

不适用于:

  • Unity IL2CPP 产物(已是 Native,见 Native 加固推荐方案)
  • C++/CLI 混合模式程序集(同时包含原生代码,需要单独验证)
  • NativeAOT 产物(已是原生二进制)

2. 可选能力

能力作用代价建议
压缩保护压缩程序集,减小体积启动时解压开
JIT 加密 / 方法体加密加密方法体,运行时解密后 JIT 编译首次调用有解密开销核心方法开
函数混淆打乱 IL 控制流,增加反编译成本小幅性能开销业务逻辑开
反调试检测调试器与诊断工具与开发、排障冲突发布版开
内存校验检测运行时内存修改少量开销视场景
函数虚拟化——当前 .NET 不支持

.NET 的函数保护以“混淆”和“加密”为主。GUI 函数保护表中的“虚拟化”列对 .NET 不可勾选。

3. 三档推荐方案

3.1 基础档:内部工具 / 快速验证

选项设置
压缩保护开
JIT 加密关
反调试关
内存校验关
函数保护5–10 个核心方法混淆

3.2 标准档:商业软件发布

选项设置
压缩保护开
JIT 加密开
反调试开
内存校验开
函数保护授权、支付、协议方法加密;业务逻辑混淆

3.3 强化档:高价值授权逻辑

选项设置
压缩保护开
JIT 加密开
反调试开
内存校验开
函数保护20–40 个敏感方法加密 + 关键业务方法混淆

加密的方法越多,首次调用和启动阶段的开销越明显。建议从授权、支付和协议签名方法开始。

4. 方法选择策略

4.1 优先加密

  • 许可证解析、签名校验、试用期判断
  • 支付回调验签、订单状态机
  • 协议序列化 / 反序列化中的签名与密钥派生
  • 核心业务算法、规则引擎
  • 反调试与完整性校验自身的方法

4.2 优先混淆

  • 普通业务逻辑、状态机、工作流
  • 字符串和常量密集的配置解析
  • 不希望暴露调用关系的模块

4.3 谨慎处理

  • 程序入口、模块初始化、静态构造函数:会影响启动
  • 高频调用的工具方法:加密后的首次 JIT 开销会放大
  • 依赖反射、dynamic、序列化特性的类型:混淆可能改变名称
  • async / await、迭代器、LINQ 表达式树:涉及编译器生成的状态机
  • P/Invoke、COM Interop 边界方法
  • 第三方库、NuGet 包:优先保护自己的程序集

5. 发布形态与兼容性

5.1 ReadyToRun(R2R)

R2R 会把部分方法预编译为原生代码。方法体加密依赖运行时解密与 JIT,R2R 方法可能绕过该流程。

建议:

  • 需要方法体加密时,发布包关闭 R2R,或只对未预编译的方法启用加密。
  • 必须使用 R2R 时,先验证加密方法是否仍被正确解密。

5.2 NativeAOT

NativeAOT 产物是原生二进制,不再有 IL 和 JIT。应改用 Native 加固推荐方案,不要对 AOT 产物套用 .NET 方法加密。

5.3 单文件发布(Single-file)

单文件发布会把依赖打包进一个宿主。保护前建议:

  • 先验证保护工具能否正确解析单文件结构。
  • 优先保护主程序集,再考虑依赖包。
  • 解包目录、AppContext.BaseDirectory 相关逻辑需要回归。

5.4 Trimming

裁剪会移除未引用的元数据和方法。保护可能引用这些元数据,导致运行时找不到方法。

建议:

  • 保留裁剪描述文件(ILLink.Descriptors.xml)中对保护相关类型的保留规则。
  • 保护后执行完整功能回归,尤其是反射和序列化路径。

5.5 Unity

后端处理方式
IL2CPP按 Native 方案保护生成的 GameAssembly.dll / libil2cpp.so

Unity 注意事项:

  • 不要直接修改 Managed 目录中由引擎生成且校验过的文件,先验证加载顺序。
  • 资源与配置的保护与代码保护分开处理。
  • Android / iOS 构建还需要处理对应平台的签名与包结构。

5.6 ASP.NET Core / 服务端

  • 优先保护业务程序集,不要保护框架自带的 Microsoft.* 程序集。
  • 发布方式(框架依赖 / 自包含 / 单文件 / AOT)决定可用能力。
  • 容器环境中反调试可能误判,建议通过配置或环境变量关闭。

5.7 强名称与签名

  • 保护会改变程序集字节,强名称签名会失效。
  • 需要强名称的场景,保护后重新签名,或在加载策略中处理验证。
  • 发布包 Authenticode 签名应在保护之后执行。

6. 推荐发布流程

Release 构建(关闭 R2R,保留 PDB)
        ↓
功能自测(原始产物)
        ↓
保护(压缩 + 方法加密 + 混淆 + 反调试)
        ↓
重新签名(强名称 / Authenticode)
        ↓
打包安装程序
        ↓
干净环境回归测试
        ↓
灰度发布

配套建议:

  • 保留每次发布的 .sugo 配置和客户端版本号。
  • 记录哪些方法被加密 / 混淆,便于崩溃定位。
  • 崩溃日志中保留方法名或 token,避免全部脱敏。

7. 验证清单

  • 原始程序集可正常运行
  • 保护后启动、登录、核心功能正常
  • 反射、序列化、依赖注入、插件加载正常
  • async / 迭代器 / LINQ 路径正常
  • 自动更新与增量升级正常
  • 强名称 / Authenticode 签名有效
  • 在干净机器上安装并运行
  • 杀毒软件与 EDR 不误报
  • 启动耗时与内存符合预期
  • 已保留原始程序集与 PDB

8. 常见问题

8.1 保护后提示找不到方法或类型

可能原因:

  • 混淆重命名了反射中使用的名称。
  • 裁剪移除了保护依赖的元数据。
  • 方法体加密与 R2R / AOT 冲突。

处理:

  • 对反射使用的类型添加保留规则。
  • 关闭 R2R 或减少加密方法范围。
  • 先用原始程序集确认功能正常,再逐项开启保护定位。

8.2 保护后启动变慢

  • 压缩会增加启动解压时间。
  • 方法体加密会在首次调用时解密并 JIT。
  • 减少加密方法数量,或把加密集中在非启动路径。

8.3 JIT 加密后程序直接退出

  • 确认运行时版本与目标框架匹配。
  • 确认没有使用 NativeAOT / R2R 预编译。
  • 检查是否有其他混淆器、代码保护工具同时处理同一程序集。
  • 用错误转储和日志定位具体方法。

8.4 强名称验证失败

  • 保护后重新签名。
  • 或在受控环境关闭强名称验证(不推荐用于生产)。
  • 确认签名用的密钥与发布流程一致。

8.5 能否与第三方混淆器一起使用

不建议对同一程序集重复混淆。多个工具修改 IL 和元数据,容易产生不可预测的冲突。确需组合时,按“先混淆、后加密、最后签名”的顺序小范围验证。

9. 相关文档

这份文档有帮助吗?欢迎反馈文档问题或补充场景。提交文档反馈