news 2026/7/7 4:12:03

.NET 程序保护实战系列02-字符串加密

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
.NET 程序保护实战系列02-字符串加密

1. 反编译器的"核武器":字符串搜索

使用 dnSpy 打开一个未保护的 .NET 程序,按 Ctrl+F 搜索 "license"、"http"、"password"——几乎所有的关键信息都藏在字符串里。字符串加密是保护的第一道防线,目的就是让攻击者无法通过字符串搜索快速定位关键代码。


2. 整体流程:从 Ldstr 到 GetString

StringEncryptionStep 的核心逻辑

public class StringEncryptionStep : IProtectionStep { public string Name => "字符串加密"; public int Order => 10; public bool IsEnabled(ProtectOptions options) => options.EncryptStrings; public void Inject(ProtectionContext context) { // 1. 注入 Constant 运行时类型到目标模块 // 2. 创建编码器(根据 StringMode: DelegateProxy/XorWithJunk/MultiLayer) // 3. 替换 Mutation.Crypt 占位符为真实解密 IL // 4. 内联 ID 解码到 GetString 方法 // 5. 在 <Module>.cctor 开头插入 Constant.Initialize() 调用 // 6. 用不可见 Unicode 重命名所有注入成员 // 7. 标记注入成员为不可重命名 } public void Execute(ProtectionContext context) { // 1. 扫描所有方法,收集 Ldstr 指令的字符串 // 2. 编码字符串到加密缓冲区 // 3. XOR 块加密缓冲区 // 4. 替换每个 Ldstr 为 GetString(encodedId) 调用 // 5. 可选:加密整数常量 } }

3. 编码器模式:三种加密策略

// DelegateProxy 模式 — 在解密和调用者之间插入委托层 typeof(Func<uint,string>).GetConstructor(new[] { typeof(object), typeof(IntPtr) }) // 静态分析只能看到 Invoke 委托,无法追踪到 DecodeString // XorWithJunk 模式 — 追加随机垃圾字节 // 解密时返回正确子串,提取工具会被伪造内容误导 // MultiLayer 模式 — 双层加密 // 外层 XOR 块加密 + 内层字节置换,使用不同的密钥

4. XOR 块加密与 xorshift32 密钥演化

// 生成 16 个 uint 的解密密钥 static uint[] GenerateKey(uint seed) { var key = new uint[0x10]; uint n = seed; for (int i = 0; i < 0x10; i++) { n ^= n >> 12; n ^= n << 25; n ^= n >> 27; key[i] = n; } return key; } // 加密字符串数据 static uint[] Encrypt(uint[] data, uint seed) { var key = GenerateKey(seed); // 从种子生成密钥 int n = 0; for (int i = 0; i < data.Length; i++) { // 每 16 个 uint 更换一次密钥 uint k = key[n++ % key.Length]; key[n % key.Length] = (data[i] ^ k); // XOR 加密 data[i] ^= k; } return data; }

核心特点:

