20、逆向工程中的算法分析:常见的加密算法识别(XOR, AES, Base64)、简单的算法逆向、使用 CE 的“查找二进制”功能。

说实话,很多刚入门的同学一听到“算法逆向”就头大。觉得那是数学天才干的事。其实不然。在游戏逆向里,我们碰到的加密算法,翻来覆去就那么几种。XOR、Base64、AES,这三个占了九成以上。今天我就带你把这三种算法扒个底朝天。

20.1 为什么游戏里会有加密?

你想想看,游戏客户端和服务端之间要传数据。比如你的血量、金币、装备ID。如果这些数据是明文传输的,那外挂作者直接抓包改数据就行了。所以游戏开发者会给数据“穿件马甲”——这就是加密。

但游戏里的加密有个特点:它必须能被快速解密。因为游戏要实时运算,不能让你等三秒才算出伤害值。所以那些特别复杂的非对称加密(RSA之类)在游戏里很少见。反而是XOR、Base64这种轻量级算法用得最多。

核心观点: 游戏加密的目的是“防君子不防小人”。它不是为了绝对安全,而是为了增加逆向门槛。你只要认得这些算法,就能绕过去。

20.2 最常见的三种加密算法识别

20.2.1 XOR(异或)加密——最基础也最阴险

XOR加密的原理简单到令人发指:明文 XOR 密钥 = 密文。解密也一样:密文 XOR 密钥 = 明文。我在项目中遇到过好几次,新手看到一串乱码就慌了,其实它就是XOR了一下。

怎么识别XOR?

  • 在CE里搜索一串数值,发现地址附近的数据看起来“有规律地乱”。比如全是0xAA、0x55这种重复字节。
  • 用CE的“查找二进制”功能,搜一段已知明文对应的密文。如果搜不到,试试把明文XOR一个常见密钥(如0xFF、0xAA)再搜。
  • 在反汇编里看到 XOR 指令频繁出现,而且操作数是一个循环变量或固定值。

我的小技巧: 如果你不确定密钥是什么,试试用0x00做XOR。因为任何数XOR 0x00都等于它自己。如果解密出来是乱码,再试0xFF。这两个是最常见的单字节密钥。

举个简单的例子。假设游戏里血量是100,存成整数。如果它被XOR了0xAA,那内存里看到的就是 100 XOR 0xAA = 0x0E。你直接搜100搜不到,但搜0x0E就能找到。这就是CE“查找二进制”的用武之地。

// 一个典型的XOR解密循环
for (int i = 0; i < dataLength; i++) {
    data[i] ^= 0xAA;  // 单字节密钥
}

20.2.2 Base64——不是加密,是编码

Base64严格来说不算加密。它只是一种编码方式。但很多游戏用它来传输二进制数据,比如装备的序列化数据。我见过有人把Base64当加密用,还加了个自定义字母表——这就有点意思了。

怎么识别Base64?

  • 看到一串由 A-Za-z0-9+/= 组成的字符串,结尾可能有等号。这就是标准Base64。
  • 如果字母表被换过,比如 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 这个顺序变了,那就是自定义Base64。
  • 在内存里搜索时,Base64字符串通常连续出现,长度是4的倍数。

我曾经踩过的坑: 有一次我盯着内存里一串Base64看了半天,觉得它就是普通文本。结果发现游戏用了自定义字母表,把 + 换成了 -,把 / 换成了 _。这就是所谓的“URL-safe Base64”。如果你用标准解码器去解,出来的全是乱码。

20.2.3 AES——高级货,但也不是无迹可寻

AES是正经的对称加密算法。游戏里如果用了AES,那说明开发者确实花了心思。但AES有个致命弱点:密钥必须藏在客户端里。你只要找到密钥,就能解密一切。

怎么识别AES?

  • 在反汇编里看到 AESKEYGENASSISTAESENCAESDEC 这些指令。这是硬件加速的AES。
  • 如果没硬件指令,那就是软件实现。搜索 S-box 的常量表(一个256字节的数组)。几乎所有AES实现都包含这个表。
  • 数据长度是16字节的倍数。AES的块大小固定为16字节。

实战经验: 我逆向过一款手游,它用AES-128加密了所有网络包。密钥就硬编码在so文件里,是一个32字节的十六进制字符串。我用CE的“查找二进制”功能,直接搜这个字符串的前8个字节,就定位到了密钥位置。然后写了个脚本,把所有包都解密了。

