首页
社区
课程
招聘
[原创]封包式游戏功能的原理与实现
发表于: 2020-4-19 20:40 20375

[原创]封包式游戏功能的原理与实现

2020-4-19 20:40
20375

游戏外挂按制造难度总共分为下面三类:

整个过程看似简单,实则困难重重,下面就通过一个例子来复现整个过程。

这里用来进行分析的游戏是幻想神域

自己搭建的一个私服,无论游戏有没有更新都可以跟着步骤操作,随时复现。按照文件中的视频教程搭建即可。

在网络游戏中,客户端和服务器的通信基于一系列的数据包。每个数据包都类似于一条指令,客户端和服务器在这个系列指令中完成指定动作。

客户端要与服务器进行通信,必须调用下面的三个发包函数发送数据包

那么我们只要在这三个函数下断点,然后进行堆栈回溯分析,就能准确定位关键的函数调用链。在这条链上,快速排查出需要的功能call。

不过,发包函数在下断点的时候,可能会碰到下面两个棘手的问题:

幻想神域就是第二种情况,属于线程发包。

对于第一个问题,是因为游戏设计者知道发包函数的重要性,重写了一份发包函数。对于这种情况有两种解决方案

send sendto和WSASend在底层都会调用一个函数叫WSPSend,F7进入send函数,第三个调用的call就WSPSend函数。

接下来解决第二个问题,游戏单独起了一个线程进行发包

由于线程发包是在游戏内部用一个死循环不断的发送数据包,其中包括心跳包,所以会出现发包函数断的很频繁的问题,这就导致无法在我们想要的时机断下,不利于调试。我们需要先解决频繁断下的问题。

幻想神域这个游戏的发包函数的WSASend(),首先来了解一下这个函数参数的含义

唯一有用的参数: lpBuffers: 指向WSABUF结构体的指针,存储的是包长和包内容

接着打开游戏,用OD附加,在WSASend函数下断,程序断下

查看一下第二个参数lpBuffers,数据包长度为1E,我们可以以数据包长度为限制条件在这个地方下条件断点,条件如下:

如果有多个心跳包可以用与的方式进行过滤

通过条件断点的方式,就可以解决发包函数频繁断下的问题。

游戏想要单独开一个线程进行发包,必然要用一个地址作为参数传递给发包线程。

第一个线程将发包内容写入地址,第二个线程从这个地址中读取发包内容。这个地址会有两种形式,一种是不变的,从正向代码的角度看就是用全局变量传递,伪代码如下:

另外一种是动态变化的,从正向代码的角度看就是用堆空间传递,伪代码如下:

跳出线程发包的核心思路就是要找到是谁将发包内容写入。也就是找到上面的memcpy的位置。

在WSASend函数下断,查看一下pBuffers的地址。这个地方的地址是一直变化的,应该是用的堆空间的方式来传递参数。

如果这个地址是不变的,说明是用的全局变量来传递参数。不变的情况下直接在这个地址下写入断点就能跳出发包线程了。

现在这个地址是每次都动态变化的,所以我们需要往上追到这个地址的来源,然后对地址的来源下写入断点,跳出发包线程。

首先需要找到包的来源,在WSASend函数下断。

eax是pBuffers的结构体地址,而eax来源于[esp+0x28]

经过这一个push,堆栈地址发生改变,包长=esp+24 包地址=esp+28,而esp+24来自eax,那么eax就等于包长

再经过上面几个push,包地址=esp+18,继续追esp+18

而esp+18来自ecx,包地址=ecx,继续追ecx

ecx的值来自[edx+esi],edx的值断下后为0,那么包地址就等于esi,继续追esi

esi来自[ebx+4],而ebx来自[edi+2888],那么包地址就等于[[edi+2888]+4]

在这个地方下个断点,可以发现edi的地址是不变的。这个时候就没有必要往上追了。

接着我们在[[edi+2888]+4]的地址下硬件写入断点,找到往这个地址写入数据的地方

断点断下以后,eax=[edi+2888],是被写入数据的地址,包内容=[eax+4]

我们需要判断这个地方是在发包线程内还是线程外。

判断的方法是对比WSASend和找到地址的调用堆栈。

我们发现两个调用堆栈的地址是相同的,说明还没有跳出发包线程。需要继续追eax的来源然后下写入断点。

eax来自[ebx+8],ebx来自edx,而edx的地址是不变的,包内容=[[edx]+8]+4],直接在edx下写入断点

断到了第二次断下的位置

这个时候再查看调用堆栈,返回地址都是游戏主模块,明显这次我们跳出了线程发包函数

接着我们需要在这个函数内找到加密的封包内容,之前的包内容偏移如下:

对比之前追的偏移表达式,这个地方就是将ebp写入到[eax],[eax]其实就相当于包内容表达式的[edx],所以

加密的封包内容就等于[ebp+8]+4]

那么怎么验证这个地方就是加密的封包内容呢?直接对比WSASend的pBuffers和[ebp+8]+4]的数据内容

这两个地方是一致的,那么说明[ebp+8]+4]就是加密的封包内容。接下来测试一下能不能通过跳出的发包线程找到游戏的喊话call。在第二次断下的位置下断点

然后在游戏内喊话,断下以后在堆栈中的返回地址,我们找到了当前的喊话内容,说明这个call就是喊话call

定位到了加密的封包位置以后,我们再来找明文发包call。

在游戏内进行喊话,内容为三个1

在加密的封包内容处下断点,喊话让游戏断下,并且在堆栈中找到第一个返回地址


分析这个call的相关参数,esi是一个结构体指针

+0的位置指向的是一个虚函数表

+4的位置里面有我们的喊话内容3个1,这个可能就是我们要的明文发包函数了