  • xorshift32 周期 2^32-1,性能极高
  • 每次保护使用不同的种子(随机选择某个方法入口点的 RVA)
  • 相同字符串在不同保护中产生完全不同的密文

5. ID 编码:让索引看起来像随机数

// 为每个 decoder 生成唯一的 (k1, k2) 编码密钥对 int k1 = random.Next(1, int.MaxValue); int k2 = random.Next(); // 编码:encodedId = (realId * k1 + k2) & 0x7FFFFFFF uint Encode(int id) => (uint)((id * k1 + k2) & 0x7FFFFFFF); // 解码逻辑被内联到 GetString 方法中: // realId = (encodedId - k2) * modInverse(k1) & 0x7FFFFFFF

没有 ID 编码的GetString(0)GetString(1)GetString(2)... 会让攻击者轻易推断出字符串的顺序。编码后变成GetString(0x8F3A2B1C)GetString(0x2E1D5C9A)...——看起来全是随机值。


6. 不可见 Unicode 命名:注入代码的隐身术

// 使用零宽 Unicode 字符作为注入成员的名称 // U+200B: Zero Width Space // U+200C: Zero Width Non-Joiner // U+200D: Zero Width Joiner // U+2060: Word Joiner // 注入后将类的原始名称替换为不可见字符组成的字符串 // 在 dnSpy 类视图中,这些成员看起来是"空"的 var invisibleName = GenerateInvisibleName(random); typeDef.Name = invisibleName; methodDef.Name = invisibleName;

同时标记注入成员为不可重命名,防止被后续混淆步骤改名后无法调用:

// 标记:此成员不可被改名 MarkAsNotRenameable(clonedMethod); // ObfuscationStep 中检查 bool CanRename(IDnlibDef def) => !IsMarkedAsNotRenameable(def);

7. 整数常量加密:顺手保护数值

// ldc.i4 12345 → 拆分为: // ldc.i4 (12345 ^ key) // ldc.i4 key // xor foreach (var instr in body.Instructions) { if (instr.OpCode == OpCodes.Ldc_I4) { int val = (int)instr.Operand; int key = random.Next(); instr.Operand = val ^ key; // 加密值 // 在下一行插入 key 和 xor InsertAfter(instr, Instruction.Create(OpCodes.Ldc_I4, key), Instruction.Create(OpCodes.Xor)); } }

8. 运行时 Constant 类:占位符驱动的动态模板

运行时Constant类的核心 —— 利用Mutation占位符机制,在保护时动态替换关键值:

internal static class Constant { // 占位符 —— 保护时会被 MutationHelper 替换为真实值和 IL // Mutation.KeyI0 → 替换为 buffer 长度 // Mutation.KeyI1 → 替换为 seed // Mutation.Placeholder(...) → 替换为数组初始化 IL // Mutation.Crypt(...) → 替换为真实解密循环 IL static Func<uint, string> _fn; // 委托代理层 static byte[] _buf; // 解密后的字符串缓冲区 public static void Initialize() { // 以下占位符在保护时被替换为真实值 uint len = (uint)Mutation.KeyI0; // → 真实数据长度 uint seed = (uint)Mutation.KeyI1; // → 真实种子 uint[] enc = Mutation.Placeholder(new uint[len]); // → 真实密文数组 uint[] key = GenerateKey(seed); for (int i = 0; i < len; i++) enc[i] = Mutation.Crypt(enc[i], key[i % 16]); // → XOR 解密循环 // 将解密后的数据复制到字节缓冲区 _buf = new byte[enc.Length * 4]; Buffer.BlockCopy(enc, 0, _buf, 0, _buf.Length); _fn = DecodeString; // 委托绑定 } // 公开入口 — 非泛型签名,模糊特征 public static string GetString(uint id) { return _fn(id); // 通过委托间接调用解码 } // 实际解码 — 根据 id 从缓冲区提取字符串 static string DecodeString(uint id) { // id 首先经过编码解码:realId = (id - k2) * invK1 uint realId = Mutation.Placeholder(id); // → 真实解码 IL // 从 _buf[offset] 读取 UTF-8 字符串 int offset = BitConverter.ToInt32(_buf, (int)realId * 4); int length = BitConverter.ToInt32(_buf, offset); return Encoding.UTF8.GetString(_buf, offset + 4, length); } }

Mutation占位符类:

public static class Mutation { // 16 个 key 占位字段 — 保护时替换为 ldc.i4 <真实值> public static int KeyI0, KeyI1, KeyI2, KeyI3, KeyI4, KeyI5, KeyI6, KeyI7, KeyI8, KeyI9, KeyI10, KeyI11, KeyI12, KeyI13, KeyI14, KeyI15; // 占位方法 — 保护时替换为数组初始化指令序列 public static T Placeholder<T>(T val) => val; // 占位方法 — 保护时替换为 Crypt IL 实现 public static uint Crypt(uint val, uint key) => 0; }

9. 相比 ConfuserEx 的改进

特性ConfuserEx我们的实现
压缩LZMA 压缩直接 XOR 加密(更轻量,更快)
GetString 方法泛型Get<T>(int)— 签名特征明显非泛型GetString(uint)— 更隐蔽
注入成员命名保留原始名称 "Constant"不可见 Unicode 名称
ID 编码无 — 直接传递索引(k1, k2) 编码密钥对
委托代理DelegateProxy 模式
整数常量加密支持 Ldc_I4/I8 XOR 拆分
安全检查验证调用方程序集
参数类型保护支持 I4/I8/U4/U8/R4/R8
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/7 4:11:40

云手机技术原理、API 代码实战与商用产品选型

标签&#xff1a;# 云手机 #ARM 虚拟化 #Python 开发 #移动自动化 #云计算 难度&#xff1a;中级 | 适用人群&#xff1a;后端开发、自动化测试、手游运维、云计算爱好者一、前言随着 ARM 虚拟化、云端串流技术不断成熟&#xff0c;云手机已经成为移动自动化测试、游戏托管、远…

作者头像 李华
网站建设 2026/7/7 4:11:26

工业AI落地的致命短板:不是算法,是数据

如果一家制造企业今天告诉你"我们要用AI做预测性维护"&#xff0c;你最该追问的不是"你们用了什么模型"&#xff0c;而是"你们的训练数据从哪来&#xff1f;" 这听起来像个技术细节&#xff0c;但它决定了90%以上的工业AI项目是真实落地&#xf…

作者头像 李华
网站建设 2026/7/7 4:11:08

《面试八股文》之 Kafka 21卷,全文背诵

作为在消息中间件中拥有神一样地位的 kafka&#xff0c;你真的了解它吗&#xff1f;夺命 21 问&#xff0c;全文背诵版来啦~1.什么是消息中间件&#xff1f;2.kafka 是什么&#xff1f;有什么作用&#xff1f;3.kafka 的架构是怎么样的&#xff1f;4.Kafka Replicas是怎么管理的…

作者头像 李华
网站建设 2026/7/7 4:10:52

2026企业级AI Agent平台推荐六大产品实测含Aionclaw多行业落地选型参考指南

前言 2026 年企业数字化转型进入AI Agent规模化落地阶段&#xff0c;各类智能体平台面向小微企业、中型企业、集团公司、跨境商家、制造工厂、研发团队形成差异化供给。不同产品技术架构、适配赛道存在明显区分&#xff0c;通用全栈、开源底座、工业自动化、合规管控、电商服务…

作者头像 李华
网站建设 2026/7/7 4:08:20

abichecker 安全最佳实践:确保 ABI 检查过程的安全可靠

abichecker 安全最佳实践&#xff1a;确保 ABI 检查过程的安全可靠 【免费下载链接】abichecker An abi checker tool 项目地址: https://gitcode.com/openeuler/abichecker 前往项目官网免费下载&#xff1a;https://ar.openeuler.org/ar/ ABI&#xff08;应用程序二进…

作者头像 李华