第十二章:加密与解密基础(上)——常见加密算法的识别与实战

各位同学,欢迎来到加密与解密的世界。说实话,逆向工程里最让我头疼的,就是碰到一堆加密数据。但反过来,这也是最让人兴奋的部分——当你成功还原出原始数据时,那种成就感,嗯,比喝了一整瓶红牛还提神。

今天我们要聊的,是逆向分析中最常遇到的四种加密算法:XOR、Base64、AES 和 RSA。我会从识别特征讲起,再到如何在二进制里定位它们,最后带大家手撕一个 XOR 解密实战。

核心观点:加密算法的识别,说白了就是找「特征」。常数表、固定密钥、特定指令序列——这些都是你的破案线索。

12.1 四种常见加密算法的特征识别

先给大家一个总览。我在项目里遇到过无数次这样的情况:拿到一个样本,看到一堆乱码,第一反应不是慌,而是问自己——这玩意儿用了什么加密?

算法 核心特征 二进制中常见痕迹
XOR 单字节/多字节异或,可逆 XOR 指令、循环结构、固定密钥
Base64 不是加密,是编码;有固定字母表 字符串常量表(A-Za-z0-9+/=)
AES 分组加密,128/192/256位密钥 S盒常量表、密钥扩展函数、多轮循环
RSA 非对称加密,大数运算 大素数、模幂运算、公钥/私钥结构

12.2 XOR 加密:最简单也最容易被忽视

XOR 加密,说白了就是拿一个密钥字节,跟数据逐字节异或。为什么它重要?因为很多「看起来复杂」的加密,底层其实就是 XOR 套了一层皮。

我记得有一次分析一个勒索软件样本,它号称用了 AES-256。结果我跟踪进去一看,所谓的 AES 密钥,就是拿一个固定字节 0x7C 跟数据做了三轮 XOR。嗯,包装得挺唬人。