为了进一步确认,把这个地方的内容修改为222,F9运行

喊话的内容也修改成了222,说明这个就是我们要的明文发包call。

我们在加密封包处下断点,第一层返回地址找到了明文发包函数,那么封包的加密call肯定就在中间。

在明文发包函数下个断点,F7进入函数并单步跟踪,上面所有的跳转都执行了,上面4个call没有执行的机会

然后在单步不过这个call的时候,喊话的内容被加密了。这个有可能就是加密call。

为了进一步确认,将断点断到加密封包内容处,查看[[ebp+8]+4]地址处的值,和之前的内容一致,说明这个call就是我们要的封包加密call

首先来看eax,eax地址指向的值每次都是变化的,对于加密函数来说,为了让密文每次都变得不一样,一个有效的方法就是让秘钥变的随机。这个eax加密call的秘钥

eax往上追可以追出一个偏移表达式,这里省略追秘钥的过程,直接给出表达式

edi是一个数值,可能是包长

我们在WSASend函数下断,查看一下,和上面的edi是一样的。那么edi就是包长-2。

封包分为两部分:前两个字节是包的头部,头部往后才是封包数据。

这个参数的含义其实就是要加密的内容长度,-2是因为要减掉封包头部的长度。

ebp和ebx可以用同样的方法论证得出是包地址+2。也就是要加密的数据地址,+2是为了不加密封包头。

到此,封包加密call的参数就分析完成了

到这里,只剩下最后一步,将封包加密函数整个复制到自己的dll代码中并修改,就能彻底脱离游戏代码了。修改后的代码如下:

接着我们在代码中调用加密函数,然后发送封包来实现喊话功能。

这里是直接用的组装好的喊话分包,至于分包要如何分析,如何组装,这个我们后面再讨论。示例代码如下:

这里我省略了send函数的套接字来源。直接在WSASend下断,往上追第一个参数,就能看到游戏中的SOCKET是通过GetWindowLongW获取的。

到此,我们已经一步步完整复现了从内存挂到封包挂的进化过程。实现效果如图:

最后,附上Github地址,里面有游戏下载链接和相关工具,需要请自取:
https://github.com/TonyChen56/GameReverseNote

 
 
 
 
 
 
 
 
 
 
 
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2020-4-19 20:47 被鬼手56编辑 ,原因:
收藏
免费 13
支持
分享
最新回复 (28)
雪    币: 441
活跃值: (1050)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
2
2020-4-19 22:15
0
雪    币: 6541
活跃值: (4336)
能力值: ( LV10,RANK:163 )
在线值:
发帖
回帖
粉丝
3
把明文到send发出去这段代码弄出来就是脱机。
2020-4-19 22:44
0
雪    币: 83
活跃值: (1087)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
4
你这加密函数是简单的 如果是那种复杂的 需要大量修复重定位  甚至加了vm 混淆的话 你就歇菜了 想做完全的脱机 还要研究登陆验证 心跳 一大坨东西
2020-4-20 03:29
0
雪    币: 6541
活跃值: (4336)
能力值: ( LV10,RANK:163 )
在线值:
发帖
回帖
粉丝
5
killpy 你这加密函数是简单的 如果是那种复杂的 需要大量修复重定位 甚至加了vm 混淆的话 你就歇菜了 想做完全的脱机 还要研究登陆验证 心跳 一大坨东西
明文发包到send, recv到明文解包 消息分发。
这几个找到就算脱机有希望。
2020-4-20 09:31
0
雪    币: 4738
活跃值: (4286)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
脱机那只适用于没有加壳从程序,加壳程序最多半脱机。帐号验证部分都是服务器端下发随机验证码,验证码在经过壳保护代码处运算又发回给服务器。
dxx勇士的站街不知道是不是半脱机。。。
2020-4-20 13:43
0
雪    币: 410
活跃值: (1168)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
主要还是三个 登陆 选角色 游戏中的发包,建议尝试一波选角色登陆的发包流程。
2020-4-20 14:27
0
雪    币: 26245
活跃值: (63302)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
8
感谢分享!
2020-4-21 09:50
0
雪    币: 8186
活跃值: (6404)
能力值: ( LV12,RANK:207 )
在线值:
发帖
回帖
粉丝
9
感谢分享
2020-4-21 10:07
0
雪    币: 174
活跃值: (181)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
谢谢分享
2020-4-21 16:00
0
雪    币: 234
活跃值: (837)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
11
感谢分享
2020-4-21 20:08
0
雪    币: 1258
活跃值: (1434)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
老铁,走远了
2020-4-21 20:15
0
雪    币: 235
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
感谢分享
2020-4-22 10:14
0
雪    币: 515
活跃值: (3272)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wem
14
谢谢分享
2020-4-22 22:33
0
雪    币: 179
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
谢谢分享,老铁厉害呀
2020-4-29 00:05
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
大神。 
2020-4-29 09:40
0
雪    币: 8447
活跃值: (5041)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
17
很详细,学习了
2020-5-1 08:31
0
雪    币: 545
活跃值: (247)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
18
mark
2020-5-1 23:39
0
雪    币: 52
活跃值: (93)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
很完整,收藏学习了!
2020-5-12 10:15
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
大佬会做棋牌透视了吗?
2020-5-12 19:52
0
雪    币: 1
活跃值: (27)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
这是把任鸟飞的视频翻译成文字了吗好兄弟
2020-5-19 15:31
1
雪    币: 45
活跃值: (1364)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
好老的游戏了,里面有一个被vmp的包,一直没弄出来。导致挂10分钟就需要重新登录一下。现在代码都找不到在哪了。
2020-5-19 17:36
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
25
mark
2020-7-16 15:30
0
游客
登录 | 注册 方可回帖
返回
//