找出数据包的 CRC 部分

逆向工程 艾达 加密 CRC
2021-06-21 17:14:38

目前,我正在尝试找出旧 MMO 的数据包结构(用于加密)以创建模拟器。它的官方服务器在 3 年前关闭,这个版本已经超过 11 年了。

在使用 IDA 加载原始服务器后(它需要大约 3gb 的 RAM 来加载并消耗大量 CPU,这就是我想模拟它的原因),我发现多项式是 0x8005 (32773) 并且它是“Crc16”。为了进一步证明这一点,我在加载时在内存中发现了一系列字节:

00 00 [05 80] 0F 80 0A 00 1B 80 1E 00 14 00 11 80 33 80 36 00 3C 00 39 80 28 00

并且,来自 IDA(由于某种原因,pInitial 始终为 0):

FnlApi::CCrc16::SetPolynomial(&this->__mCrc, 32773);
//////////////////////////////
v5 = FnlApi::CCrc16::CalcCrc(&this->__mCrc, pPack + 2, pLng - 2, 0);
//////////////////////////////
int result; // eax@1
const char *v5; // edx@1
__int16 v6; // bx@2
unsigned __int16 v7; // bx@2
bool v8; // zf@2

LOWORD(result) = pInitial;
v5 = pMsg;
do
{
    LOBYTE(v6) = 0;
    HIBYTE(v6) = result;
    v7 = this->__mTbl[(unsigned __int8)(*v5++ ^ BYTE1(result))] ^ v6;
    v8 = pLng-- == 1;
    result = v7;
}
while ( !v8 );
return result;

这是来自服务器的未加密数据包。

[1B 00] [00] [00 F2 2C 00 00 00] [31 32 37 2E 30 2E 30 2E 31 00 00 00 20 01 00 00 16 27]

据我了解,这是数据包“结构”:

[Packet ID] [Key (00?)] [CRC, padding?] [Actual packet]

这是来自服务器的加密数据包。

[2F 00] [74] [38 ED 2C 00 00 01] [93 85 AE 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A F3 3C EF EF F9 AE 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 9A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00]

解密后,这是(我认为):

[2F 00] [74] [38 ED 2C 00 00 01] [75 69 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 70 61 73 73 77 64 00 00 00 00 00 00 00 00 00 00 00 00 00 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC]

我认为这两个数据包的结构是:

[Packet ID] [Key] [CRC, padding?] [Actual packet]

另一个使用相同结构加密的数据包:

[18 00] [42] [56 D6 07 00 00 01] [2E 75 3D D2 75 03 CC 03 CD CC 03 8E 2D CC B1 6B 9F DB 6B D1 DB 6B 4F 4F D3 73 78 C6 AA 4B DF 63 1E 4A 18 C6 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00]

解密:

[18 00] [42] [56 D6 07 00 00 01] [63 74 65 73 74 00 01 00 08 01 00 04 02 01 03 C7 0F AC C7 C4 AC C7 A3 A3 E7 5C 7F 05 CA 6D A0 77 46 64 F8 05 9E 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F]

我很抱歉这是一团糟,但我已经这样做了好几天没有实际进展。

编辑:

我目前的课程(解密工作)在这里:http : //pastebin.com/7e05AJLa

1个回答

正如怀疑的那样,CRC 是一个相当标准的 CRC16,多项式为 0x8005(反映为:0xA001)。我能找到的唯一在线计算器是:

  • BobTech(清除“反向数据字节”和“反向 CRC”复选框)
  • GHS Infotronic(链接已插入多项式和消息“123456789”)

后者对于获取数据包十六进制转储的一部分、对它们进行 CRC 校验、对结果进行思考,然后在重复实验之前更改一些位特别方便......

除了多项式 - 0x8005 而不是 0x1021 - 所有细节都与 CRC-CCITT (XModem) 相同。如果您插入 0x1021 多边形,那么您可以使用多种在线检查器来验证您自己的实现,例如Lammert Bies或 FoxPro 的内置 CRC 函数(以 0x10000 作为种子强制 0 作为初始值,例如sys(2007, "123456789", 0x10000))。

相反,有很多现成的实现,您只需将 0x8005 替换为 0x1021。我还在C找到了一个基于表的实现,它已经使用了 0x8005。

反汇编中的代码与CRC圣经第10章中的代码基本相同,也就是Ross Williams' Painless Guide to CRC Error Detection Algorithms此处为HTML版本):

r=0; 
while (len--) 
    r = (r<<8) ^ t[(r >> 8) ^ *p++];