20.3 简单的算法逆向实战

光说不练假把式。我们来看一个真实场景。假设游戏里有一个“金币”数值,你搜到了它的内存地址。但每次修改后,数值会立刻被服务器校验并改回来。这说明客户端和服务器之间有加密通信。

步骤一:定位加密函数

用CE附加游戏进程。在金币地址上下“写入断点”。当游戏修改金币时,CE会断在写入指令处。往上翻汇编代码,找到调用这个写入函数的上级函数。通常加密就在这个上级函数里。

步骤二:识别加密算法

在上级函数里,你会看到一些循环和位运算。如果看到 XORSHLSHR 这些指令,八成是XOR或自定义加密。如果看到查表操作(mov al, [table + ecx]),那可能是AES或某种自定义S-box。

步骤三:用CE“查找二进制”验证

假设你怀疑游戏用XOR加密了金币。你当前金币是1000,十六进制是 0x000003E8。你在内存里看到的值是 0x0000F618。那么 0x000003E8 XOR 0x0000F618 = 0x0000F5F0。这个 0xF5F0 可能就是密钥(或者密钥的一部分)。

你再用CE搜索 F5 F0(二进制模式),看看能不能找到这个密钥在内存中的位置。如果能找到,恭喜你,你找到了密钥。接下来写个Lua脚本,每次游戏读取金币时,自动用这个密钥解密,显示真实值。

-- CE Lua脚本示例:自动解密XOR加密的金币
function decryptGold(encryptedValue, key)
    return encryptedValue ~ key  -- Lua的XOR操作符是~
end

-- 在读取金币时调用
local encrypted = readInteger(0x12345678)
local realGold = decryptGold(encrypted, 0xF5F0)
print("真实金币: " .. realGold)

20.4 使用CE的“查找二进制”功能

这个功能很多人没用过。其实它特别强大。普通搜索只能搜数值,比如搜100。但“查找二进制”可以搜字节序列。比如你搜 AA BB CC DD,CE就会在内存里找这四个连续字节。

什么时候用?

  • 你知道加密后的数据长什么样,但不知道它在哪。
  • 你想定位一个固定的密钥或常量表。
  • 你想验证自己的解密算法是否正确。

操作步骤:

  1. 在CE里点击“Memory View”(内存视图)。
  2. Ctrl + B 打开“查找二进制”对话框。
  3. 输入你要搜索的字节序列。比如 01 02 03 04。支持通配符 ??,表示任意字节。
  4. 点击“OK”开始搜索。结果会显示在内存视图中。

我的习惯: 搜索时尽量用 ?? 通配符。比如你知道密钥的前两个字节是 AA BB,但后面不确定。那就搜 AA BB ?? ?? ?? ??。这样能过滤掉大部分无关数据。

20.5 知识体系总览

下面这张图总结了本章的核心逻辑。你可以把它当作一个检查清单。遇到加密数据时,按这个流程走一遍,基本不会漏。

算法逆向识别流程 发现加密数据 数据长度特征? 4的倍数 16的倍数 Base64 候选 AES 候选 检查字母表是否标准 搜索 = 号结尾特征 搜索S-box常量表 查找AES指令 XOR 候选 用CE“查找二进制”验证 成功识别!编写脚本

20.6 避坑指南

最后,我把自己这些年踩过的坑总结一下。你遇到了能少走弯路。

我曾经犯过的错:

  • 看到Base64就以为是加密,结果浪费半天去解密。其实它就是编码,直接解码就行。
  • 搜AES的S-box时,没注意字节序。有些实现是反着存的,你搜 63 7C 77 搜不到,试试 7C 63 77
  • 用CE“查找二进制”时,忘了勾选“Writable”(可写)选项。结果搜出来的全是代码段,根本改不了。
  • XOR密钥是动态生成的。你以为找到了固定密钥,结果游戏重启后密钥变了。这种情况要追一下密钥的生成逻辑。

嗯,算法逆向其实没那么玄乎。你只要认得这三种最常见的,再配合CE的“查找二进制”功能,九成以上的游戏加密你都能搞定。剩下的那一成,无非是这三种的变种或组合。万变不离其宗。


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