free Q.744303 (只接受志同道合的技术研究伙伴,验证说明“看雪”,无聊人士勿扰)
潜水很多年,第一次发技术贴,不会排版还请版主帮忙。。
下载了某游戏的客户端,目的就是练习一下算法逆向,并且目标不是汇编代码,是C++代码。
研究了两天,有可能有疏漏,还请大侠批评指教!
用到的调试工具只有OllyDBG。
一、获取与运算封包Key
获取很简单,直接连接服务器,会收到服务器返回的包含了SendKey和RecvKey的钥匙包。
经过一定的算法,得到两个Key,在后续的游戏中,将使用这两个Key进行加密通信。
封包[0][1]为封包长度,不进行加解密操作。
封包:
2C 00 20 00 00 0A 00 0A 00 BD
AD F2 A5 D5
75 61 6E 78 75
0B 73 D6 7C
38 38 38 40 31 36 33 2E 63 6F 6D 00 00 00 00 00 00 00 00 00 00
下断点在 AD 这个字节上,运行,会断在访问的地址,如下汇编代码:
mov edx, dword ptr [esi+11] //取得 0B 73 D6 7C
mov esi, dword ptr [esi+8] //取得 AD F2 A5 D5
mov ecx, edx //第二个KEY开始运算
mov ebx, edx
xor ecx, 6D23CF
sub ecx, 6D2399
xor edx, FFFFFFCF
not ecx
add edx, 67
xor ebx, 2E6D23CF
and ecx, 0FFFF00
not edx
sub ebx, 2E6D2399
shl edx, 18
not ebx
shr ebx, 18
or ecx, ebx
or ecx, edx
push ebp
push ecx
mov edx, esi //第一个KEY开始运算
mov ecx, esi
xor edx, 6D23CF
xor ecx, 2E6D23CF
sub edx, 6D2399
sub ecx, 2E6D2399
xor esi, FFFFFFCF
not edx
not ecx
add esi, 67
and edx, 0FFFF00
shr ecx, 18
or edx, ecx
not esi
shl esi, 18
or edx, esi
经过逆向,逆出C++代码如下:
DWORD __stdcall GetRecvKey(DWORD PacketRecvKey)
{
DWORD sKey = 0;
sKey = (((~((PacketRecvKey ^ 0x006D23CF) - 0x006D2399)) & 0x0FFFF00)
| (~((PacketRecvKey ^ 0x2E6D23CF) - 0x2E6D2399)) >> 0x18)
| ((~((PacketRecvKey ^ 0xFFFFFFCF) + 0x67)) << 0x18);
return sKey;
}
DWORD __stdcall GetSendKey(DWORD PacketSendKey)
{
DWORD sKey = 0;
sKey = (((~((PacketSendKey ^ 0x6D23CF) - 0x6D2399)) & 0xffff00)
| ((~((PacketSendKey ^ 0x2E6D23CF) - 0x2E6D2399)) >> 0x18))
| ((~((PacketSendKey ^ 0xFFFFFFCF) + 0x67)) << 0x18);
return sKey ;
}
这两个KEY逆完后,继续逆封包加解密算法,中间碰到一个包,经过分析是心跳包,以07 00 01开头。
其实只是以01开头,刚才说过第一位和第二位是长度,所以07 00 是封包长度而已。心跳包是提交时间的。
这个封包的组成是这样的:
07 00 01 (XX XX XX XX)_time32()
蓝色是长度,红色是封包类型,后面的四位由MSVCR80.DLL里面的_time32() 函数得到结果。
在VS 2005+ 里可以直接使用这个函数,头文件:time.h。
通过类似的断点方法,找到解密用的函数,汇编如下:
push ebx
push ebp
mov ebp, dword ptr [esp+14]
mov ecx, dword ptr [ebp]
push esi
mov esi, dword ptr [esp+10]
mov ebx, esi
and ebx, 3 //求余数,下面以DWORD(4Byte)操作,如果有余数还要另行操作
shr esi, 2
push edi
mov edi, dword ptr [esp+18]
je short 004D5605
lea ecx, dword ptr [ecx] //取得传入的经过运算的KEY
sub esi, 1
lea eax, dword ptr [ecx+esi]
xor edx, edx
div dword ptr [51FEDC]
add edi, 4
mov ecx, dword ptr [edx*4+51A620] //进行完上面的运算,进行异或,注意是以DWORD(4字节)为单位的
add ecx, 2E6D23C1
xor dword ptr [edi-4], ecx
test esi, esi //全完成了吗?
ja short 004D55E0 //没有全完成的话继续
xor edx, edx
mov eax, ebx
div dword ptr [51FEDC]
xor ecx, dword ptr [edx*4+51A620]
test ebx, ebx //有余数吗?
jbe short 004D562F //没有的话就不继续处理剩下的字节了
lea ebx, dword ptr [ebx]
xor byte ptr [edi], cl
sub ebx, 1
add edi, 1
shr ecx, 8
test ebx, ebx //余下的几个字节处理完了吗?
ja short 004D5620 //没处理完继续
mov eax, dword ptr [ebp]
mov ecx, eax
shl ecx, 5
sub ecx, eax
pop edi
add ecx, 8088405
pop esi
mov dword ptr [ebp], ecx
pop ebp
mov eax, 1
pop ebx
retn
经过分析,逆出的C++代码如下:
VOID __stdcall EncryptionPacket(DWORD *enbyte,int len,DWORD PacketKey)
{
int NextLen = len % 4,PacketLength = len / 4;
int i=0,j=0;
DWORD NextKey = 0,AddNum = 0;
BYTE *NewPacketAddr = (BYTE *)enbyte;
AddNum = PacketKey;
while (PacketLength--)
{
AddNum += PacketLength;
NextKey = Pack_Tabel[AddNum % 0x162F] + 0x2E6D23C1;
enbyte[i] = enbyte[i] ^ NextKey;
AddNum = NextKey;
i++;
}
j=i*4;
NextKey ^= Pack_Tabel[NextLen % 0x162F];
if (NextLen != 0)
{
while (NextLen--)
{
(BYTE)NewPacketAddr[j] = (BYTE)NewPacketAddr[j] ^ (BYTE)NextKey;
NextKey = NextKey >> 8;
j++;
}
}
}
至此封包算法已经逆完了,经过测试,是正确的:
取得服务端封包验证KEY...
开始发送帐号密码...
验证正常...
取得人物信息了...
帐号:h3389@vip.qq.com
人物:凌行恶
性别:男
等级:4
其实直接套汇编速度非常快,解了此游戏算法的人也不少,这里只是作为真正逆向代码的练习方法罢了。
接下来说一下是如何将算法逆成C++代码的,上面只给出了结果,其实我在个人空间里面分了两个贴子来说的,在这里直接就贴在下面给各位朋友参考,有不妥的地方还请大家批评指教:
寻找修改变量的代码,一直找到计算完毕,废话不多说,拿昨天的代码再来继续做个示例,看看我是如何把它逆成C++代码的:
(一)使用变量修改追踪找出单独的计算代码:
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课