如果您将其与我的反汇编转录(我在 C# 中做的,因为我正在 ATM 中学习)进行比较,这很明显:

static ushort crc16 (byte[] pMsg, ushort pInitial = 0)
{
    uint eax = pInitial;

    for (int i = 0; i < pMsg.Length; ++i)  // EDX == pMsg + i
    {
        uint cl = ((eax >> 8) & 0xFF) ^ pMsg[i];
        uint bx = (eax & 0xFF) << 8;

        eax = bx ^ lookup[cl];
    }

    return (ushort)eax;
}

我已经展示了一个中间版本,它仍然将寄存器作为值名称,这使得它很容易与反汇编相关联。

旁注:反汇编显示了 IDA 的古老错误,即它在本地将寄存器重命名为它们在函数入口处保存的参数的名称。

例如,为了阅读这个特定函数的反汇编,你需要在心理上用 'ecx' 代替 'this',否则它就没有意义:

    ...
    xor this, this
    mov cl, ah
    xor cl, bl

    xor ebx, ebx
    mov bh, al
    and this, 0FFh
    movzx   this, cx
    xor bx, [esi+this*2]
    ...

这简直太荒谬了。我想有人需要阅读生命范围的概念,以及为什么编译器费心将生命范围数据添加到 IDA 从中获取名称/寄存器关联的调试信息位。

为了完整起见,这里是 CRC 装备的其他部分,对于那些想要使用 C# 进行探索的人:

static ushort[] lookup;

static void initialise_lookup (ushort polynomial = 0x8005)
{
    for (uint edx = 0; edx <= 0xFF; ++edx)
    {
        uint ax = edx << 8;

        for (uint esi = 0; esi < 8; ++esi)
            if ((ax & 0x8000) != 0)
                ax = (ax << 1) ^ polynomial;
            else
                ax <<= 1;

        lookup[edx] = (ushort)ax;
    }
}

static ushort crc16 (string s, ushort initial = 0)
{
    return crc16(System.Text.Encoding.ASCII.GetBytes(s), initial);
}

这就是数据包 CRC 的原始基础。现在是敲定细节的时候了,看看是否有人狡猾地传递了 0 以外的初始值,或者结果 CRC 在插入数据包之前是否以某种方式被踩踏......另一件需要注意的事情原始 EXE 是 CRC 类很可能在程序的不同部分使用不同的多项式进行实例化。

观察数据包WPE_Plogtxt.txt显示只有第三个和第四个字节(偏移量 2 和 3)有足够的熵来作为 CRC,并且初始字是数据包的长度:

[u16 length] [u16 crc?] [5 headerish bytes] [payload]

例子:

09 00 : 01 B9 : DF 07 00 00 01
09 00 : E2 CD : 75 00 00 00 01

0B 00 : 12 AD : 10 00 00 00 01 : 95 03 
0B 00 : 14 05 : 10 00 00 00 01 : A9 9A
0B 00 : 11 2B : 10 00 00 00 01 : BA CD
0B 00 : 77 D0 : 46 00 00 00 01 : 4D 2A

0C 00 : 2D B1 : 42 00 00 00 01 : B7 D1 90
0D 00 : 04 E6 : 6D 01 00 00 01 : F1 52 ED DF
0E 00 : 50 9D : 2C 00 00 00 01 : F7 E9 94 FC 3A
0F 00 : 4C 88 : 21 01 00 00 01 : 30 EB 62 D0 C4 D0
10 00 : 81 28 : 1B 00 00 00 01 : 64 CD E6 F0 24 CD F0
11 00 : C5 FB : 31 00 00 00 01 : 75 0F 13 10 DE 0F 11 2F 
12 00 : 9E F7 : 18 00 00 00 01 : 19 D8 F5 D8 F2 01 95 D8 D8

但有时这些东西根本不适合:

13 00 : 00 00 : 03 00 00 00 00 : 00 00 0B E1 F5 05 00  00 00 00

为了快速取得进展,对大量样本进行分析会很有帮助,即直接对漂亮的胖包捕获文件进行分析。这个“PAC”格式来自哪里?

以下是我能找到的所有长度为 9 的样本,以说明为什么需要进行更多分析。几个样本仅在假定的 CRC 上有所不同。这意味着如果这些帧确实包含 CRC,那么这些帧必须引用一些更大的参考帧。

09 00 : C5 28 : 05 00 00 00 01
09 00 : CD 65 : 05 00 00 00 01
09 00 : EA A6 : 1E 00 00 00 00
09 00 : 58 29 : 2D 00 00 00 00
09 00 : CC 96 : 2D 00 00 00 00
09 00 : D0 B0 : 2D 00 00 00 00
09 00 : F8 8A : 2D 00 00 00 00
09 00 : 57 3C : 4F 00 00 00 01
09 00 : 41 47 : 5A 00 00 00 00
09 00 : 8E A2 : 5A 00 00 00 00
09 00 : DF B3 : 5A 00 00 00 00
09 00 : E2 CD : 75 00 00 00 01
09 00 : 01 B9 : DF 07 00 00 01
09 00 : 02 C9 : DF 07 00 00 01
09 00 : 0F 39 : DF 07 00 00 01
09 00 : 1A F2 : DF 07 00 00 01
09 00 : 36 7E : DF 07 00 00 01
09 00 : CD 65 : DF 07 00 00 01
09 00 : D0 55 : DF 07 00 00 01
09 00 : D2 D9 : DF 07 00 00 01

这使情况复杂化,并使通过数据包分析计算出 CRC 的细节变得更加困难。通过分析对 CRC 函数的引用,使用 IDA 解决问题可能会更快。