??xml version="1.0" encoding="utf-8" standalone="yes"?> push 1FFF ;1FFF入栈
命o体部分经q普通解密后Q还需要根据一个掩码来q行二次解密
q里是二ơ解密命令体的部?br />
push ebp
mov ebp, esp
and esp, FFFFFFF8
push -1
push 004C833C
mov eax, dword ptr fs:[0]
push eax
mov dword ptr fs:[0], esp
push ecx
mov eax, 549C
call 004BC0B0
push ebx
push esi
push edi
mov edi, dword ptr [ebp+8]
cmp byte ptr [edi], 2B <------判断W一个字节是否ؓ +
mov ebx, ecx
jnz L029
inc edi
push edi
call 0042B0D0
mov ecx, dword ptr [esp+54AC]
mov dword ptr fs:[0], ecx
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
retn 4
L029:
push edi <----- 密文
lea eax, dword ptr [esp+3C]
push eax <------ 密文解密后被保存在这?br />
call 004A0CE0
mov cx, word ptr [ebx+49B162]
xor word ptr [esp+3C], cx
xor edx, edx
mov dh, byte ptr [esp+43]
mov cl, byte ptr [ebx+49B161]
xor eax, eax
mov ah, byte ptr [esp+3F]
mov dl, byte ptr [esp+41]
mov al, byte ptr [esp+3D]
shl edx, 10
or edx, eax
mov al, byte ptr [ebx+49B160]
mov esi, edx
xor cl, byte ptr [esp+3C]
xor edx, edx
mov dh, cl
xor al, byte ptr [esp+38]
mov dword ptr [esp+1C], esi
mov dl, al
mov eax, dword ptr [esp+38]
shr eax, 10
mov cx, dx
movzx dx, byte ptr [esp+39]
mov dh, byte ptr [esp+3E]
mov word ptr [esp+20], cx
mov word ptr [esp+22], dx
xor edx, edx
mov dh, byte ptr [esp+40]
mov dl, al
movzx ax, ah
mov ah, byte ptr [esp+42]
mov word ptr [esp+24], dx
mov word ptr [esp+26], ax
movzx eax, cx
add eax, -138A
cmp eax, 123
ja 0043BF9D
movzx ecx, byte ptr [eax+43C220]
jmp dword ptr [ecx*4+43BFB4]
?MIR3G二次加解密反汇编分析Q三Q——跟t?nbsp; 中有4个赋?br />
mov byte ptr [ebx+49B160], al
mov byte ptr [ebx+49B161], ah
mov word ptr [ebx+49B162], ax
mov word ptr [ebx+49B164], ax
q就是命令体二次解密时的掩码
从一ơ解密的消息体中提取掩码的部?br />
sub eax, edx
cmp eax, 3C ;判断消息体长度是否ؓ60
jnz 0043BF9D
mov ecx, dword ptr [esp+CA8] esp+CA8保存的就是经q一ơ解密的消息体(不包含命令体Q?br />
mov edx, dword ptr [esp+CAC]
mov eax, dword ptr [esp+CB0]
mov dword ptr [esp+38], ecx
mov ecx, dword ptr [esp+CB4]
mov dword ptr [esp+44], ecx
mov ecx, dword ptr [esp+CC0]
mov dword ptr [esp+3C], edx
mov edx, dword ptr [esp+CB8]
mov dword ptr [esp+40], eax
mov eax, dword ptr [esp+CBC]
mov dword ptr [esp+54], ecx
mov ecx, dword ptr [esp+CCC]
mov dword ptr [esp+48], edx
mov edx, dword ptr [esp+CC4]
mov dword ptr [esp+50], eax
mov eax, dword ptr [esp+CC8]
mov dword ptr [esp+60], ecx
mov ecx, dword ptr [esp+CD8]
mov dword ptr [esp+58], edx
mov edx, dword ptr [esp+CD0]
mov dword ptr [esp+5C], eax
mov eax, dword ptr [esp+CD4]
mov dword ptr [esp+24], ecx
lea ecx, dword ptr [esp+1C]
mov dword ptr [esp+1C], edx ;最?0个字?br />
mov edx, dword ptr [esp+CDC]
mov dword ptr [esp+20], eax
mov eax, dword ptr [esp+CE0]
push ecx
mov ecx, ebx
mov byte ptr [esp+50], 0
mov byte ptr [esp+68], 0
mov dword ptr [esp+2C], edx
mov dword ptr [esp+30], eax
mov byte ptr [esp+34], 0
call 0042BD60
lea edx, dword ptr [esp+38] ?0个字?br />
push edx
mov ecx, ebx
mov byte ptr [ebx+49B160], al
mov byte ptr [ebx+49B161], ah
call 0042BD60
mov word ptr [ebx+49B162], ax
lea eax, dword ptr [esp+50]
push eax
mov ecx, ebx
call 0042BD60
mov word ptr [ebx+49B164], ax
提取掩码的函?0042BD60
push ebx
push esi
mov esi, dword ptr [esp+C] esi = arg1 ;消息?br />
mov eax, esi eax = arg1
xor ebx, ebx ebx = 0
lea edx, dword ptr [eax+1] edx = arg+1 Q从W二个字节开?br />
lea ecx, dword ptr [ecx]
L007:
mov cl, byte ptr [eax]
inc eax
test cl, cl
jnz L007
sub eax, edx
cmp eax, 14 查参数长度是否是20
jnb L018
pop esi
xor ax, ax
pop ebx
retn 4
L018:
mov eax, 2 ;eax =2
lea edx, dword ptr [esi+1] ;edx指向W二个字?nbsp; edx = 1
push edi
L022:
mov cl, byte ptr [edx-1] ;cl = arg[edx-1]
movzx esi, byte ptr [edx+8] ;esi = ((long)(arg[edx+8]))
movzx ecx, cl ;ecx = ((long)cl)
add esi, ecx ;esi = esi+ecx
movzx ecx, byte ptr [edx] ;ecx = (long)arg[edx]
cmp ecx, esi ;if(ecx < esi) 跌{?L033
jl L033
lea ecx, dword ptr [eax-2] ; ecx = eax-2
mov edi, 8000 ; edi = 0x8000
sar edi, cl ; edi = edi >> cl
or ebx, edi ; ebx = ebx | edi
L033:
movzx ecx, byte ptr [edx+1] ;ecx = (long)arg[edx+1]
cmp ecx, esi ;if(ecx<esi) 跌{?L040
jl L040
lea ecx, dword ptr [eax-1] ;ecx = eax-2
mov edi, 8000 ;edi = 0x8000
sar edi, cl ;edi = edi >> arg[eax-1]
or ebx, edi ;ebx = ebx | edi
L040:
movzx ecx, byte ptr [edx+2] ;ecx = (long)arg[edx+2]
cmp ecx, esi ;if(ecx < esi) 跌{?L047
jl L047
mov edi, 8000 ;edi = 0x8000
mov ecx, eax ;ecx = eax
sar edi, cl ;edi = edi >> cl
or ebx, edi ;ebx = ebx | edi
L047:
movzx ecx, byte ptr [edx+3] ;ecx = (long)arg[edx+3]
cmp ecx, esi ; if(ecx < esi) 跌{?L054
jl L054
lea ecx, dword ptr [eax+1] ;ecx = eax+1
mov edi, 8000 ;edi = 0x8000
sar edi, cl ;edi = edi >> cl
or ebx, edi ;ebx = ebx | edi
L054:
movzx ecx, byte ptr [edx+4] ;ecx = (long)arg[edx+4]
cmp ecx, esi ; if(ecx < esi) 跌{?L061
jl L061
lea ecx, dword ptr [eax+2] ;ecx = eax+2
mov edi, 8000 ;edi = 0x8000
sar edi, cl ;edi = edi >> cl
or ebx, edi ;ebx = ebx | edi
L061:
movzx ecx, byte ptr [edx+5] ;ecx = (long)arg[edx+5]
cmp ecx, esi ; if(ecx < esi) 跌{?L068
jl L068
lea ecx, dword ptr [eax+3] ;ecx = eax+3
mov edi, 8000 ;edi = 0x8000
sar edi, cl ;edi = edi >> cl
or ebx, edi ;ebx = ebx | edi
L068:
movzx ecx, byte ptr [edx+6] ;ecx = (long)arg[edx+6]
cmp ecx, esi ; if(ecx < esi) 跌{?L075
jl L075
lea ecx, dword ptr [eax+4] ;ecx = eax+4
mov edi, 8000 ;edi = 0x8000
sar edi, cl ;edi = edi >> cl
or ebx, edi ;ebx = ebx | edi
L075:
movzx ecx, byte ptr [edx+7] ;ecx = (long)arg[edx+7]
cmp ecx, esi ; if(ecx < esi) 跌{?L082
jl L082
lea ecx, dword ptr [eax+5] ;ecx = eax+5
mov esi, 8000 ;edi = 0x8000
sar esi, cl ;edi = edi >> cl
or ebx, esi ;ebx = ebx | edi
L082:
add eax, 8 ;eax = eax+8
add edx, 0A ;edx = edx+0x0A
cmp eax, 0A ;if(eax <= 0X0A) 跌{?L022
jle L022
movzx edx, bl ;edx = (long)bl ??扩展
movzx eax, bh ;eax = (long)bh ??扩展
pop edi ;
xor edx, 87 ;edx = edx ^ 0x87
xor eax, 87 ;eax = eax ^ 0x87
shl edx, 8 ;edx << 8
pop esi
or eax, edx ;eax = eax | edx
pop ebx
retn 4
xQ消息的加解密部分已l全部还?
lea eax, dword ptr [esp+CAC] ;(3244) 距离栈顶811个存储单?
push eax ;eax入栈 解密后要存入的地址入栈
add edi, 10 ;edi=从字W串W?7个字节开始往后的?br />
push edi ;密文入栈
call 004A0BD0 ;跟进发现q是普通的一ơ解密,解密后数据存在esp+CAC?
mov byte ptr [esp+eax+CA8], 0
xor eax, eax
mov ecx, 100 ;ecx=100 (256) 循环ơ数
lea edi, dword ptr [esp+3F8] ;edi
rep stos dword ptr es:[edi] ;
lea ecx, dword ptr [esp+3F8]
push ecx
lea edx, dword ptr [ebx+3CD864]
push edx
mov ecx, ebx
call 0042C440 ;二次解密Q生成回复密?br />
lea eax, dword ptr [esp+3F8]
push eax
mov ecx, 004D5C70
call 0049F770 ;调用messageSend(char* msg)
lea eax, dword ptr [esp+CA8]
lea edx, dword ptr [eax+1]
L023:
mov cl, byte ptr [eax]
inc eax
test cl, cl
jnz L023
sub eax, edx
cmp eax, 3C
jnz 0043BF9D
mov ecx, dword ptr [esp+CA8]
mov edx, dword ptr [esp+CAC]
mov eax, dword ptr [esp+CB0]
mov dword ptr [esp+38], ecx
mov ecx, dword ptr [esp+CB4]
mov dword ptr [esp+44], ecx
mov ecx, dword ptr [esp+CC0]
mov dword ptr [esp+3C], edx
mov edx, dword ptr [esp+CB8]
mov dword ptr [esp+40], eax
mov eax, dword ptr [esp+CBC]
mov dword ptr [esp+54], ecx
mov ecx, dword ptr [esp+CCC]
mov dword ptr [esp+48], edx
mov edx, dword ptr [esp+CC4]
mov dword ptr [esp+50], eax
mov eax, dword ptr [esp+CC8]
mov dword ptr [esp+60], ecx
mov ecx, dword ptr [esp+CD8]
mov dword ptr [esp+58], edx
mov edx, dword ptr [esp+CD0]
mov dword ptr [esp+5C], eax
mov eax, dword ptr [esp+CD4]
mov dword ptr [esp+24], ecx
lea ecx, dword ptr [esp+1C]
mov dword ptr [esp+1C], edx
mov edx, dword ptr [esp+CDC]
mov dword ptr [esp+20], eax
mov eax, dword ptr [esp+CE0]
push ecx
mov ecx, ebx
mov byte ptr [esp+50], 0
mov byte ptr [esp+68], 0
mov dword ptr [esp+2C], edx
mov dword ptr [esp+30], eax
mov byte ptr [esp+34], 0
call 0042BD60
lea edx, dword ptr [esp+38]
push edx
mov ecx, ebx
mov byte ptr [ebx+49B160], al
mov byte ptr [ebx+49B161], ah
call 0042BD60
mov word ptr [ebx+49B162], ax
lea eax, dword ptr [esp+50]
push eax
mov ecx, ebx
call 0042BD60
mov word ptr [ebx+49B164], ax
mov ecx, dword ptr [esp+54AC]
mov dword ptr fs:[0], ecx
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
retn 4
分析以上代码可以单得Z下结?br />
1. 从call 004A0BD0 的调用可以简单分析出二次密文可能也是标准消息l构
2. 通过Ҏ未更新版本与最新版本对3EF的发送情况,未更新版本发?EFӞ消息体是I,命o体其他参C?Q再l分析,
未更新的版本?EF其实是割肉的命?..........
最新版本的客户端收Cơ密文后Q经q一些处理,?EF为命令发向服务器
3. 从add edi, 10可以猜测Q二ơ密文的命o体可能是qh人用?br />
4. 从call 0049F770q个调用断定 esp+3F8 是二次解密后的明文
5. 对于call 0042C440q个调用Q可以猜,q个是二次解密的函?br />
6. 服务器发来的密文对于本次解密是没有媄响的Q只是对它进行了一些操作,生成?个数q进行如下保?br />
mov byte ptr [ebx+49B160], al
mov byte ptr [ebx+49B161], ah
mov word ptr [ebx+49B162], ax
mov word ptr [ebx+49B164], ax
7. 二次解密函数?个参? arg1 = dword ptr [ebx+3CD864] , arg2 = [esp+3F8]
也就是根据dword ptr [ebx+3CD864]来生成解密明文,q存入地址esp+3F8Q于是ebx+3CD864成了解密的关键
........客户端与Gateserver,Loginserver的数据互换,省略
1. 客户端向Gameserver发送[**d用户?角色?验证?/验证?/版本验证?1/0]
2. Gameserver向客L发送密文,cM#eLrBHMNx<F=hgmlYA]X]ENtpGM`X@?PuN`LwT_m>RmleJ_l{PAMHQ?pUCpdbENa<F`pjBllQC=HSC\\pT?LduQ_y=PQM>JptK!
3. 客户端向Gameserver发送解密后密文Q类?3<<<<<Jx?<<<<<<<<A>xZCNLSHoPpAnQRF?ljIaaUPmlSF^L_BmtfFODJA_X\\A]T`GNlq@L!
........Ƣ迎信息Q装备信息等省略
数据格式
[#][标识位][指o头][消息体][!]
例如 #3<<<<<B\\<<<<<<<<<mi{EhL!
命ol构?br />
typedef struct tag_TDEFAULTMESSAGE
{
int nRecog;
WORD wIdent; 3EF
WORD wParam;
WORD wTag;
WORD wSeries;
} _TDEFAULTMESSAGE, *_LPTDEFAULTMESSAGE;
解密命o?lt;<<<<Jx?<<<<<<<<得到wIdent值是3EF(1007)QOD打开Mir3.exeQ查扑ָ?00003EFQ跟t进入,得到如下代码
>>Method1(未知参数)
push esi
push edi
push 0
push 0
push 0
push 0
mov esi, ecx
push 3EF
lea edi, dword ptr [esi+18] ;esi+18 是命令结构体的首地址
push edi ;命ol构体首地址
call 004A0D00 ;生成命ol构?(edi,3ef,0,0,0,0)
mov eax, dword ptr [esp+C] ; eax保存W一个参?br />
push 0
push eax
push edi
mov ecx, esi ;传递this指针Q?br />
call 0049E450 ;发送数据,arg3: 0, arg2: W一个参? arg1: 命ol构?br />
pop edi
pop esi
retn 4
单分析,入栈???EF,则对应结构体,该函数至?个参敎ͼ目前不知q个参数是什么意思,跟进0049E450看看
>>Method2(命ol构?未知参数,0)
mov eax, dword ptr [esp+4] ;eax=命ol构?br />
push ebx
push ebp
push esi
mov esi, ecx ;得到this指针
push 20
lea ebp, dword ptr [esi+24] ;esi+18是命令结构体Q加密命令结构体得到的字W串保存在esi+24?br />
push ebp
push eax
call 004A0CA0 ;跟进发现是加密命令结构体,arg3: 20(32), arg2: esi+24, arg1: 命ol构体地址
mov eax, dword ptr [esi+14] ;eax=esi+14=标识?br />
cmp eax, 9
jl L015 ;如果标识位小?Q则跛_L015
mov dword ptr [esi+14], 1 ;否则标识位重设ؓ1
jmp L017
L015:
inc eax
mov dword ptr [esi+14], eax ;标识位自?
L017:
mov edx, dword ptr [esp+14] ;edx=W?个参?br />
test edx, edx ;
je L048 ;如果W?个参Cؓ0Q则跌{到L048
mov eax, dword ptr [esp+18] ;eax=W?个参? 0
test eax, eax
push edi
jnz L031 ;eax不等?则蟩?br />
mov eax, edx ;eax=W?个参?br />
lea edi, dword ptr [eax+1]
L026:
mov cl, byte ptr [eax] ;cl=W?个参数第1个字?br />
inc eax ;eax
test cl, cl ;循环得到W一个参数的长度
jnz L026 ;没到字符串尾则l@?br />
sub eax, edi
L031:
push 2000
push eax
lea edi, dword ptr [esi+44]
push edi
push edx
call 004A0B10 ;调用加密函数Q将edx加密Q保存在esi+44?br />
mov ecx, dword ptr [esi+14]
push edi
push ebp
push ecx
lea ebx, dword ptr [esi+2044]
push 004CBFE4 ; #%d%s%s!
push ebx
call 004BB568
add esp, 14
pop edi
jmp L056
L048:
mov edx, dword ptr [esi+14]
push ebp
push edx
lea ebx, dword ptr [esi+2044]
push 004CBFC4 ; #%d%s!
push ebx
call 004BB568 ;sprintf 格式化发送给服务器端的数?br />
add esp, 10
L056:
mov eax, ebx
lea edx, dword ptr [eax+1]
L058:
mov cl, byte ptr [eax]
inc eax ;q个循环得到数据长度
test cl, cl
jnz L058
push 0
sub eax, edx
push eax ; 数据长度
mov eax, dword ptr [esi+6044]
push ebx ; Data Q要发送的数据
push eax ; Socket对象
call <jmp.&WS2_32.#19> ;q里是调用send(Socket对象, 要发送的数据, 数据长度, 0 (flag));
pop esi
pop ebp
pop ebx
retn 0C
L017和L031说明Method1和Method2中的未知参数是明文消息体,Method1只有1个消息体参数
OD打开Mir3.exe(Mir3G_20070108) -> ASCII ->扑ֈ**%s/%s/%d/%d/%d/1/%d->双击Q得到如下代?/p>
q段代码是将几个参数Q用sprintf生成字符Ԍ然后6BIT加密Q以#%d%s!格式发送到服务器端
0049E2D0 mov eax, dword ptr [esp+14] ; eax=arg5Q第5个参敎ͼ
0049E2D4 sub esp, 600 ; 预留1536个存储单?br />
0049E2DA push esi
0049E2DB push edi
0049E2DC mov edi, dword ptr [<&USER32.wsprint>; USER32.wsprintfA
0049E2E2 push eax ; /<%d> ;eax入栈
**%s/%s/%d/%d/%d/1/arg5
0049E2E3 mov eax, dword ptr [esp+618] ; | eax = arg3
0049E2EA mov esi, ecx ; |
0049E2EC mov ecx, dword ptr [esp+61C] ; | ecx = arg4
0049E2F3 mov edx, ecx ; | edx=ecx=arg4
0049E2F5 xor edx, FA0280AF ; | edx=arg4异或FA0280AF
0049E2FB push edx ; |<%d> edx入栈
**%s/%s/%d/%d/arg4异或FA0280AF/1/arg5
0049E2FC mov edx, eax ; | edx=eax=arg3
0049E2FE xor edx, ecx ; | edx=arg3异或arg4
0049E300 mov ecx, dword ptr [esp+614] ; | ecx=arg1
0049E307 xor edx, 5580AF27 ; | edx = edx异或5580AF27
0049E30D push edx ; |<%d> edx入栈
**%s/%s/%d/arg3异或arg4再异?580AF27/arg4异或FA0280AF/1/arg5
0049E30E xor eax, 3EB2C5CC ; | eax = arg3异或3EB2C5CC
0049E313 push eax ; |<%d>
**%s/%s/arg3异或3EB2C5CC/arg3异或arg4再异?580AF27/arg4异或FA0280AF/1/arg5
0049E314 mov eax, dword ptr [esp+620] ; | eax = arg2
0049E31B push eax ; |<%s>
**%s/arg2/arg3异或3EB2C5CC/arg3异或arg4再异?580AF27/arg4异或FA0280AF/1/arg5
0049E31C push ecx ; |<%s>
**arg1/arg2/arg3异或3EB2C5CC/arg3异或arg4再异?580AF27/arg4异或FA0280AF/1/arg5
0049E31D lea edx, dword ptr [esp+220] ; | edx 指向上面预留?536个存储单元中最?12个单元的首地址
0049E324 push 004CBFCC ; |**%s/%s/%d/%d/%d/1/%d
0049E329 push edx ; |s
0049E32A call edi ; \wsprintfA edx指向生成的明文字W串首地址
0049E32C add esp, 20 ; 前移32个存储单?br />
0049E32F push 1FF 512入栈 <加密函数W?个参?gt;
0049E334 lea eax, dword ptr [esp+20C] eax =明文字符?br />
0049E33B push eax ; /String
0049E33C call dword ptr [<&KERNEL32.lstrlenA>] ; \lstrlenA 得到明文的长度,保存在eax?br />
0049E342 push eax eax入栈 明文长度入栈<加密函数W?个参?gt;
0049E343 lea ecx, dword ptr [esp+10] ecx=W一ơ入栈的edi的|应该是某个成员变?br />
0049E347 push ecx ecx入栈<加密函数W?个参?gt;
0049E348 lea edx, dword ptr [esp+214]
0049E34F push edx 明文字符串首<加密函数W?个参?gt;
0049E350 call 004A0B10 调用加密函数
fnEncode(char *strSrc, char *strDest, int lenSrc, 512)
...................
后面是发送信?/p>