• 标 题:以倚天II为例讨论网络游戏账号密码的破解
  • 作 者:shoooo
  • 时 间:2004-12-20,15:38
  • 链 接:http://bbs.pediy.com

【破解作者】 shoooo
【作者邮箱】 shoooo314@163.com
【使用工具】 softice 套装2.7 iceext插件,Iris
【破解平台】 win2000 sp4 
【破解目标】 账号,密码
【软件名称】 倚天II (公测中)
【破解时间】 2004-12-20
【加壳方式】 无壳
【破解声明】 我是一只小菜鸟,偶得一点心得,愿与大家分享:)
             本文旨在讨论网络游戏的破解思路,错误或不妥之处,各位批评指正
             文章中的代码粘贴自IDA,我softice不会dump汇编代码
             由于更新频繁或其它原因,内存地址可能存在出入

一,寻找登录包

    输入错误的用户名或密码,让TCP流尽早结束,这样交流的数据包较少,容易找

    截包工具有很多,看个人喜好了~~我Iris用惯了
    启动倚天II,出现登录界面,随便找个服务器,以用户名shoooo 密码1234登录,报错。看看截的包包,在登录过程中,含有数据的共有9

个包,包中的数据都是无规则的16进制,可以肯定是经过加密的。很容易发现前面7个包长度较短,内容近似。
    第8个包是客户端发给服务器,长度有56字节如下
                C8 9E 23 AC BA 93 FD 12   05 72 11 85 BF D0 E7 BB
                D1 C0 A9 36 35 AA 5B 0B   05 72 11 85 BF D0 E7 BB
                F3 9C 41 BD C1 62 6B 7A   22 E5 37 46 B7 13 1E 00
                B2 BC FA 80 11 B6 2D 82 
    第9个包从服务器发来给客户端,长度为16字节如下
                0E 6B 32 F1 40 39 80 69   05 72 11 85 BF D0 E7 BB 

    猜测第8个包是登录包,第9个登录包是登录响应包。

    用相同的账号再登录一次
    重点观察第8个包如下,并发现前32字节与前次登录中的数据相同
                C8 9E 23 AC BA 93 FD 12   05 72 11 85 BF D0 E7 BB
                D1 C0 A9 36 35 AA 5B 0B   05 72 11 85 BF D0 E7 BB
                5C 7C 2A 03 B6 CF D3 39   FE 04 3D 80 C8 B0 E3 25
                5A F8 EA 9E 62 2A 13 87 
    
    重点观察第9个包如下,与前次完全相同
                0E 6B 32 F1 40 39 80 69   05 72 11 85 BF D0 E7 BB 

    省略n次的相同或不同账号密码组合的登录试验......

    这时我们得到如下结论
    a. 登录包长度固定为56字节,若以相同的账号,密码登录,登录包的前32字节相同。登录的错误响应包数据相同,固定16字节
    b. 56,32,16都是8的倍数,加密算法极有可能是8字节的分组算法


二,寻找发送buffer

    可以从输入的账号,密码开始跟,也可以从发送的数据包反向跟,我个人倾向于后者,这里采用后者的方法

    请出softice,symbol loader 加载ws2_32.dll,倚天2有防softice,用上iceext插件顺利躲过
    启动客户端至登录界面
    ctrl-d 呼出
    确认右下角显示为倚天II的领空metein2(以下不再提示),如果不是可多试几次或用addr命令

    下断点bpx send
    下断点bpx wsasend
    随便选个服务器,输用户名shoooo,密码1234。点登录
    softice在send处断下,按F11回到调用send的地方,看EAX的值,EAX是调用send后的返回值也就是实际发送的字节长度,我们想要看到的

是56字节,即十六进制的38。现在不是,ctrl-d让它继续跑,马上又在send处断下,F11,看EAX的值,不是就ctrl-d 再继续
    约5到6次后,EAX的值显示为38,立即对准调用send的EIP:499722下断点,同时清除send,wsasend断点,按若干次ctrl-d,让登录过程结束

                 .text:00499716                 push    0               ; flags
                 .text:00499718                 push    eax             ; len
                 .text:00499719                 push    dword ptr [esi+24h] ; buf
                 .text:0049971C                 push    dword ptr [esi+244h] ; s
                 .text:00499722                 call    send            此处下断点
                 .text:00499727                 mov     edieax
                 .text:00499729                 test    ediedi

    再次点击登录,这次在 EIP:499722处断下
    下命令 d esi+24
                 B8 07 EE 01 08 08 00 00  .......        
    下命令 d 1ee07b8
                 C8 9E 23 AC BA 93 FD 12  05 72 11 85 BF D0 E7 BB 
                 D1 C0 A9 36 35 AA 5B 0B  05 72 11 85 BF D0 E7 BB
                 0D 15 41 28 73 52 5A 05  CF 87 39 DF 74 35 24 1C
                 87 7B 41 D6 18 21 BA B3
    这便是实际发送的登录包的数据

