模块 77 - 暴力破解密钥技术
恶意软件开发课程 - 暴力破解密钥技术
模块 77 - 暴力破解密钥技术#
暴力破解密钥技术#
简介#
在初级模块中,我们演示了 Payload 的加密与解密,并给出了一个警告:不要将加密密钥直接以明文形式存储在二进制文件中。如果密钥是明文存储的,分析者可以轻而易举地提取它。
💡 初学者提示:为什么要爆破找回密钥?
类比理解: 想象你有一副保险箱(Payload),你手里有一串 256 把外形一模一样的钥匙(256 种可能的密钥因子)。
- 常规方式:你在保险箱旁边贴了一张纸条说:“只有红色的那把是真正的钥匙”。分析师进屋一读纸条,直接就打开了保险箱。
- 爆破方式:你没有留下任何提示。当你的“管家”(程序)需要开锁时,他在现场飞速地尝试这 256 把钥匙,直到门打开为止。
- 效果:分析师进屋后,只看到一堆没标记的钥匙和上锁的箱子。在不运行程序进行动态跟踪的情况下,他很难一眼判断究竟哪把钥匙才是真的。
一个改进方案是使用“密钥的密钥”进行多重保护,并在运行时解密。而为了彻底避免在二进制文件中硬编码任何密钥,我们可以采用 暴力破解 (Brute-forcing) 的方式在运行时动态还原密钥。
本模块将演示一种基于 XOR 的解密算法,程序必须通过暴力猜解来获取正确的解密密钥。
1. 密钥加密过程:提示字节 (Hint Byte)#
为了实现有效的密钥爆破,加密和解密函数需要一个 提示字节 (Hint Byte)。只要知道加密前后某一个特定位置字节的值,解密过程就变得可行。在本例中,我们选择 第一个字节 作为提示字节。
举例说明:如果提示字节为 0xBA,经过加密后变成了 0x71。那么解密函数逻辑就是:不断尝试不同的爆破因子,直到 0x71 经计算重新变回 0xBA 映射。一旦匹配成功,就说明当前找到的因子就是正确的解密密钥。

2. 生成受保护的密钥 (生成端逻辑)#
GenerateProtectedKey 函数接收一个提示字节,并将其作为明文密钥的首字节。然后,它使用一个在运行时随机生成的保护字节 b 对整个密钥进行加密。
/*
- HintByte: 将被存储为密钥首字节的提示字节
- sKey: 要生成的密钥大小
- ppProtectedKey: 接收加密后的动态密钥的缓冲区指针
*/
VOID GenerateProtectedKey(IN BYTE HintByte, IN SIZE_T sKey, OUT PBYTE* ppProtectedKey) {
srand(time(NULL));
// 'b' 是用于保护“主密钥”的爆破因子,范围 0x01 - 0xFF
BYTE b = (rand() % 0xFF) + 0x01;
PBYTE pKey = (PBYTE)malloc(sKey);
PBYTE pProtectedKey = (PBYTE)malloc(sKey);
if (!pKey || !pProtectedKey)
return;
srand(time(NULL) * 2);
// 密钥的第一位就是我们的提示字节 HintByte
pKey[0] = HintByte;
// 随机生成密钥的剩余部分
for (int i = 1; i < sKey; i++){
pKey[i] = (BYTE)rand() % 0xFF;
}
printf("[+] 随机生成的保护因子 b : 0x%0.2X \n\n", b);
printf("[+] 原始密钥内容 : ");
PrintHex(pKey, sKey); // PrintHex 是一个打印十六进制数组的辅助函数
// 使用 XOR 算法和因子 'b' 对密钥进行加密
// 注意这里我们加入了一个偏移量 i 增加复杂度
for (int i = 0; i < sKey; i++){
pProtectedKey[i] = (BYTE)((pKey[i] + i) ^ b);
}
// 导出加密后的密钥
*ppProtectedKey = pProtectedKey;
// 释放原始明文密钥内存
free(pKey);
}c3. 密钥爆破过程 (运行端/解密端)#
由于加密“受保护密钥”的因子 b 并没有存储在任何地方,解密函数必须“猜”出它的值。它会对加密密钥的首字节(即加密后的提示字节)进行循环尝试,直到逆向计算出的结果等于原始的 HintByte。
逻辑演示:
// 循环尝试 0-255
if (((EncryptedKey[0] ^ b) - 0) == HintByte)
// 找到了!当前的 b 就是正确的保护因子
else
// 不对,b++ 继续试下一个c4. 爆破与解密函数实现#
BruteForceDecryption 需要传入与生成端一致的 HintByte。
/*
- HintByte : 生成端使用的那个提示字节
- pProtectedKey : 存储在二进制文件中的加密后的密钥
- sKey : 密钥大小
- ppRealKey : 接收爆破还原后的真实密钥的指针
*/
BYTE BruteForceDecryption(IN BYTE HintByte, IN PBYTE pProtectedKey, IN SIZE_T sKey, OUT PBYTE* ppRealKey) {
BYTE b = 0;
PBYTE pRealKey = (PBYTE)malloc(sKey);
if (!pRealKey)
return NULL;
while (1){
// 利用提示字节进行匹配测试。
// 如果满足公式,说明我们找到了还原密钥所需的 b 值
if (((pProtectedKey[0] ^ b) - 0) == HintByte)
break;
b++; // 因子自增,由于字节溢出,它会在 0-255 之间循环
}
// 既然找到了正确的 b,现在执行解密过程
for (int i = 0; i < sKey; i++){
pRealKey[i] = (BYTE)((pProtectedKey[i] ^ b) - i);
}
*ppRealKey = pRealKey;
// 返回找到的因子 b (可选)
return b;
}c5. 演示效果#
下图展示了生成加密密钥的过程:

下图展示了在运行时成功进行暴力破解并还原密钥的过程:

🎯 总结#
在本模块中,我们学习了:
- 动态密钥还原: 理解了为什么不存储密钥(或密钥的保护因子)能有效对抗静态特征提取。
- 提示字节策略: 掌握了如何通过已知的“部分真理”推导出“全部真相”的高级解密逻辑。
- 迫使动态分析: 这种技术强制分析师必须运行程序并在调试器中跟踪逻辑,这时我们之前学过的反调试和反虚拟机技术就能发挥最大的联动效果。
📚 下一步学习
下一个模块将探讨 Module 78 - MalDev Academy Tool - KeyGuard。我们将学习如何使用官方工具自动化这一复杂的暴力破解加固过程。