在对一款游戏的数据分析中,明文封包的重要地位是无可取代的,准确的发送封包可以让我们在实现功能时避免很多风险与麻烦。比如,我们可以节省大量的功能函数的参数分析与特征定位,也可以跳过大量中间函数的判断,直接实现功能,甚至可以实现很多普通玩家无法做到的特殊功能。
NesCafe.Theme 2.0 [Themes]


然而,这一切都是建立在“准备”这两个字上面的,如果胡乱的使用封包,或者某些结构数据没有分析完全,很有可能会让事情变糟,掉线,崩溃,甚至封号等等。
准确的分析封包,不只限于对单个封包的结构进行分析,因为很多功能并不单一封包可以实现的,如果不能将所有相关的封包一一发送,也许无法实现某些功能,甚至可能会在“漏发”之后出现一些未知的错误。下面我们对一款本地架设的游戏封包进行分析。

我们先调到明文封包的位置,分析一个最简单的打开NPC。

图中是最外层明文包头部地址,之前的线程发包分析过程我们就不做讲解了。
我们在这个头部下断,并打开NPC,游戏会断下

返回后重新下断,对结构体进行分析
这款游戏有一个特点,就是我们需要通过游戏自带的函数申请内存,才可以对明文CALL进行调用,否则游戏就会崩溃,虽然功能也会实现。

申请结构体之后会在下面的函数中进行组包

根据组包函数的参数可以分析出我们只需要传入一个NPCID即可
那么我们通过代码注入器来对这个封包进行发送

发送之后游戏没有反应,NPC并没有打开。多次尝试之后依然如此。这说明我们找的封包并不是打开NPC的。相信很多人已经想到问题所在了,再打开NPC之前,有一个前置封包对我们进行了干扰,而这个封包就是选中NPC。而游戏中没有反应的原因是因为选中NPC的本地效果在外层,所以并没有显示出来。
那么我们通过左键先选中NPC,然后再次点击左键打开,这时游戏会断到一个新的返回地址。

这个函数和选中NPC唯一不同的地址就是组包函数的地址,那么我们修改之后再次调用

打开NPC成功了
以上的问题常常会对萌新造成一些麻烦,但是注意一些还是可以避免的。
单个的功能实现比较简单,但是如果我们将这些封包以一定的逻辑组合起来,编成一个脚本的话,会发现可能会出现一些未知的错误。
比如,我们发送以下连续封包 打开NPC----交任务----接任务
这时就会发现,接受任务无法成功

这时如果手动打开NPC也无法打开NPC,以及接受任务。
这说明我们在这个过程中忽略一些东西,而出现这种问题的主要原因是我们通过函数头部下断去分析封包很麻烦,各种跟包和心跳会影响我们的判断。
想要避免这种问题的出现,我们就需要对明文封包进行HOOK,通过调试输出对发包流程进行观察。
由于外层函数HOOK起来比较麻烦,而且还需要调用游戏自带的申请内存。所以我们在内层寻找一个比较合适的地方进行HOOK,并分析了加密函数,自我实现send发包。


具体的分析过程略过,感兴趣的话可以通过相关课程进行了解。
接着上面继续分析,由于提交任务是成功的,但是接受任务失败了,所以我们主要对提交任务的封包进行观察

在提交任务后,我们得到了这些封包,第一个包很明显是交任务包,第二个包则是打开NPC,单独调用打开NPC,会跟着输出一个包

单独发送一次交任务包,会跟出另外两个包
那么也就是说我们在提交任务之后,需要再次发送一个打开NPC包,这样才能够继续接受任务。
下面做一个连续测试,打开NPC----交任务----打开NPC----接任务

接受任务成功,说明我们的分析是没有问题的。
以上就是我们对封包实现功能的分析过程,在这个过程中,HOOK封包使我们的分析简单多了很多,虽然通过OD去逐条分析每一个访问也可以达到效果,但是很明显会麻烦很多。
在一个游戏里得到明文发包函数以后
对于我们逆向最大的用处,就是去HOOK,然后分析封包
因为如果在OD等调试软件中下断分析是很难的,封包的种类很多很频繁,单一的动作也不一定只是一个封包,所以HOOK明文包进行分析是必然的
也有一些工具例如WPE等,也是做此类事情的,不过他只是HOOK的发包函数,对于加密的封包,还要去找解密函数,HOOK明文函数就相当于直接省去了解密过程。
我们要HOOK的位置

首先我们要在明文发包CALL上面找5字节或则大于5字节的空间就行HOOK

这5字节即可
然后将去修改成JMP xxxx 跳转到我们自己DLL的子程序里
执行我们想执行的过程(调试输出封包信息)
然后再跳转回去
此过程不能对游戏代码产生任何影响,包括寄存器的值和堆栈。
代码如下
void Call_Hook明文包(){DWORD Hook地址 = Addr_hook明文包;DWORD Hook子程序指针 = (DWORD)Hook明文包子程序;DWORD 跳转值=Hook子程序指针-Hook地址-5;Call_提升权限(TRUE);//提升EXE权限DWORD pid=NULL;DWORD WriteSize=NULL;GetWindowThreadProcessId(Call_获取窗口句柄(),&pid);//获得进程IDhProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);//打开进程__try{byte Temp=0xE9;//写内存有三种方式 不同情况进行不同应用WriteProcessMemory(hProcess,(LPDWORD)(Hook地址 + 0x00),&Temp,1,&WriteSize);DWORD Temp1=跳转值;WriteProcessMemory(hProcess,(LPDWORD)(Hook地址 + 0x01),&Temp1,4,&WriteSize);}__except (1){Call_输出调试信息("QQ西游 Hook明文包异常\r\n");}}在不用HOOK的时候我们要对其进行还原,否则直接卸载DLL 游戏会因为找不到跳转子程序而崩溃的void Call_UnHook明文包(){__try{DWORD Hook地址 = Addr_hook明文包;DWORD WriteSize=NULL;byte Temp []={0x8B,0x44,0x24,0x08,0x50};WriteProcessMemory(hProcess,(LPDWORD)Hook地址,&Temp,5,&WriteSize);}__except (1){Call_输出调试信息("QQ西游 UnHook明文包异常\r\n");}}跳转过去的子程序员任意输出我们想要的信息

同样用 pushad 和popad 保存和还原寄存器 防止我们的代码对游戏产生影响
void Call_输出明文包信息(){p =new byte[g_包长];ReadProcessMemory(hProcess,(LPCVOID)g_包地址,p,g_包长,0);for (int i=0;iint)g{sprintf(s,"%02X",p[i]);strcat_s(a,s);}Call_输出调试信息("QQ西游 flag1:%x flag2:%x flag3:%x 包长:%x %s\r\n",g_flog1,g_flog2,g_flog3,g_包长,a);sprintf(a,"%s","");//格式化delete p;}__declspec(naked) void Hook明文包子程序(){__asm{pushadmov eax,[esp+0x28]//由于有 pushad 相当于sub esp,20 +0x8 变+0x28mov ecx,[eax]mov g_flog1,ecxmov ecx,[eax+4]mov g_flog2,ecxmov ecx,[eax+8]mov g_flog3,ecxmov ecx,[eax+0xC]mov g_包地址,ecxmov edx,[eax+0x10]mov ebx,[eax+0x14]sub edx,ecxmov g_包长,edx}Call_输出明文包信息();__asm{popadmov eax,[esp+8]push eaxjmp Addr_hook明文包返回}}那么我们来看一下 调试输出的效果


可以捕捉到游戏里所有的封包了
进行随意分析了
本文介绍了在游戏数据分析中如何利用WPE封包分析工具,通过HOOK明文封包来实现游戏功能。分析了封包发送的逻辑,包括打开NPC、交任务、接任务等,强调了HOOK的重要性,以及在封包分析中可能出现的错误和解决办法。
1156

被折叠的 条评论
为什么被折叠?