三,寻找加密过程

    即寻找发送buffer的数据从何而来,不断的下内存断点,直到找到我们需要的账号,密码的明文    

    只保留499722的eip断点
    下断点bpmd 1ee07b8
    以shoooo,1234再次登录
    第一次断在1ee07b8的内存写入,先别急着看,ctrl-d继续
    第二次断在了EIP:499722处。
    好了,到这里清楚了,第一次断下的地方便是对发送buffer的头8个字节的写入
    再次登录,断在第一处

                   .text:0048F30D                 mov     [edx], eax      这里edx值是1ee07b8
                   .text:0048F30F                 mov     [edx+4], ecx    断在此处
                   .text:0048F312                 pop     ebx
                   .text:0048F313                 pop     ebp
                   .text:0048F314                 retn                    跳转到48F3D0

    取消1ee07b8的内存断点,F10单步跟踪,48F314的retn语句后,跳转到了48F3D0
                   
                   .text:0048F383                 push    ebp
                   .....省略若干行
                   .text:0048F3C0              /  push    [ebp+arg_0]        
                   .text:0048F3C3              |  push    [ebp+arg_8]
                   .text:0048F3C6              |  push    dword ptr [edi]
                   .text:0048F3C8              |  push    dword ptr [edi+4] 指向明文,待输出密文的指针入栈
                   .text:0048F3CB              |  call    sub_48F2AA        核心加密,8字节明文加密,8字节密文输出
                   .text:0048F3D0              |  add     [ebp+arg_0], 8    准备下次加密的8字节明文
                   .text:0048F3D4              |  add     esp, 10h
                   .text:0048F3D7              |  add     edi, 8            准备下次输出的8字节密文
                   .text:0048F3DA              |  dec     [ebp+arg_C]
                   .text:0048F3DD              \  jnz     short loc_48F3C0  跳上去继续加密下一组
                   .text:0048F3DF                 pop     edi
                   .text:0048F3E0                 mov     eaxesi
                   .text:0048F3E2                 pop     esi
                   .text:0048F3E3                 pop     ebp
                   .text:0048F3E4                 retn                     跳转到499706

    48F3E4返回后,到了 499706

                   .text:004996EF                 push    edi             需要加密的字节数
                   .text:004996F0                 lea     ecx, [esi+42h]
                   .text:004996F3                 push    ecx             指向密钥
                   .text:004996F4                 mov     ecx, [esi+30h]
                   .text:004996F7                 add     ecxeax
                   .text:004996F9                 mov     eax, [esi+24h]
                   .text:004996FC                 add     eax, [esi+2Ch]
                   .text:004996FF                 push    ecx             指向登录包的明文(包含账号,密码)
                   .text:00499700                 push    eax             指向登录包的待输出的密文,这里是1ee07b8
                   .text:00499701                 call    sub_48F383      最外层的加密函数
                   .text:00499706                 add     [esi+2Ch], eax

    此时可以清除先前的所有断点,在EIP: 499071处下断,跟踪整个加密过程,到这里不难发现登录包的明文必须是8字节的倍数,每8个字节

调用call 48F2AA进行加密,输出8字节的密文

四,加密算法求逆

   既然服务器能对我们发送的加密登录包作出登录失败的反应,显然它是可逆的

   重点观察48F2AA处的核心加密函数,分析出相应的解密算法
    
                   .text:0048F2AA                 push    ebp
                   .text:0048F2AB                 mov     ebpesp
                   .text:0048F2AD                 mov     eax, [ebp+arg_4]
                   .text:0048F2B0                 mov     ecx, [ebp+arg_0]
                   .text:0048F2B3                 mov     edx, [ebp+arg_8]
                   .text:0048F2B6                 push    ebx
                   .text:0048F2B7                 push    esi
                   .text:0048F2B8                 push    edi
                   .text:0048F2B9                 xor     esiesi
                   .text:0048F2BB                 mov     [ebp+arg_4], 20h  作32轮
                   .text:0048F2C2                 mov     ediecx
                   .text:0048F2C4                 shr     edi, 5
                   .text:0048F2C7                 mov     ebxecx
                   .text:0048F2C9                 shl     ebx, 4
                   .text:0048F2CC                 xor     ediebx
                   .text:0048F2CE                 add     ediecx
                   .text:0048F2D0                 mov     ebxesi
                   .text:0048F2D2                 and     ebx, 3
                   .text:0048F2D5                 mov     ebx, [edx+ebx*4]
                   .text:0048F2D8                 add     ebxesi
                   .text:0048F2DA                 xor     ediebx
                   .text:0048F2DC                 add     eaxedi
                   .text:0048F2DE                 mov     edieax
                   .text:0048F2E0                 shr     edi, 5
                   .text:0048F2E3                 mov     ebxeax
                   .text:0048F2E5                 shl     ebx, 4
                   .text:0048F2E8                 xor     ediebx
                   .text:0048F2EA                 sub     esi, 61C88647h    加密函数中唯一出现的奇怪常数
                   .text:0048F2F0                 mov     ebxesi
                   .text:0048F2F2                 shr     ebx, 0Bh
                   .text:0048F2F5                 and     ebx, 3
                   .text:0048F2F8                 mov     ebx, [edx+ebx*4]
                   .text:0048F2FB                 add     edieax
                   .text:0048F2FD                 add     ebxesi
                   .text:0048F2FF                 xor     ediebx
                   .text:0048F301                 add     ecxedi
                   .text:0048F303                 dec     [ebp+arg_4]
                   .text:0048F306                 jnz     short loc_48F2C2
                   .text:0048F308                 mov     edx, [ebp+arg_C]
                   .text:0048F30B                 pop     edi
                   .text:0048F30C                 pop     esi
                   .text:0048F30D                 mov     [edx], eax
                   .text:0048F30F                 mov     [edx+4], ecx
                   .text:0048F312                 pop     ebx
                   .text:0048F313                 pop     ebp
                   .text:0048F314                 retn

    一般到这里就要开始体力劳动了,不过或许可以碰碰运气 
    google 关键字 0x61C88647  很快便能找到这个公开的加密解密算法 ^_^
    本文以讨论思路为主,破解到此为止,在网吧玩游戏的朋友小心哦~