我目前正在尝试使用聊天消息包对旧在线游戏的解密算法进行逆向工程,因为它包含易于识别的文本。
我使用数据包嗅探器从服务器获取接收到的数据包(已加密):
5e 00 09 32 3c c1 6e b5 ae be 90 4a 70 8f b7 3d
3a 81 c5 3a f9 11 a6 06 36 3d f3 68 a3 dd 72 a3
ff e1 c3 e9 12 28 d7 c5 a0 f1 ce 38 35 59 19 b6
85 90 76 23 42 af 50 6f 66 52 ec ad f9 da 61 3f
5d 09 ee 8d dd 9e ff ee 7d 27 0f 5c 1e df ba 30
de d0 c8 8c 1b 93 1d 53 66 13 98 ff 29 db
前 4 个字节是已知且未加密的:
5E 00是大小,这里09 32是94。
是大端的 OPCODE:这里是 2354。接下来是加密的有效载荷。
知道加密数据包后,我使用 Cheat Engine 在 winsocket 接收函数之后设置断点,然后搜索存储数据包的缓冲区。
需要注意的重要事项:显然,存储收到(加密)数据包的缓冲区也将存储解密后的数据包。
由于包含加密数据包的缓冲区也包含解密数据包,我可以肯定地说,这必须是解密该值的函数。
解密后的数据包如下所示(最后一位包含聊天消息,存储在用户名之间的某处):
5e 00 09 32 00 00 00 00 4d 00 6e 60 00 00 00 00
06 0a 2c 00 00 00 74 00 65 00 73 00 74 00 63 00
68 00 61 00 72 00 31 00 00 00 91 12 ad 14 5e 75
00 00 cf 00 00 00 00 00 28 cd b8 69 fc f7 91 12
aa f1 07 4d 00 00 cf 00 00 00 00 00 10 74 65 73
74 5f 6d 65 73 73 61 67 65 5f 31 32 33 00
然后我使用 Ghidra 来检查处理解密部分的具体指令——这是我目前卡住的地方,不知道如何继续,因为我对这个话题没有深入的了解。
undefined4 decrypt_package(int type,int param_2,int param_3,int param_4,int param_5)
{
undefined4 success;
int counter;
if ((param_3 == 0) || (param_4 == 0)) {
s = 0;
}
else {
// Old encryption function which only used byte ^ 255
// I assume it's still in here, because the code wasn't cleaned up.
if (type == 0) {
counter = 0;
while (counter < param_5) {
*(byte *)(param_3 + counter) = *(byte *)(param_4 + counter) ^ 0xff;
counter = counter + 1;
}
}
else { // Don't know when this is used - at least not for decryption
if (type == 1) {
param_2 = 0x48473c;
counter = 0;
while (counter < param_5) {
*(byte *)(param_3 + counter) = *(byte *)(param_4 + counter) ^ (byte)((uint)param_2 >> 8);
param_2 = ((uint)*(byte *)(param_3 + counter) + param_2) * 0x2ba339 + 0x2cad2b5;
counter = counter + 1;
}
}
else {
// This block of code is called for the decryption part.
if (type == 2) {
counter = 0;
while (counter < param_5) {
*(byte *)(param_3 + counter) = *(byte *)(param_4 + counter) ^ (byte)((uint)param_2 >> 8)
;
param_2 = ((uint)*(byte *)(param_3 + counter) + param_2) * 0x8e9a99 + 0x685b24;
counter = counter + 1;
}
}
}
}
success = 1;
}
return success;
}
(type == 0的注意事项:游戏刚发布时,只使用了简单的255的异或作为加密,这显然后来停产了)
根据我的理解,代码执行以下操作(假设 param_2 是数据包的缓冲区?):
- 循环缓冲区,只要 counter < param_5 (我假设这是要解密的有效负载的长度?)
- 将字节与一个值进行异或(需要解密的第一个字节将在位置 5,所以我假设 param_3 将是一种跳过前 4 个字节的偏移量。)
- 无论解密是否有效,都返回一个布尔值(不知道为什么它被声明为未定义)。
下面我还贴出了这块代码的汇编代码(从while循环中的counter=0开始,for type == 2)

正如开头所说,我对这种事情没有经验。
因此,我想要一些关于我将如何从这里继续的附加信息,以便我可以用我选择的编程语言实现解密 - 上面的代码块到底在做什么?
我希望我包含了所有需要的信息。
感谢阅读(和帮助)!:)