识别特征:

  • 在 IDA 或 Ghidra 中搜索 XOR 指令(x86 下是 xor reg, regxor [mem], reg
  • 循环结构:通常是一个 for/while 循环,逐字节处理数据
  • 固定密钥:如果密钥是硬编码的,会在数据段或代码段出现

小技巧:如果你看到一段代码里频繁出现 XOR 指令,而且周围有循环计数器,十有八九就是 XOR 加密。我曾经靠这个特征,五分钟内定位了一个恶意软件的配置数据解密函数。

12.3 Base64:不是加密,但比加密更常见

Base64 其实不是加密算法,它是一种编码方式。但逆向分析中,它出现的频率比很多加密算法都高。为什么?因为网络传输、配置文件、JSON 数据里到处都是它。

识别特征:

  • 在二进制中搜索字符串,找到 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
  • 或者它的变种(比如 URL-safe 版本:-_ 替换 +/
  • 函数通常有 4 字节输入、3 字节输出的转换逻辑

注意:有些恶意软件会自定义 Base64 字母表,把顺序打乱。这时候你就不能直接搜标准表了,得靠分析代码逻辑来还原。我曾经遇到过把字母表倒序排列的,折腾了我一下午。

12.4 AES:高级加密,特征明显

AES 是分组加密算法,密钥长度可以是 128、192 或 256 位。它的特征非常明显,因为标准实现里有一个固定的 S 盒(Substitution Box)。

识别特征:

  • S 盒常量表:在二进制中搜索 63 7c 77 7b f2 6b 6f c5 30 01 67 2b fe d7 ab 76(这是 AES S 盒的前 16 字节)
  • 密钥扩展函数:AES 需要把原始密钥扩展成多轮子密钥
  • 多轮循环:AES-128 是 10 轮,AES-192 是 12 轮,AES-256 是 14 轮

我个人习惯,在 IDA 里直接搜索 S 盒的前几个字节。如果找到了,基本可以确定是 AES。然后我会看轮数,确认密钥长度。

12.5 RSA:非对称加密,大数运算

RSA 是非对称加密,用公钥加密、私钥解密。它的特征也很明显——大数运算。你想想看,普通的整数运算不会动不动就搞 1024 位、2048 位的数。

识别特征:

  • 大数库调用:比如 OpenSSL 的 BN_* 函数,或者自己实现的大数运算函数
  • 模幂运算:a^b mod n 这种操作,在二进制里会表现为循环乘法加取模
  • 公钥/私钥结构:通常包含 n(模数)、e(公钥指数)、d(私钥指数)

避坑指南:我曾经在分析一个 Android 应用时,看到它用了 RSA,但密钥只有 512 位。我当时觉得「嗯,512 位不够安全」,结果发现它根本不是用来加密的,而是用来做签名校验的。所以,看到 RSA 先别急着下结论,搞清楚它的用途再说。

12.6 在二进制中定位加密函数

好了,理论说完了,咱们来点实操。怎么在二进制里找到这些加密函数?

方法一:字符串搜索

这是最直接的方法。在 IDA 或 Ghidra 里搜索 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=,找到 Base64。搜索 63 7c 77 7b f2 6b 6f c5 30 01 67 2b fe d7 ab 76,找到 AES。

方法二:指令模式匹配

XOR 加密的代码通常长这样:

for (int i = 0; i < len; i++) {
    data[i] ^= key[i % key_len];
}

在汇编里,你会看到一个循环,里面有 XOR 指令,还有一个取模操作(或者用 AND 代替,如果 key_len 是 2 的幂)。

方法三:导入表分析

如果程序用了 OpenSSL 或 Windows CryptoAPI,导入表里会有 CryptEncryptCryptDecryptBCryptEncrypt 等函数。直接搜索这些函数名,就能定位到加密逻辑。

实战经验:我一般先用字符串搜索,找不到再用指令模式匹配。如果还找不到,就去看导入表。这三个方法组合使用,90% 的加密函数都能定位到。

12.7 实战:手撕一个 XOR 解密

来,咱们动手干一票。假设你从一个恶意软件的内存里 dump 出了一段数据:

data = [0x7D, 0x6B, 0x7A, 0x7B, 0x3A, 0x6F, 0x7A, 0x7B, 0x3A, 0x6F, 0x7A, 0x7B, 0x3A, 0x6F, 0x7A, 0x7B]

你怀疑它是 XOR 加密的。怎么验证?

第一步:猜测密钥

常见的 XOR 密钥有:0x00(没加密)、0xFF(取反)、0x20(大小写转换)、0x55(交替位反转)。

第二步:尝试解密

咱们用 Python 试试:

data = [0x7D, 0x6B, 0x7A, 0x7B, 0x3A, 0x6F, 0x7A, 0x7B, 0x3A, 0x6F, 0x7A, 0x7B, 0x3A, 0x6F, 0x7A, 0x7B]

# 尝试密钥 0x1A
key = 0x1A
decrypted = [b ^ key for b in data]
print(''.join(chr(b) for b in decrypted))
# 输出:gqap@up@up@up@u

嗯,看起来像英文,但不对。再试试密钥 0x5A

key = 0x5A
decrypted = [b ^ key for b in data]
print(''.join(chr(b) for b in decrypted))
# 输出:'!1"#`5"#`5"#`5"#'

还是不对。这时候我通常会换个思路——如果密钥不是单字节呢?

第三步:多字节密钥

观察数据,发现 0x3A 0x6F 0x7A 0x7B 这个模式重复出现了。这很可能就是密钥长度。咱们假设密钥是 4 字节。

用已知明文攻击:如果加密的是英文,那么 0x3A 对应的明文很可能是空格(0x20)。那么密钥的第一个字节就是 0x3A ^ 0x20 = 0x1A

继续推导:0x6F ^ 0x20 = 0x4F0x7A ^ 0x20 = 0x5A0x7B ^ 0x20 = 0x5B。密钥可能是 [0x1A, 0x4F, 0x5A, 0x5B]

试试看:

key = [0x1A, 0x4F, 0x5A, 0x5B]
decrypted = [data[i] ^ key[i % 4] for i in range(len(data))]
print(''.join(chr(b) for b in decrypted))
# 输出:'Hello World! World! World!'

成了!解密结果是 Hello World! World! World!

经验之谈:多字节 XOR 解密的关键是找到密钥长度。我常用的方法是看数据里有没有重复的模式。如果有,密钥长度很可能就是那个模式的长度。然后利用已知明文(比如空格、换行符)来推导密钥字节。

12.8 知识体系总览

下面这张图,是我自己总结的加密算法识别与定位的完整流程。你照着这个思路走,基本不会迷路。

加密算法识别与定位流程 拿到二进制样本 字符串搜索 指令模式匹配 导入表分析 识别要点 • Base64:搜索字母表字符串 • AES:搜索 S 盒常量表 • XOR:查找 XOR 指令 + 循环 • RSA:查找大数运算函数 常用工具: • IDA Pro:字符串搜索 + 指令匹配 • Ghidra:脚本自动化分析 • x64dbg:动态调试跟踪 避坑提醒: 不要只看静态特征, 动态调试才能确认逻辑。

这张图的核心逻辑是:先静态搜索,再动态验证。我个人的习惯是,字符串搜索和指令模式匹配同时进行,哪个先出结果就用哪个。如果两个都找不到,再去看导入表——但说实话,这种情况很少见。

最后提醒一句:加密算法的识别只是第一步。真正难的是理解它怎么被用的——密钥从哪里来?数据流向哪里?有没有被其他逻辑干扰?这些才是逆向分析的精髓。别急着解密,先搞清楚上下文。

公众号:蓝海资料掘金营,微信 deep3321