前段日子有些时间,分析了某游戏且对其进行了协议的还原,还原后可在反外挂和外挂中使用
,流程如下图所示:
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可以参考:
786K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3!0H3k6h3&6K6M7$3I4Q4x3X3g2U0L8W2)9J5c8W2)9J5z5g2!0q4x3#2)9^5x3q4)9^5x3W2!0q4y4g2!0n7c8q4)9&6x3#2!0q4y4W2)9^5b7g2)9&6x3#2!0q4y4g2)9^5b7#2)9^5y4g2!0q4x3#2)9^5x3q4)9^5x3g2!0q4z5g2)9^5x3q4)9^5y4W2!0q4y4g2)9&6x3q4)9&6x3g2!0q4y4g2)9^5z5q4)9^5y4W2!0q4y4W2)9&6c8g2)9&6x3q4!0q4x3#2)9^5x3q4)9^5x3g2!0q4y4g2!0m8c8W2!0n7z5g2!0q4y4#2)9^5y4g2!0m8y4@1!0H3k6h3&6e0f1@1I4Q4c8e0k6Q4b7V1q4Q4z5e0m8Q4c8e0N6Q4b7e0m8Q4z5o6q4Q4c8e0g2Q4z5e0m8Q4z5p5g2Q4c8e0g2Q4b7U0W2Q4b7U0k6Q4c8e0k6Q4z5f1y4Q4b7f1q4Q4c8e0g2Q4z5p5k6Q4z5e0q4Q4c8e0N6Q4z5p5g2Q4b7U0m8Q4c8e0c8Q4b7V1c8Q4b7V1k6Q4c8e0N6Q4z5e0c8Q4b7e0S2e0f1@1I4Q4c8f1k6Q4b7V1y4Q4z5p5y4Q4c8e0g2Q4z5p5k6Q4b7f1q4Q4c8e0c8Q4b7V1c8Q4b7V1k6Q4c8e0N6Q4z5e0c8Q4b7e0S2Q4c8e0c8Q4b7V1q4Q4z5o6k6d9f1@1q4Q4c8e0g2Q4z5p5q4Q4b7e0m8Q4c8e0S2Q4b7e0N6Q4b7e0y4Q4c8e0g2Q4b7f1k6Q4z5o6k6Q4c8e0N6Q4b7f1g2Q4z5e0N6Q4c8e0k6Q4b7U0y4Q4z5e0g2Q4c8e0c8Q4b7V1y4Q4b7e0m8Q4c8e0W2Q4z5o6m8Q4z5o6q4d9b7K6c8Q4c8e0N6Q4z5f1q4Q4z5o6c8Q4c8e0c8Q4b7U0S2Q4b7V1u0Q4c8e0g2Q4b7f1k6Q4z5o6k6Q4c8e0W2Q4z5e0u0Q4b7e0g2Q4c8e0y4Q4z5o6m8Q4z5o6u0Q4c8e0g2Q4z5f1y4Q4b7e0S2Q4c8e0g2Q4b7f1g2Q4b7e0u0Q4c8e0k6Q4z5o6S2Q4b7U0N6Q4c8e0N6Q4b7f1u0Q4b7f1k6i4f1@1q4d9k6h3y4$3i4@1f1K6i4K6R3H3i4K6R3I4g2#2y4m8f1$3g2F1k6q4!0q4y4q4!0n7z5q4)9^5b7W2!0q4y4W2)9&6y4W2!0m8c8q4!0q4y4#2)9^5x3W2!0n7z5g2!0q4z5g2!0m8y4W2)9&6y4W2!0q4y4g2)9^5y4g2)9^5z5q4!0q4y4W2)9^5b7W2!0m8y4W2!0q4y4W2)9^5z5q4!0m8b7g2!0q4y4g2)9^5z5q4!0n7x3q4!0q4y4q4!0n7b7W2!0m8y4g2!0q4y4q4!0n7z5q4)9^5b7W2!0q4y4g2)9^5y4#2!0m8x3q4!0q4y4q4!0n7z5q4!0m8b7g2!0q4y4g2)9^5b7#2)9^5y4g2!0q4c8W2!0n7b7#2)9&6b7b7`.`.
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;肖沕?誩
[招生]科锐逆向工程师培训(2025年3月11日实地,远程教学同时开班, 第52期)!
上传的附件: