逆向在线游戏:创建一个包记录器和编辑器

发表于:2016-02-26 13:30:00 来源:  黑客与极客 阅读数(0人)

免责声明:逆向猎龙游戏完全出于学习目的,我反对本文提供的信息被用于任何恶意行为,并且目前发现的所有漏洞已提交至Aeria游戏或已经被修复。

0×01 概述


在这篇博客中,我准备向大家展示如何逆向在线游戏猎龙,为其创建一个包记录器和编辑器。而在后续的博客中我可能还会展示如何发送我们自己的数据包。


从数据包着手,可以做你想做的任何事情,包括寻找漏洞,相关功能函数。猎龙是一款Aeria Games开发的大型多人在线游戏。尽管现今这些游戏都捆绑了一系列的反作弊软件,例如 nProtect、GameGuard、AhnLab、HackShield和 XIGNCODE3,但事实上本文中的方法适用于任何游戏。


这些反作弊软件的功能类似杀毒软件,即扫描已知类型的作弊手段。如果你运行了任何这种类型的作弊软件,反作弊软件就会将游戏关闭。这种软件同时还有一个内核模式的驱动,阻止使用分析类软件,例如Ollydbg或Cheat Engine,通过在ring0上HOOK某种API函数可以阻止在ring3上使用OpenProcess, ReadProcessMemory和WriteProcessMemory。(这些API函数经常用作分析)。更重要的是,这些反作弊软件使用了反调试技术,扫描内存以确保反作弊软件的代码不被修改,游戏时刻被保护着。


而绕过反作弊软件最简单的办法就是阻止其运行,但是一旦不运行,就有95%的几率在1-5分钟之内与游戏服务端断开连接。这是由于大部分的反作弊软件会有一个叫做”heartbeat packet”来确保软件时刻运行。服务端发送一个数据包至客户端,然后反作弊软件调用函数执行算法程序,返回一个正确的值,接着这个值再发送至服务端。如果返回的数据不正确或者压根没有返回,此时就会断开连接。这种工作机制类似于安装软件的许可证。如果你键入错误的Key,软件不会运行。但这些反作弊软件的代码通常都是运行在虚拟机下的字节码,导致这些算法非常难以被逆向。


猎龙游戏是一款大型多人在线游戏,不具备任何类型的反作弊软件,也没有包括很复杂的代码,因此这款游戏适合作为逆向在线游戏的教程,那下面就开始吧。


注意:2016年1月28日更新后Aeria Games为猎龙游戏增加了XIGNCODE3,如果你不知道怎么绕过,那么接下来的分析将不能完成。而目前,并不需要通过一个”heartbeat”包,只需找到XIGNCODE3的初识化函数,然后修改其中的代码导致其不能初始化即可。(在函数返回值前,植入mov eax,1即可)。


0×02 准备工作


                    

l 熟悉 x86 汇编, 逆向工程以及 Win32 APIs


l Aeria Games 账户 (http://aeriagames.com)

l 猎龙游戏在线 (http://dragomonhunter.aeriagames.com/)


l Ollydbg v1.10 (http://www.ollydbg.de/)


l IDA Pro (https://www.hex-rays.com/products/ida/support/download_freeware.shtml)


l 通过Sirmabus将IDA函数字符串 关联到 插件上


(http://sourceforge.net/projects/idafunct ionstringassociate/)


l Keygener Assistant v2.0 (http://www.softpedia.com/get/Programming/Other-Program ming-Files/Keygener-Assistant.shtml)


0×03 分析


警告:如果分析这些类型的恶意软件,为了防止感染你的电脑应在VMware上运行。但由于本次的软件来源是可信的,因此可以放心运行。


再一次确保你的猎龙游戏是最新版的。




打开Keygener Assistant,点击”scanning”,在猎龙游戏的安装文件夹下找到Game.bin。请确保将“文件类型”选为“所有文件”,否则在在下图的Keygener Assistant中不会找到该文件(尽量它是.bin的扩展名,但是根据PE文件头,其实它是.exe文件)。




Keygener Assistant显示Game.bin文件没有加壳,同时指出了hash和加密特征的偏移以及编译器。下面通过IDA进一步分析。




根据IDA的分析,代码没有被混淆。




输入表也没有混淆。




字符串也没有加密。


在多数情况下,如果分析的是恶意软件,上述的步骤还需要做进一步的确认。


这个游戏使用的是Gamebryo Game Engine (http://www.gamebryo.com/)>来阻止游戏被逆向的,根据我多年的经验,断定使用的是下图IDA中相关的字符串:




当我分析在线游戏的时候,经常会用到IDA的字符串关联插件。这个插件可以在函数入口处创建引用字符串的注释。在IDA中,按ALT+F6,如下图:




点击继续,插件开始分析。软件的大小决定分析的时间。而这个软件只有14MB,因此不会太久。之后你会看见函数引用字符串的注释:




所有就绪,我们开始创建包编辑器。问题是,从哪里着手呢?发送和接受数据包函数的路径有太多太多。


一种方法就是找到一些成员的指针,例如游戏人物的坐标,这些可能存在数据包中并发送至服务端。接着找到那个代码访问该指针,移动你的人物角色,之后不断跟踪直到你找到发送人物位置数据包的代码。


最好的办法就是从结束的地方开始。我这么说是什么意思呢?在Windows上运行,数据包最后发送的地方或者数据包刚开始接收的地方都会出现一个由WS2_32.dll导出的Winsock函数。因此在这里我会展示如何追踪发送加密数据包之前的函数,可能在以后的博客中会展示如何记录加密后的数据包。


Winsock Send APIs:


send (https://msdn.microsoft.com/en-us/library/windows/desktop/ms740149(v=vs.85).aspx)


sendto (https://msdn.microsoft.com/en-us/library/windows/desktop/ms740148(v=vs.85).aspx)


WSASend (https://msdn.microsoft.com/en-us/library/windows/desktop/ms742203(v=vs.85).aspx)


Winsock Receive APIs:


recv (https://msdn.microsoft.com/en-us/library/windows/desktop/ms740121(v=vs.85).aspx)


recvfrom (https://msdn.microsoft.com/en-us/library/windows/desktop/ms740120(v=vs.85).aspx)


WSARecv (https://msdn.microsoft.com/en-us/library/windows/desktop/ms741688(v=vs.85).aspx)


现在检查这些函数的导入表,(最简单的方法就是在IDA的”library”中滚动查找WS2_32)。




可以看到,游戏使用WSASend发送数据包,使用WSARecv接收数据包。


通过交叉引用功能查看发送数据包的函数。




可以看到两个函数调用WSASend。查看第一个函数。



如果WSASend返回值为-1即十六进制的0xFFFFFFFF,将会执行WSAGetLastError,随后从该函数返回一个0值,意味着返回错误,这样由于数据包的错误导致游戏关闭。如果WSASend返回0意味着数据包正确,发生跳转继续执行后续函数。


在WSASend函数的下面有一个WSAAsyncselect函数。我猜测可能这个游戏在运行的时候不仅仅只开放一个连接,可能是两个,因为有两个函数调用WSASend。可以看到WSASend的buffer计数器参数一直是1,这个游戏同样可以使用WSAAsyncSelect让游戏知道数据包正确发送了,这样保持分组的顺序让下一个数据包继续发送。


在WSAAsyncSelect之前的“mov [esi+288Ch], eax”也很有意思。可能是一种互斥元素,或者网络数据结构或者是一个类,其他数据包发送之前必须要被设置。


看看第二个调用WSASend的函数。


call ds:WSASend


cmp eax, 0FFFFFFFFh


jnz short loc_F1E730




和第一个函数有些类似,如果向上回溯,可以看到另一个调用WSAAsyncSelect的地方




mov [eax+288Ch], esi


我们又发现了这个指针+0x288C可能是网络数据结构或者类。通过Ollydbg的内存监视在这个地址处下断点,能够找到这个指针成员的值,接着回溯该网络成员指针。


这里我使用的是Ollydbg的动态调试功能。因此将游戏附加至Ollydbg。


在WSASend处下断点(使用OD的热键F2即可)




通常我会通过游戏的聊天窗口回溯。输入”Hello”,点击enter,OD就会断在WSASend,看到如下图所示:(注意断点时间不宜过长,否则就会断开连接)




检查一下数据包是否被加密了。跟踪WSABUF数组(pBuffers)在这里是0x0018FC10。


这是一个WSABUF的结构,C++形式:




“len”是buffer的长度,”buf”是指向buffer的指针。这里我们只有一个buffer,(nBuffers=1)这个数组是这样的:

0x0018FC10 (pBuffers) + 0×00 = the length of the buffer (0x0018FC10)


0x0018FC10 (pBuffers) + 0×04 = pointer to the buffer (0x0018FC14)


由于软件是32-bit的无符号数据格式,总共4个字节,因此要在这个基础上加4得到Buffer地址,指向buffer同样是一个4字节的地址。




我们可以看到buffer大小为0×19十进制为25。Buffer保存地址为0x180c4940。在内存中跟踪这个Buffer。我在聊天栏里键入”hello”,因此在buffer中可以看到保存的文本,如果这个值没加密,我们的记录发送数据包的工作就可以结束了。




但是上述数据包中的数据看起来像是被加密的,因为在内存中没有看到”hello”的ASCII。这个是现今在线游戏的常见问题,目前还有很多游戏是没有加密数据包的。




在次键入”hello”之后,出现这种情况:


这个很有趣,显示加密方案可能是一个初始化的vector (IV) 或者 starting variable (SV),这就能解释为什么输入一样而输出确不能始终一样,而且两个buffer的前两个字节是相同的(0×17 0×00)。


尝试键入”hello1”看看有什么:




“1700”变为”1800”,这就说明前两个字节应该是数据包的大小。


现在我们知道数据包是被加密的,现在我们要尝试找到加密前的buffer以及buffer大小,因此我们可以在这里hook到发送至服务器的数据包。通常这个位置就在加密buffer函数之前。为了验证,可以在WSASend上再下一个断点,发送聊天数据包,在栈中找到函数的返回地址。这个返回地址在栈顶,如下图:


OD中显示了这个函数来源自IDA分析的第二个函数,跟踪这个函数直到看到这个函数的调用者。




如果数据包发送成功,则函数跳转至结束,如果继续回溯是在WindowProc处结束。似乎我们能够回溯到buffer是如何加密的点。


上述显示这个游戏是通过Window的消息来通告一个数据包是否准备发送或者已经被发送。这种通过WSAAsyncSelect的消息参数0x9C40发送Windows消息的途径有很多种,在下图可以看到,使用不同的消息参数0x9C41可以调用PostMessageW。由于当数据包不发送时,PostMessageW才会被执行,我就通过消息0x9C41通知这个游戏数据包没有发送。


在WSAAsyncSelect处下断点。




在OD中找到WSAAsyncSelect,看下这个函数:




这个函数看起来有点意思,因为调用WSAAsyncSelect函数和指针,假设是网络数据结构或者类,这里的ESI和WSASend的ESI一样。


根据我的经验和这段代码,这个调用约定是”thiscall”,因为网络指针在函数开始处通过ECX向ESI传值,并且在函数结束处我们可以看到RETN 4。Keygen Assistant告诉我们游戏是被MSVC++编译的,这就意味着这个是”this”指针,同样根据网络指针,传入ECX,因此函数的功能是清洁栈空间。


如果阅读汇编代码,第二个参数是一个指向结构体的指针。


在函数开始处下断点,分析压进栈区的参数。




在上图可以看到指向结构体的指针,下面会解释这个结构体,但是现在我们看看结构体的第二个成员:




可以看到加密前的数据包,buffer大小同样是0×0017。在发送”hello”数据包之前找到了该buffer。

这个结构体的第一个参数可能是buffer的hash或者一个ID用来后续整理的,第二个参数指向buffer起始地址,第三个参数指向buffer结束地址,第四个参数看起来应该是指向分配给buffer的内存结束的地址。如果将第三个成员和第二个成员相减(0x0E680279 - 0x0E680260)结果是0×19,这个就是最终服务器读取的包括头两个字节完整buffer的大小。


为了确认这个buffer是加密前的,将”hello”修改为”hello1”,看下结果:




现在看来,我们可以在数据包发送至服务器前修改buffer值,而在你接收聊天信息前不会显示任何消息。


为了构造一个数据包记录器,可以在让程序跳转至你的代码中打印数据包内容。这样工作搞定!


0×04 可以做的事


现在已经可以成功修改数据包了,可以分析服务端和客户端之间发送的数据包。现在我们也可以发送我们自己定义的数据包,甚至通过逆向工程可以创建自己的游戏客户端。通过向服务器发送不正常的数据包而无需通过软件的开发者,通常就可以引发漏洞。你也可以根据数据包来回溯函数功能,例如聊天栏,人物移动信息,购买条目信息更直接的是直接调用游戏功能而不通过按键的形式。


这里我展示一个简单的方法利用这个在线游戏。大多数在线游戏有一个”swear”的过滤系统,可以防止玩家互喷脏话。


例如,如果我在游戏里说”shit”,是这样的:






游戏的过滤系统比较输入的文本,如果是脏话则用垃圾字符代替,下面看看如果我将buffer修改为”shit”会怎样:






现在客户端绕过了过滤系统,显示了”shit”。这个只是众多可利用的一部分而已。当和其他玩家一起登录的时候有时会意外崩溃,当然这个漏洞几小时之后就被修复了。


0×05 总结


个人认为游戏公司以及其他任何一家公司,应该把更多的精力投入到自己的网络安全,而且应当由服务器来控制过滤器。总是会有人试图利用,攻击和黑掉这些安全性低的产品。游戏公司经常收到不计其实的盗号,欺骗投诉从而导致玩家退出,这必然意味着亏损。


然而不幸的是,大多数游戏公司往往不愿意在安全上开销。他们经常通过反作弊软件来抵抗攻击。但这种做法通常只能阻止比较低级的攻击,这里建议这些公司应更专注于自己的服务器的安全性。


相关新闻

大家都在学

课程详情

IDA教程系列

课程详情

XP挑战赛技能培训

课程详情

Ollydbg使用教程