首页
社区
课程
招聘
[旧帖] [原创]某游戏的协议明文还原 0.00雪花
2013-9-3 15:44 13252

[旧帖] [原创]某游戏的协议明文还原 0.00雪花

2013-9-3 15:44
13252
前段日子有些时间,分析了某游戏且对其进行了协议的还原,还原后可在反外挂和外挂中使用,流程如下图所示:

                                    
      1. 解除游戏保护:
      脱壳、反调试见看雪文章。

      2. 还原游戏主程序:
先介绍下LoadAndRun技术,就是替换一个进程的内存空间为另一个程序,主用用作免杀和反调试。代码如下所示:
CreateProcess(nil, pchar(ParamStr(0)), nil, nil, False, CREATE_SUSPENDED, nil, nil, StartInfo, ProcInfo);
     Context.ContextFlags := CONTEXT_FULL;
     GetThreadContext(ProcInfo.hThread, Context);
     ReadProcessMemory(ProcInfo.hProcess, pointer(Context.Ebx + 8), @BaseAddress, 4, Bytes);
     VirtualAllocEx(ProcInfo.hProcess, pointer(ImageNtHeaders.OptionalHeader.ImageBase),InjectSize,  MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
     WriteProcessMemory(ProcInfo.hProcess, pointer(ImageNtHeaders.OptionalHeader.ImageBase),InjectMemory,InjectSize, Bytes);
     WriteProcessMemory(ProcInfo.hProcess, pointer(Context.Ebx + 8), @ImageNtHeaders.OptionalHeader.ImageBase, 4, Bytes);
     Context.Eax := ImageNtHeaders.OptionalHeader.ImageBase + ImageNtHeaders.OptionalHeader.AddressOfEntryPoint;
     SetThreadContext(ProcInfo.hThread, Context);
     ResumeThread(ProcInfo.hThread);


      上面的代码创建一个进程并挂起主线程,替换其内存空间。把要运行的进程的内存拷贝到此时的Ebx+8(被替换进程的加载基址)处,然后设置EAX(进程的入口点)为被替换进程的入口点。最后恢复主线程让程序运行起来。
游戏利用LoadAndRun技术,读取客户端所在目录res下的“abc.def”文件到内存,然后启动一个僵尸进程,然后在它运行前将其替换成“abc.def”,进程运行后执行的就是内存中的代码了。实际上“abc.def”就是游戏主程序。
      下图是某游戏IDA中关于僵尸进程保护的代码片段:
                                               

      因为游戏主程序是按需加载的,“abc.def”文件完全可以打乱二进制次序(实际也是这样做的),静态分析此文件将不是一个可执行的PE文件。但若动态跟踪解析文件逻辑,还原出代码或内存dump出镜像文件。
      这里用的是dump镜像文件的方法。Dump点是当内存中拼凑出完整可执行镜像之后,运行进程之前。在识别处的IDA函数LoadAndRun,选取其中一个参数为内存数据大小的数据个字节的窗口内存,选择"备份"->"保存数据到文件"。之后UltraEdit打开,剪切掉头两行,改后缀名为exe。
      找游戏加解密函数:找加解密算法有好几种方法:A.利用算法扫描器。B.使用OD、IDA搜索加解密特征码(XOR [REG],DWORD PTR [REG]、OpenSSL字符串等)。C. 发送接收函数断点法。
“发送接收函数断点法”是在WSASend、WSARecv下断点(游戏中并未使用send、recv),WSASend断下后栈回溯,加密函数可能在它之前,WSARecv断下后往后跟,解密函数可能在它之后。游戏主要通信协议加解密一般使用对称加密算法,所以加解密函数很可能使用一个函数。这里选取在WSASend断下后栈回溯的方法找加解密函数。OD栈回溯后如下图所示:
                                               
      分别跟踪0xAAFB2B、0xAAFE98、0xAB0063没有发现加解密函数,随后0x7xxxxxxx的地址是系统Dll调用,最终是由DispatchMessageW调用的。也就是把接收的消息发送(WSASend)出去。由此推测加密函数、发送函数不是在同一个线程中完成的,所以栈回溯不到(一个线程对应一个栈)。一般接收并处理消息代码如下:
while (GetMessage(&msg,0,0,0))
{	
	TranslateMessage(&msg);		
	DispatchMessage(&msg);
}


      断在WSASend后,逆向分析向上看上一层函数调用在消息循环里,下面是注册消息循环的函数:
00AB00F0 sub_AB00F0      proc near               ; CODE XREF: sub_AB0140+13F p
.text:00AB00F0
.text:00AB00F0 var_28          = dword ptr -28h
.text:00AB00F0 var_24          = dword ptr -24h
.text:00AB00F0 var_20          = dword ptr -20h
.text:00AB00F0 var_1C          = dword ptr -1Ch
.text:00AB00F0 var_18          = dword ptr -18h
.text:00AB00F0 var_14          = dword ptr -14h
.text:00AB00F0 var_10          = dword ptr -10h
.text:00AB00F0 var_C           = dword ptr -0Ch
.text:00AB00F0 var_8           = dword ptr -8
.text:00AB00F0 var_4           = dword ptr -4
.text:00AB00F0
.text:00AB00F0                 sub     esp, 28h
.text:00AB00F3                 mov     ecx, [ecx+0Ch]
.text:00AB00F6                 xor     eax, eax
.text:00AB00F8                 lea     edx, [esp+28h+var_28]
.text:00AB00FB                 push    edx             ; lpWndClass
.text:00AB00FC                 mov     [esp+2Ch+var_28], eax
.text:00AB0100        mov     [esp+2Ch+var_24], offset lpfnWndProc  ; 消息循环
.text:00AB0108                 mov     [esp+2Ch+var_20], eax
.text:00AB010C                 mov     [esp+2Ch+var_1C], eax
.text:00AB0110                 mov     [esp+2Ch+var_18], ecx
.text:00AB0114                 mov     [esp+2Ch+var_14], eax
.text:00AB0118                 mov     [esp+2Ch+var_10], eax
.text:00AB011C                 mov     [esp+2Ch+var_C], eax
.text:00AB0120                 mov     [esp+2Ch+var_8], eax
.text:00AB0124                 mov     [esp+2Ch+var_4], offset off_C21AA4
.text:00AB012C                 call    ds:RegisterClassW  ; 注册窗口类
.text:00AB0132                 neg     ax
.text:00AB0135                 sbb     eax, eax
.text:00AB0137                 neg     eax
.text:00AB0139                 add     esp, 28h
.text:00AB013C                 retn
.text:00AB013C sub_AB00F0     endp


下面是消息循环函数,观察uMsg=9C40h的跳转:
00AAFEE0  lpfnWndProc:
.text:00AAFEE0                 mov     ecx, [esp+8]     ; wMsg
.text:00AAFEE4                 push    ebx
.text:00AAFEE5                 push    ebp
.text:00AAFEE6                 mov     eax, ecx
.text:00AAFEE8                 sub     eax, 1
.text:00AAFEEB                 push    esi
.text:00AAFEEC                 push    edi
.text:00AAFEED                 jz      loc_AB0095
.text:00AAFEF3                 sub     eax, 9C3Fh
.text:00AAFEF8                 jz      loc_AAFFCC      ; wMsg=9C40h
.text:00AAFEFE                 sub     eax, 1
.text:00AAFF01                 jz      short loc_AAFF11
.text:00AAFF03                 pop     edi
.text:00AAFF04                 pop     esi
.text:00AAFF05                 pop     ebp
.text:00AAFF06                 pop     ebx
.text:00AAFF07                 mov     [esp+8], ecx
.text:00AAFF0B                 jmp     ds:DefWindowProcW
......
.text:00AAFFCC loc_AAFFCC:
.text:00AAFFCC                 mov     ebx, [esp+20h]
.text:00AAFFD0                 mov     ebp, [esp+14h]
.text:00AAFFD4                 push    0FFFFFFEBh  ;GWL_USERDATA
.text:00AAFFD6                 mov     edi, ebx
.text:00AAFFD8                 push    ebp
.text:00AAFFD9                 shr     edi, 10h
.text:00AAFFDC                 call    ds:GetWindowLongW 
.text:00AAFFE2                 mov   esi, eax     ; 返回值会传入00AB0073
.text:00AAFFE4                 push    esi
.text:00AAFFE5                 call    sub_AAEA30  
.text:00AAFFEA                 add     esp, 4
.text:00AAFFED                 test    al, al
.text:00AAFFEF                 jz      loc_AB00A9
.text:00AAFFF5                 movzx   eax, bx
.text:00AAFFF8                 add     eax, 0FFFFFFFFh
.text:00AAFFFB                 cmp     eax, 1Fh
.text:00AAFFFE
.text:00AAFFFE loc_AAFFFE:
.text:00AAFFFE                 ja      loc_AB00A9 
.text:00AB0004                 movzx   eax, ds:byte_AB00C8[eax]
.text:00AB000B                 jmp     ds:off_AB00B4[eax*4]
......
.text:00AB006A loc_AB006A:
.text:00AB006A                  
.text:00AB006A                 cmp     byte ptr [esi+8], 0
.text:00AB006E                 jz      short loc_AB00A9 ; default
.text:00AB0070                 push    edi
.text:00AB0071                 mov     ecx, esi
.text:00AB0073                 call    _SendData      ; 会调用WSASend


      SendData 反汇编代码函数省略,它会调用WSASend发送数据。由WSASend的Buffers.buf参数向上分析,提取出了下面的反汇编代码:
mov     edi,ecx
mov     edx, [edi+2880h]
mov     ebx, [edx]
mov     eax, [ebx+8]
mov     [edi+2888h], eax
mov     ebx, [edi+2888h]
mov     esi, [ebx+4]
mov     [esp+24h+Buffers.buf], esi
call      WSASend


      WSASend发送的数据可推导是由[[[ecx+2880h]]+8]+4公式得来的,ecx为00AAFFDC地址处GetWindowLongW传入GWL_USERDATA参数调用后的返回值。GWL_USERDATA参数解释为:Retrieves the user data associated with the window. This data is intended for use by the application that created the window。
在前面的消息循环中获得待发送的数据。
发送的数据由前面的[[[ecx+2880h]]+8]+4公式,以此得到待发送数据的地址。随后发现得到的是一个动态产生的地址,无法下断点。发现[[ecx+2880h]]+8是个静态地址,于是在地址[[对象+2880h]]+8下写入断点。
观察内存中存在的“说话数据”会容易一些,从上面下的写入断点往下跟,发现输入的“说话内容”,再对说话内容下写入断点。在一个函数中中断下,这就是加解密函数。
加解密函数反汇编代码如下:
EncryptDataAlgorithm  proc  near
.text:00ADD240
.text:00ADD240 arg_0           = dword ptr  4    ;子密钥产生代换表
.text:00ADD240 arg_4           = dword ptr  8    ;需要加解密数据长度
.text:00ADD240 arg_8           = dword ptr  0Ch  ; 加解密源地址
.text:00ADD240 arg_C           = dword ptr  10h  ; 加解密目的地址
.text:00ADD240
.text:00ADD240                 push    ebp 
.text:00ADD241                 push    ebx
.text:00ADD242                 push    esi
.text:00ADD243                 push    edi
.text:00ADD244                 mov     edi, [esp+10h+arg_0]
.text:00ADD248                 mov     edx, [esp+10h+arg_4]
.text:00ADD24C                 mov     esi, [esp+10h+arg_8]
.text:00ADD250                 mov     ebp, [esp+10h+arg_C]
.text:00ADD254                 xor     eax, eax
.text:00ADD256                 xor     ebx, ebx
.text:00ADD258                 cmp     edx, 0
.text:00ADD25E                 jz      loc_ADD3C7
.text:00ADD264                 mov     al, [edi]
.text:00ADD266                 mov     bl, [edi+4]
.text:00ADD269                 add     edi, 8
.text:00ADD26F                 lea     ecx, [esi+edx]
.text:00ADD272                 sub     ebp, esi
.text:00ADD274                 mov     [esp+10h+arg_4], ecx
.text:00ADD278                 inc     al
.text:00ADD27A                cmp     dword ptr [edi+100h], 0FFFFFFFFh
.text:00ADD284                 jz      loc_ADD390
.text:00ADD28A                 mov     ecx, [edi+eax*4]
.text:00ADD28D                 and     edx, 0FFFFFFFCh
.text:00ADD293                 jz      loc_ADD350 
.text:00ADD299                 lea     edx, [esi+edx-4]
.text:00ADD29D                 mov     [esp+10h+arg_8], edx
.text:00ADD2A1                 mov     [esp+10h+arg_C], ebp
……
.text:00ADD2B0
.text:00ADD2B0 loc_ADD2B0:  ; CODE XREF: EncryptDataAlgorithm+F8j
.text:00ADD2B0                 add     bl, cl
.text:00ADD2B2                 mov     edx, [edi+ebx*4]
.text:00ADD2B5                 mov     [edi+ebx*4], ecx
.text:00ADD2B8                 mov     [edi+eax*4], edx
.text:00ADD2BB                 add     edx, ecx
.text:00ADD2BD                 inc     al
.text:00ADD2BF                 and     edx, 0FFh   //mod 255
.text:00ADD2C5                 mov     ecx, [edi+eax*4]
.text:00ADD2C8                 mov     ebp, [edi+edx*4]
.text:00ADD2CB                 add     bl, cl
.text:00ADD2CD                 mov     edx, [edi+ebx*4]
.text:00ADD2D0                 mov     [edi+ebx*4], ecx
.text:00ADD2D3                 mov     [edi+eax*4], edx
.text:00ADD2D6                 add     edx, ecx
.text:00ADD2D8                 inc     al
.text:00ADD2DA                 and     edx, 0FFh
.text:00ADD2E0                 ror     ebp, 8
.text:00ADD2E3                 mov     ecx, [edi+eax*4]
.text:00ADD2E6                 or      ebp, [edi+edx*4]
.text:00ADD2E9                 add     bl, cl
.text:00ADD2EB                 mov     edx, [edi+ebx*4]
.text:00ADD2EE                 mov     [edi+ebx*4], ecx
.text:00ADD2F1                 mov     [edi+eax*4], edx
.text:00ADD2F4                 add     edx, ecx
.text:00ADD2F6                 inc     al
.text:00ADD2F8                 and     edx, 0FFh
.text:00ADD2FE                 ror     ebp, 8
.text:00ADD301                 mov     ecx, [edi+eax*4]
.text:00ADD304                 or      ebp, [edi+edx*4]
.text:00ADD307                 add     bl, cl
.text:00ADD309                 mov     edx, [edi+ebx*4]
.text:00ADD30C                 mov     [edi+ebx*4], ecx
.text:00ADD30F                 mov     [edi+eax*4], edx
.text:00ADD312                 add     edx, ecx
.text:00ADD314                 inc     al
.text:00ADD316                 and     edx, 0FFh
.text:00ADD31C                 ror     ebp, 8
.text:00ADD31F                 mov     ecx, [esp+10h+arg_C]
.text:00ADD323                 or      ebp, [edi+edx*4]
.text:00ADD326                 ror     ebp, 8
.text:00ADD329                 xor     ebp, [esi]  ;密钥与明文异或
.text:00ADD32B                 cmp     esi, [esp+10h+arg_8]
.text:00ADD32F                 mov     [ecx+esi], ebp
.text:00ADD332                 lea     esi, [esi+4]
.text:00ADD335                 mov     ecx, [edi+eax*4]
.text:00ADD338                 jb      loc_ADD2B0
.text:00ADD33E                 cmp     esi, [esp+10h+arg_4]
.text:00ADD342                 jz      loc_ADD3BF
.text:00ADD348                 mov     ebp, [esp+10h+arg_C]
…….
.text:00ADD350 loc_ADD350:
.text:00ADD350                 add     bl, cl
.text:00ADD352                 mov     edx, [edi+ebx*4]
.text:00ADD355                 mov     [edi+ebx*4], ecx
.text:00ADD358                 mov     [edi+eax*4], edx
.text:00ADD35B                 add     edx, ecx
.text:00ADD35D                 inc     al
.text:00ADD35F                 and     edx, 0FFh
.text:00ADD365                 mov     edx, [edi+edx*4]
.text:00ADD368                 xor     dl, [esi]
.text:00ADD36A                 lea     esi, [esi+1]
.text:00ADD36D                 mov     ecx, [edi+eax*4]
.text:00ADD370                 cmp     esi, [esp+10h+arg_4]
.text:00ADD374                 mov     [ebp+esi-1], dl
.text:00ADD378                 jb      loc_ADD350
.text:00ADD37E                 jmp     loc_ADD3BF
.text:00ADD37E ; ---------------------------------------------------------------------------
.text:00ADD383                 align 10h
.text:00ADD390
.text:00ADD390 loc_ADD390:
.text:00ADD390                 movzx   ecx, byte ptr [edi+eax]
.text:00ADD394
.text:00ADD394 loc_ADD394:
.text:00ADD394                 add     bl, cl
.text:00ADD396                 movzx   edx, byte ptr [edi+ebx]
.text:00ADD39A                 mov     [edi+ebx], cl
.text:00ADD39D                 mov     [edi+eax], dl
.text:00ADD3A0                 add     dl, cl
.text:00ADD3A2                 movzx   edx, byte ptr [edi+edx]
.text:00ADD3A6                 add     al, 1
.text:00ADD3A8                 xor     dl, [esi]
.text:00ADD3AA                 lea     esi, [esi+1]
.text:00ADD3AD                 movzx   ecx, byte ptr [edi+eax]
.text:00ADD3B1                 cmp     esi, [esp+10h+arg_4]
.text:00ADD3B5                 mov     [ebp+esi-1], dl
.text:00ADD3B9                 jb      loc_ADD394
.text:00ADD3BF
.text:00ADD3BF loc_ADD3BF:
.text:00ADD3BF                 dec     al
.text:00ADD3C1                 mov     [edi-4], bl
.text:00ADD3C4                 mov     [edi-8], al
.text:00ADD3C7
.text:00ADD3C7 loc_ADD3C7:
.text:00ADD3C7                 pop     edi
.text:00ADD3C8                 pop     esi
.text:00ADD3C9                 pop     ebx
.text:00ADD3CA                 pop     ebp
.text:00ADD3CB                 retn
.text:00ADD3CB EncryptDataAlgorithm endp
.text:00ADD3CB
.text:00ADD3CB ; --------------------------


来到加密函数EncryptDataAlgorithm 的调用者,可识别出这是个加密线程:
.text:00AAF610 EncryptData_Thread proc near
.text:00AAF610
.text:00AAF610 arg_0           = dword ptr  4
.text:00AAF610
.text:00AAF610                 push    ebx
.text:00AAF611                 push    ebp
.text:00AAF612                 push    esi
.text:00AAF613                 push    edi
.text:00AAF614                 mov     edi, [esp+10h+arg_0]
.text:00AAF618                mov     ebp, [edi+4]    ; 第一个参数 +4h(明文起始地址) +8h(明文结束地址)
.text:00AAF61B                 cmp     ebp, [edi+8]
.text:00AAF61E                 mov     esi, ecx        ; [ecx+54h]为产生子密钥的代换表
.text:00AAF620                 jbe     short loc_AAF62A
.text:00AAF622                 call    __invalid_parameter_noinfo
.text:00AAF627                 cmp     ebp, [edi+8]
.text:00AAF62A
.text:00AAF62A                 jb      short loc_AAF631
.text:00AAF62C                 call    __invalid_parameter_noinfo
.text:00AAF631
.text:00AAF631                 mov     ebx, [edi+4]
.text:00AAF634                 cmp     ebx, [edi+8]
.text:00AAF637                 jbe     short loc_AAF641
.text:00AAF639                 call    __invalid_parameter_noinfo
.text:00AAF63E                 cmp     ebx, [edi+8]
.text:00AAF641
.text:00AAF641                 jb      short loc_AAF648
.text:00AAF643                 call    __invalid_parameter_noinfo
.text:00AAF648
.text:00AAF648                 mov     eax, [edi+4]
.text:00AAF64B                 test    eax, eax
.text:00AAF64D                 jnz     short loc_AAF653
.text:00AAF64F                 xor     edi, edi
.text:00AAF651                 jmp     short loc_AAF658
.text:00AAF653 ; ---------------------------------------------------------------------------
.text:00AAF653
.text:00AAF653                 mov     edi, [edi+8]
.text:00AAF656                sub     edi, eax  ; 结束地址 - 起始地址 = 明文数据长度
.text:00AAF658
.text:00AAF658                 add     ebp, 2
.text:00AAF65B                 push    ebp
.text:00AAF65C                 add     ebx, 2
.text:00AAF65F                 push    ebx             ; 加解密源、目的地址
.text:00AAF660                 add     edi, 0FFFFFFFEh ; 设置需要加解密数据长度 至少>=2
.text:00AAF663                 lea     eax, [esi+54h]
.text:00AAF666                 push    edi             ; 需要加解密数据长度
.text:00AAF667                 push    eax             ; 产生子密钥的代换表
.text:00AAF668                 call    EncryptDataAlgorithm
.text:00AAF66D                 mov     ebx, [esi+2880h]
.text:00AAF673                 mov     edx, [ebx+4]
.text:00AAF676                 add     esp, 10h
.text:00AAF679                 lea     edi, [esi+287Ch]
.text:00AAF67F                 lea     ecx, [esp+10h+arg_0]
.text:00AAF683                 push    ecx
.text:00AAF684                 push    edx
.text:00AAF685                 push    ebx
.text:00AAF686                 mov     ecx, edi
.text:00AAF688                 call    sub_65DAF0
.text:00AAF68D                 push    1
.text:00AAF68F                 mov     ecx, edi
.text:00AAF691                 mov     ebp, eax
.text:00AAF693                 call    sub_9729E0
.text:00AAF698                 mov     [ebx+4], ebp
.text:00AAF69B                 mov     eax, [ebp+4]
.text:00AAF69E                 mov     [eax], ebp
.text:00AAF6A0                 or      dword ptr [esi+288Ch], 2
.text:00AAF6A7                 mov     eax, [esi+288Ch]
.text:00AAF6AD                 mov     ecx, [esi+10h]
.text:00AAF6B0                 mov     edx, [esi+40h]
.text:00AAF6B3                 push    eax             ; lEvent
.text:00AAF6B4                 push    9C40h           ; wMsg
.text:00AAF6B9                 push    ecx             ; hWnd
.text:00AAF6BA                 push    edx             ; s
.text:00AAF6BB                 call    ds:WSAAsyncSelect
.text:00AAF6C1                 pop     edi
.text:00AAF6C2                 pop     esi
.text:00AAF6C3                 pop     ebp
.text:00AAF6C4                 pop     ebx
.text:00AAF6C5                 retn    4
.text:00AAF6C5 EncryptData_Thread endp


EncryptDataAlgorithm调用完后会调用WSAAsyncSelect 发送wMsg=9C40h的消息到前面注册过的消息循环。消息循环lpfnWndProc函数会处理参数为9C40h的消息,通过_SendData函数WSASend出去。
由此分析出了加密函数EncryptDataAlgorithm为后面的分析做了铺垫,并看到了加密函数和发送函数分别在两个函数中执行。

此函数识别出来是RC4算法,RC4 伪代码如下:
i,j=0;
while(明文未结束)
  i=(i+1) mod 256;
  j=(j+S[i]) mod 256;
  Swap(S[i],S[j]);
  t=(S[i]+S[j]) mod 256;
  k=S[t];
  m^=k;  //明文m与得到的密钥k进行异或

      RC4是一次一密,即每次对明文进行加解密的密钥都不一样。RC4有一个置换表,加密时每次从置换表中产生不同的密钥与明文字节异或,同时置换表作相应变换。解密与加密相同且置换表是对应的,这样才能保证加密后的密文能正确解密成明文。但在实际通信过程中,要求服务端和客户端网络数据的加解密之间不能丢包,否则两边的置换表对应不上,造成后续的加解密错误。
RC4需要对置换表作初始化,且初始化结果不能每次相同,否则黑客可以破译加解密过程,还原明文,因为RC4置换表操作是相同的。使用初始密钥5~16字节对置换表进行初始化,伪代码如下:
for i=0 to 255 do
  S[i]:=i;
j:=0;
for i=0 to 255 do
   j:=(j+S[i]+key[i mod keylength]) mod 256;  //重复使用密钥
   Swap(S[i],S[j]);                        //交换S[i],S[j]

      开始使用mod 256(and reg, 0FFh)特征定位初始化置换表反汇编代码,找到200多处地方,但未发现哪处是初始化部分。随后想到RC4相关逻辑应该封装在一个类中,所以会在RC4加解密函数EncryptDataAlgorithm附近,果然就在EncryptDataAlgorithm紧接着的一个函数。
InitEncryptReplacementTable proc near   
.text:00AB1E90                                         
; 参数:置换表、初始化密钥长度、密钥基址
.text:00AB1E90
.text:00AB1E90 arg_0           = dword ptr  4
.text:00AB1E90 arg_4           = dword ptr  8
.text:00AB1E90 arg_8           = dword ptr  0Ch
.text:00AB1E90
.text:00AB1E90                 push    ebp
.text:00AB1E91                 push    ebx
.text:00AB1E92                 push    esi
.text:00AB1E93                 push    edi
.text:00AB1E94                 mov     edi, [esp+10h+arg_0]
.text:00AB1E98                 mov     ebp, [esp+10h+arg_4]
.text:00AB1E9C                 mov     esi, [esp+10h+arg_8]
.text:00AB1EA0                 lea     edx, dword_DD73F4
.text:00AB1EA6                 lea     edi, [edi+8]
.text:00AB1EA9                 lea     esi, [esi+ebp]  ; esi+=ebp
.text:00AB1EAC                 neg     ebp
.text:00AB1EAE                 xor     eax, eax
.text:00AB1EB0                 mov     [edi-4], ebp
.text:00AB1EB3                 bt      dword ptr [edx], 14h
.text:00AB1EB7                 jb      loc_AB1F00
.text:00AB1EBD                 nop
.text:00AB1EBE                 nop
.text:00AB1EBF                 nop
.text:00AB1EC0
.text:00AB1EC0 loc_AB1EC0:
.text:00AB1EC0                mov     [edi+eax*4], eax ; for i=0 to 255 do  S[i]:=i;
.text:00AB1EC3                 add     al, 1
.text:00AB1EC5                 jnb     loc_AB1EC0
.text:00AB1ECB                 xor     ecx, ecx        ; ecx=0
.text:00AB1ECD                 xor     edx, edx
.text:00AB1ECF                 nop
.text:00AB1ED0
.text:00AB1ED0 loc_AB1ED0:
.text:00AB1ED0            mov     eax, [edi+ecx*4] ; for i=0 to 256 do j:=(j+S[i]+key[i mod keylength]) mod 256
.text:00AB1ED3                 add    dl, [esi+ebp]; [esi+ebp]保存的key: 5个字节 会话密钥(会变)
.text:00AB1ED6                 add     dl, al
.text:00AB1ED8                 add     ebp, 1
.text:00AB1EDE                 mov     ebx, [edi+edx*4]
.text:00AB1EE1                 jnz     loc_AB1EEA
.text:00AB1EE7                 mov     ebp, [edi-4]    ; ebp=0FFFFFFFB
.text:00AB1EEA
.text:00AB1EEA                 mov     [edi+edx*4], eax ; Swap(S[i],S[j])
.text:00AB1EED                 mov     [edi+ecx*4], ebx
.text:00AB1EF0                 add     cl, 1
.text:00AB1EF3                 jnb     loc_AB1ED0      ; 循环256次
.text:00AB1EF9                 jmp     loc_AB1F53
.text:00AB1EF9 ; ---------------------------------------------------------------------------
.text:00AB1EFE                 align 10h
.text:00AB1F00
.text:00AB1F00                 mov     [edi+eax], al
.text:00AB1F03                 add     al, 1
.text:00AB1F05                 jnb     loc_AB1F00
.text:00AB1F0B                 xor     ecx, ecx
.text:00AB1F0D                 xor     edx, edx
.text:00AB1F0F                 xor     ebx, ebx
…….
text:00AB1F20
.text:00AB1F20                 mov     al, [edi+ecx]
.text:00AB1F23                 add     dl, [esi+ebp]
.text:00AB1F26                 add     dl, al
.text:00AB1F28                 add     ebp, 1
.text:00AB1F2E                 mov     bl, [edi+edx]
.text:00AB1F31                 jnz     loc_AB1F3A
.text:00AB1F37                 mov     ebp, [edi-4]
.text:00AB1F3A
.text:00AB1F3A                 mov     [edi+edx], al
.text:00AB1F3D                 mov     [edi+ecx], bl
.text:00AB1F40                 add     cl, 1
.text:00AB1F43                 jnb     loc_AB1F20
.text:00AB1F49              mov     dword ptr [edi+100h], 0FFFFFFFFh
.text:00AB1F53
.text:00AB1F53                 xor     eax, eax
.text:00AB1F55                 mov     [edi-8], eax
.text:00AB1F58                 mov     [edi-4], eax
.text:00AB1F5B                 pop     edi
.text:00AB1F5C                 pop     esi
.text:00AB1F5D                 pop     ebx
.text:00AB1F5E                 pop     ebp
.text:00AB1F5F                 retn
.text:00AB1F5F InitEncryptReplacementTable endp
.text:00AB1F5F
.text:00AB1F60 ; ---------------------------------------------------------------------------
.text:00AB1F60                 call    $+5
.text:00AB1F65                 pop     eax
.text:00AB1F66                 lea     eax, [eax+1Bh]
.text:00AB1F69                 lea     edx, dword_DD73F4
.text:00AB1F6F                 bt      dword ptr [edx], 14h
.text:00AB1F73                 jnb     locret_AB1F7E
.text:00AB1F79                 add     eax, 3
.text:00AB1F7E
.text:00AB1F7E                 retn
.text:00AB1F7E ; ---------------------------------------------------------------------------
.text:00AB1F7F                 align 10h
.text:00AB1F80                 xor     al, 78h
.text:00AB1F82                 add     [ecx], dh
.text:00AB1F84                 js      short $+2


      RC4初始化、加解密函数都找到了,剩下的是找“初始化置换表的加密密钥”了。通过OD InitEncryptReplacementTable调用栈回溯,对应IDA截图如下图:
                                               

       经过分析该函数在连接上服务端后,便初始化RC4置换表。在InitEncryptReplacementTable中对00AB1ED3地址处的add dl, [esi+ebp]这句的esi+ebp地址下写入断点,在sub_AB2080函数中某处断下。说明是客户端产生密钥传送给服务端的。
可以看到上面的截图两次调用InitEncryptReplacementTable,即初始化两个置换表,一个为esi+54h处、一个为esi+45Ch处。分别对应加密、解密置换表,通过加密、解密函数传入的参数可以看出,如图下面两幅图所示:
                                               
                                               
                                               

      也可以看到InitEncryptReplacementTable的第3个参数都是edi指向esi+4Ch,说明用来初始化置换表的密钥是相同的。
通过跟入0xAAEF65(_InitRSALib)处理流程,出现OpenSSL字符,如下图所示:
                                               

      说明使用了OpenSSL做游戏协议加解密(OpenSSL可以参考: http://openssl.cn/)。当抓包、逆向分析、对照OpenSSL源码后并未发现使用SSL,只使用了RSA加解密算法传送RC4的主密钥。在客户端WSARecv、WSASend下断点首先拦截到以下几个包:

WSARecv  256字节数据,为服务器发送的公钥(从0364E555 开始的256字节为n,最后两个字节0xffff为e):
0364E54D  00 01 00 00 02 00 00 00 C9 3C C8 CD 6C 9D B9 A6
0364E55D  55 33 0B EE B2 1B 11 A6 30 16 A2 B2 48 91 23 58
0364E56D  7F D7 40 5E CB 92 AA DA CE 57 9A EC 3B D8 08 EF
0364E57D  B6 BB C3 CB E5 5C 9D A0 0B 07 1E 82 E1 B0 BA 68
0364E58D  6A 53 49 2D 99 FE CE E6 E9 89 37 9B 25 E4 9D E5
0364E59D  7E 0D 37 3C C9 90 7B D7 E4 46 5A EF C4 94 0E FF
0364E5AD  13 D2 E7 AB 8F 45 AA 71 64 AB 8A 46 9B F8 72 32
0364E5BD  72 B4 5A 00 0D 32 AB 2C 70 C0 E2 B5 05 7D 09 98
0364E5CD  6A EF 20 92 58 3E A2 34 D2 7F D9 CF 82 2F 5B 23
0364E5DD  02 BE A0 1D E6 CA C3 3A 90 AC 2E 17 EC CB 27 F4
0364E5ED  6B 41 DA DA E7 01 5C 08 ED D6 CE 36 1F 7F 0A 3C
0364E5FD  C4 01 98 B5 C6 2D 0E 37 76 88 85 C7 B7 E5 2C B5
0364E60D  D5 2B 7E D0 BA 8C 92 06 6E DB 57 0B 94 74 D6 7E
0364E61D  6B 8A 11 06 A1 B8 96 65 82 AE 19 CC 78 AE A9 EA
0364E62D  19 62 B1 D8 DE 2B 1C 84 FE D7 98 8B E4 C7 34 40
0364E63D  79 7E D7 3A 97 EB D3 C1 51 8B 48 21 C2 0F 09 61
0364E64D  FD 02 94 F3 96 15 AE B7 FF FF

WSASend 256字节数据 用服务器发送的公钥加密客户端产生的5字节密钥
0364A910  5D 2B D2 39 47 94 A0 3E 22 6A 94 9E F1 70 1D 2E  ]+?G敔>"j敒駊.
0364A920  06 6C 1E E9 C9 E9 C5 1D A6 E6 D8 D3 42 90 C6 7B  l 樯榕︽赜B惼{
0364A930  DC 1A 2A 54 A7 41 19 5B 93 98 37 20 3C CA FC 2A  ?*T[摌7 <庶*
0364A940  1C A5 C6 38 57 80 67 22 B8 13 EC F7 A6 B2 40 56  テ8W€g"?祺Σ@V
0364A950  2B CB 60 C1 83 D0 9C 89 C4 91 40 F6 52 1A 07 4E  +薫羶袦壞慇鯮N
0364A960  19 83 3C CA 43 5D 02 DE F1 86 32 23 FD FA CF E4  ?蔆]揆?#箱
0364A970  52 7D 8F 4D BF 90 12 64 17 F8 6D 89 40 47 35 C7  R}廙繍d鴐堾G5
0364A980  94 F4 F5 E8 00 1F 8A F3 FD 79 73 5A BF C7 38 20  旚蹊.¬婓齳sZ壳8
0364A990  38 10 27 70 80 0C F9 82 6B 86 96 AE E8 AC 57 3B  8'p€.鶄k問琖;
0364A9A0  36 86 E0 D0 7D 31 0D E7 67 3C 90 1B 1B C2 89 5E  6嗋衹1.鏶<?聣^
0364A9B0  B7 D9 A1 A3 57 6F 3B D0 A4 9B 5E F0 20 D5 65 D7  焚。Wo;肖沕?誩
0364A9C0  A4 1C 07 25 99 D9 72 E2 2C 12 45 E3 60 83 86 B0  ?%欃r?E鉦儐
0364A9D0  07 CF 6C 7D 60 97 8F B5 11 77 C4 EC 2E AF BF CA  蟣}`棌?w撵.
0364A9E0  91 B4 84 57 A7 3D 6C 00 9A ED 7C 9F 66 A4 68 71  懘刉?l.氻|焒q
0364A9F0  D7 6A C4 C1 E7 D8 02 EB 7B F0 58 B9 D5 6E 16 77  譲牧缲雥餢拐nw
0364AA00  D1 9D 40 0F 54 F9 8E 6E 2A 3D D6 67 54 CA 7B B7  褲@T鶐n*=謌蕒

WSARecv(收8字节数据)
036529BD  06 00 0B 1B 9F 5D 57 F7
036522ED  06 00 05 64 9B 2A 7B 55

-----------------------------后面开始就对数据加解密了--------------------------------------

WSARecv(收6字节数据):解密后为 6字节03 00 9A 99 E9 40 不变                  
其中03 00 为Opcode,9A 99 E9 40(7.300000)

WSASend(发送64字节数据): 用户名、密码MD5、

WSARecv(收107字节数据):体验服...字符串

      确定客户端接收的第一个包为公钥(e,n)的方法有:
1).根据RSA算法,e是自己选取的,通常为3、17、65537(2的16次方+1)。可以确定RSA中的e为0xFFFF,n是从C9 3C C8 CD 6C 9D B9 A6开始的256字节数据,因为n是由两个大素数p和q相乘的结果,中间不能有很多0。
2).对数据下断并跟踪其处理流程。
3).找到OpenSSL中使用服务器公钥加密的函数(RSA_eay_public_encrypt),其形式为:static int RSA_eay_public_encrypt(int flen, const unsigned char *from,unsigned char *to, RSA *rsa, int padding);其中第四个参数RSA *rsa结构中包含公钥中的n、e,RSA结构如下:
struct rsa_st
        {
        /* The first parameter is used to pickup errors where
         * this is passed instead of aEVP_PKEY, it is set to 0 */
        int pad;
        long version;
        const RSA_METHOD *meth;
        /* functional reference if 'meth' is ENGINE-provided */
        ENGINE *engine;
        BIGNUM *n;
        BIGNUM *e;
        BIGNUM *d;
        BIGNUM *p;
        BIGNUM *q;
        BIGNUM *dmp1;
        BIGNUM *dmq1;
        BIGNUM *iqmp;
        /* be careful using this if the RSA structure is shared */
        CRYPTO_EX_DATA ex_data;
        int references;
        int flags;

        /* Used to cache montgomery values */
        BN_MONT_CTX *_method_mod_n;
        BN_MONT_CTX *_method_mod_p;
        BN_MONT_CTX *_method_mod_q;

        /* all BIGNUM values are actually in the following data, if it is not
         * NULL */
        char *bignum_data;
        BN_BLINDING *blinding;
        };

      在IDA中找到RSA_eay_public_encrypt ,用OD断下,获得RSA结构中的BIGNUM *n;以及BIGNUM *e; 可以验证与客户端接收到的公钥(n,e)相同。

      验证方法为测试 “服务器发送的公钥加密客户端产生的5字节密钥”结果是否正确。写一个小程序引入OpenSSL库,使用RSA_eay_public_encrypt函数传入n、e对5字节密钥加密,然后比较加密后的结果与发送的数据是否相同。
      私钥保存在Linux服务器,使用GDB对RSA_eay_private_decrypt或者RSA_eay_private_encrypt下断点,获取RSA中的n、d。d为:

0x96afa00:        0x0758d7ff        0x1f15a7ec        0x125668ed        0x40d706fa
0x96afa10:        0x337b8c38        0xe18cc08c        0x841c8b68        0xa64e7cdd
0x96afa20:        0x9b7d9eba        0x0cbf56c2        0x0596f7b0        0xc26c73bc
0x96afa30:        0xf309e207        0x400ef6b1        0xe3c04ae4        0x457997b4
0x96afa40:        0x6fe47eb0        0x0ca9ad2d        0xbbef0936        0xac87f24b
0x96afa50:        0x33b06752        0xd4b72be2        0x8bc3c316        0xf4940ad3
0x96afa60:        0x69de1f24        0x1458f15c        0x932a0a3a        0xe98afdc9
0x96afa70:        0x37a6bbec        0xe1d6f8a2        0x32593b33        0x643e44c4
0x96afa80:        0xfb86d3a9        0x0f128385        0x8b715153        0x18036cfc
0x96afa90:        0xaf63135a        0xbccea9e4        0x67e891e7        0x502b4277
0x96afaa0:        0x1c897fd7        0x15369667        0x89304650        0x6919679d
0x96afab0:        0x9e8e1b0f        0x42cb8dc7        0x301f25d2        0xcf3c8b82
0x96afac0:        0x681c78e0        0x989f90ad        0xdde13148        0xefb5e385
0x96afad0:        0x445f4192        0xdf562dfb        0x48c4eb9f        0x710268e6
0x96afae0:        0x5672c65b        0x6678c372        0xb4fdb59a        0xfa50e483
0x96afaf0:        0xee629300        0x4ac07da7        0x2426165e        0x7243689b

同样为256个字节。

      找到了游戏加解密函数,剩下的就是分析明文协议各字段的意义了。游戏功能协议字段一般包括:主功能号、次功能号、怪物ID、NPC ID、任务ID、物品ID、玩家ID、地图ID、背包格子位置、扩展背包ID、自身坐标(有可能为浮点数)、释放技能坐标等。有手动分析和工具分析两种方法,这里介绍手工分析方法。
      1). 在调用加解密函数加密数据的位置下断点,虽然游戏主要协议使用对称加密,且加解密函数相同,但加密、解密是两处调用同一加解密函数。
      2). 触发游戏中某功能,比如:说话、走路、拾取物品等,1)中的断点断下后,copy协议明文到Word文档。断下后,有可能是心跳包等无效包需要排除。
      3). 仍然触发2)中相同的功能,不同的是稍作调整,比如:走路功能走到不同的点、释放技能释放不同的技能、拾取物品拾取不同的物品、接任务到不同NPC处接任务或接不同任务…copy协议明文到Word文档。
      4). 对比相同功能2)、3)copy的协议。依据包头xx字节一般是主功能号、变化点和不变化点、各个字段一般是偶数字节(程序员为了方便写程序,short、DWORD),并用颜色标示出。
      5). 多分析几个相同功能的包,以确定各个字段的含义。需要注意:有些2D游戏和绝大多数3D游戏使用浮点数标示坐标。比如28 53 BF 3F、D0 0F 15 40、AA 94 24 43分别为浮点数1.494725、2.329090、164.580719。使用自己写的“十六进制 浮点数 转换工具”即可转换,微软提供的计算器转换不了。这里提供自制的浮点数转换器:
                              
      以下给出此工具实现的关键代码:
void CFloatToHex2Dlg::OnTransfer() 
{
	// TODO: Add your control notification handler code here
	
	char szHex[9]={0},szResult[4]={0};
	int nHex[9]={0};
	int nBack=0;
	m_hex.GetWindowText(szHex,sizeof szHex);
	
	char szFloat[MAX_PATH]={0};
	m_float.GetWindowText(szFloat,MAX_PATH);

	if (strcmp(szHex,"")) //十六进制转浮点数
	{
		for (int i=0;i<8;i++)
		{
			if( ( szHex[ i ] >= '0' && szHex[ i ] <= '9' ) ||
				( szHex[ i ] >= 'A' && szHex[ i ] <= 'F' ) ||
				( szHex[ i ] >= 'a' && szHex[ i ] <= 'f' ) )
			{
				if( szHex[ i ] >= '0' && szHex[ i ] <= '9' )
					nHex[i] = szHex[ i ] - '0';
				else if( szHex[ i ] >= 'A' && szHex[ i ] <= 'F' )
					nHex[i] = szHex[ i ] - 'A' + 10;
				else if( szHex[ i ] >= 'a' && szHex[ i ] <= 'f' )
					nHex[i] = szHex[ i ] - 'a' + 10;
			}

			if( i % 2 )    //如果是第奇数个1,3,5
			{
				nBack <<= 4;
				nBack += nHex[i];
				*( szResult +3 -( i / 2 )) = ( unsigned char )nBack;
			}
			else
				nBack = nHex[i];
		}
		
		float* pfValue=(float*)&szResult[0];
		memset(szFloat,0,sizeof szFloat);
		sprintf(szFloat,"%f",*pfValue);

		m_float.SetWindowText(szFloat);
	}
	else if (strcmp(szFloat,"")) //浮点数转十六进制
	{
		float  x=atof(szFloat);
		unsigned   int   y; 
		y=   *(unsigned   int   *)(&x);
		memset(szHex,0,sizeof szHex);
		itoa(y,szHex,16);
		m_hex.SetWindowText(szHex);
	}
}


该函数实现两个功能,十六进制转浮点数和浮点数转十六进制。代码保存在附件里。

以下为分析出的协议片段:

幻术师技能:
幻惑:
09 00 DB C3 3E 6A FF FF
凛峰冰锥:
09 00 D6 C3 E8 E6 FF FF
催眠之眼:
10 00 D7 C3 E4 1C 18 43 77 A4 E9 43 (152.112854、467.284882 技能释放地点x、y)
精神激发:
11 00 DA C3 D0 0F 15 40(2.329090)

深蓝色为技能大分类, 紫色为小分类,绿色为技能释放对象,澄黄色为技能释放范围。

某游戏的协议明文还原.doc

FloatToHex2.rar

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
点赞3
打赏
分享
最新回复 (59)
雪    币: 48
活跃值: (167)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
crazy、shit 2013-9-3 16:10
2
0
大神、好膜拜
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xieyb 2013-9-7 21:25
3
0
你是高高在上的星星,我是地上的一粒沙子。除了做梦,永远也达不到你那样的高度。
雪    币: 257
活跃值: (67)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
AioliaSky 1 2013-9-7 21:40
4
0
路过,来围观一下。。。
雪    币: 1683
活跃值: (380)
能力值: ( LV15,RANK:440 )
在线值:
发帖
回帖
粉丝
hackerlzc 10 2013-9-7 22:31
5
0
膜拜妞妞……
雪    币: 124
活跃值: (319)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
topofall 2013-9-8 09:03
6
0
受教了,膜拜一下
雪    币: 108
活跃值: (44)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
tuobaofeng 2013-9-8 09:33
7
0
好贴要顶~~
雪    币: 43
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mythjoyo 2013-9-8 09:33
8
0
传奇的游戏?APEX?
雪    币: 1737
活跃值: (110)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
vvking 2013-9-8 10:01
9
0
神马游戏???
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小小笑儿 2013-9-8 10:04
10
0
mark
雪    币: 14
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zdyruoshui 2013-9-8 14:22
11
0
急需破解协议这方面内容,谢谢楼主好好学习一下
雪    币: 17
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
YvonneRSny 2013-9-8 20:53
12
0
你是高高在上的月亮,我是海底的一只小虫。除了做梦,永远也达不到你那样的高度。
顺便请问下 有了这些协议 然后怎么用呢?
雪    币: 43
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
pecado 2013-9-8 21:36
13
0
围观凑个热闹
雪    币: 33
活跃值: (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
信念随心 2013-9-11 13:01
14
0
膜拜·······
雪    币: 8861
活跃值: (2364)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 10 2013-9-11 13:25
15
0
内容太敏感词了。
雪    币: 114
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dahwa 2013-9-11 13:55
16
0
圣境传说?
雪    币: 560
活跃值: (704)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
babalove 2013-9-11 14:43
17
0
好厉害              .
雪    币: 118
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xiaowz 2013-9-11 14:53
18
0
不错 不错
雪    币: 67
活跃值: (51)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Rlcupk 2013-9-11 15:04
19
0
正在学习驱动保护...膜拜下
雪    币: 17
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gqlseed 2013-9-11 15:31
20
0
膜拜大神!!!
雪    币: 242
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
司中 2013-9-11 15:43
21
0
确实厉害,江山代有才人出,各领风骚数百年。
雪    币: 339
活跃值: (133)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
地狱怪客 2 2013-9-11 15:52
22
0
膜拜。。。。。。
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
a糊涂虫 2013-9-11 16:42
23
0
mark
雪    币: 42
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
LShang 2013-9-18 09:22
24
0
当真要膜拜一下,正文中有一处暴漏的游戏名,还请楼主修正以免和谐。
雪    币: 680
活跃值: (68)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
稻草人Z 2 2013-9-18 09:47
25
0
1024
游客
登录 | 注册 方可回帖
返回