网络游戏客户端与服务端协同形式:
一般分为两种情况:
发送结果型:
首先在界面里按了吃药的操作,按下吃药的操作以后,生命值增加,客户端会看有没有药水,有没有cd 能不能吃,生命值就增加了,然后把这个血量发送给服务器,但发送给服务器前的这个是可以改的比如直接改成999999,这不就无敌了吗,所以用这种操作往往会有一系列加密的技术防止去修改,但再怎么修改本质上就是能改,这不是技术高低决定的,是这种结构就决定了能改,大部分都不会采用这种结构
另外一种比较完善的:
UI操作,首先检测有没有药水,在不在cd,能不能吃,然后告诉服务器要吃药了,服务器收到这个要吃药的请求也要看服务器上的数据:有没有药水,在不在cd,能不能吃,然后服务器就把吃药操作完了,接下来服务器把血量写到数据库里,再给客户端发送个消息 吃药完成了,这个过程是不是就严谨了很多,从整个流程看,可攻击的地方很小,在客户端和服务端都检测了一次,客户端的检测排除掉了以后,服务端的压力就会变小,这个主要是为了保护服务器,有的游戏就会把第二步取消掉了,都由服务器完成,这样服务器的压力就会大,服务器就有可能变卡
实际上,不是所有游戏都采用第二种,其实大部分的游戏都是第一种和第二种相结合(安全本质就是经济的对决),用第二种方式行,但成本比较高,这是一个吃药的操作,但有的操作非常频繁,比如走路,这种操作在服务器上验证是比较困难的,在服务器验证困难就会消耗大量的时间,客户端是一个面对服务器,服务器是一个面对N个玩家,所以同一时间要处理大量的消息,虽然现在硬件水平变好了,但依然还是很有压力的,有的时候服务器不得不放弃一些验证的操作,改用第一种结果式的发给服务器,从而就造成了漏洞,有的时候用了第二种,但是连续性的,比如需要三个第二种操作才能完成整个操作,如果其中某一个被我们人为忽略掉了,修改掉了,也会造成漏洞,只要都是在网络通信这块
网路通信是一个网络游戏的核心命脉,可以说大多数的游戏漏洞都是利用网络通信设计的不合理来实现的,比如绝地求生游戏中早期出现的一击必杀,瞬移等漏洞
逆向分析游戏宏发送数据和接收数据的过程,寻找游戏数据包明文发送与接受的关键函数,并且尝试利用该函数发送和接收数据
对于网络游戏中数据包漏洞利用的特点,给出检测和修复建议
网络游戏通信操作模型
UI操作 -》组织数据-》加密数据-》发送数据
接收数据-》解密数据-》分发数据-》使用数据
比较小型的会用单线程通信
一般大多是多线程通信
常见函数:
send recv 一般用于tcp通信,默认为阻塞型(可以改成非阻塞)
sendto recvfrom 一般用于udp
WSASend WSARecv 一般配合完成端口使用
数据包有关的关键信息:
char* buffer 数据包
unsigned len 数据包长度
我们要找的是明文数据发送的那个函数,要实现这个过程,至少要提供明文的数据包和数据长度
这个数据包跳过去一定有可辨识的,比如说话123,那里面就是123,里面就有,这就是明文,使用技能就有技能的ID或者编号,使用物品就有物品的代码,光有数据包不行,本身来说就是一段数据,就是一个指针,那数据包到底多长必须得给出来,长度不可能太长,总不可能发个这么长的片把?长度一般就是几百几十的样子,也可能数据包和包长混在一起的,数据包前面就是包长,后面就是内容,这样就只能看到一个东西,不管是哪一种,这个都是很敏感的东西。发送数据是这样的,那么接收数据也是一样的道理,也是这两个关键的信息,不然不好解析。
翻一下栈 ,这就是ip和 端口
返回值等于-1就执行WSAGetLastError
这个地方大概就是它建立连接的地方,没什么太多可以注意的
但有一个地方还是要注意,如果连接成功,能拿到它的套接字,这个套接字肯定会保存起来
这个connect往上跳
传入的两个参数是IP地址和端口
这个地方应该就是连接,连接完了要发送账号密码的操作
这个ecx也先保存一下
然后send和WSASend都断下
发现send被调用
上面这四个传递参数就比较奇怪 他可能是编译器的问题esi代替了 ecx,第二种可能是编译器的优化策略,但这种优化策略很少见
这个地方直接暴露了这发送失败了 ,相当于给别人提供武器了
这个函数没啥,也就是个发送的封装,也没什么值得一提的东西,还得看看上级
就比较明显了,这是数据的发送过程
这参数一看就是数据包和数据包长度
这就要测试是不是,看看数据包内容,内容看不懂,因为不知道是啥样的数据包
那我们就自己发送一个可以分辨的数据包
直接是明文了,直接改一个
所以这个地方恰恰就是我们想要的
这个 地方又是个虚函数,和connect很像
ecx = 3CDAF4A8
发送数据的时候要建立网络连接,指针一定是和跟网络有关系的值
这是个虚函数 就是thiscall 用 ecx传递参数
改一下参数,eax ebx改成0看看崩不崩:
结果也只是断线了,但没崩溃什么的问题,所以这个函数用法就是得到ecx就可以,而ecx可以通过网络连接获取,所以只要在这个地方做个小小的HOOK就能知道这个函数是什么样的情况了,有哪些地方调用了它,发送的是什么内容看看这个函数:
这就是我们发送数据的过程
往下翻就看到了刚刚那个发送数据函数 esi这个东西应该就是加密好的数据放进来了
上面那个函数应该就是加密数据的函数
数据包本身数据是没有变化的 就只加了个EE 加EE主要是为了防止粘包,服务器好处理,但里面没有个长度有关联的数据 万一也出现了EE 就寄了,如果数据包里没有长度光靠结尾加个EE是解决不了问题的,而且,居然没有加密的过程?可能是因为这是比较早期的客户端,没有加这个,当然,对于我们逆向来讲,加不加密不重要。目前来看 这个是只加了个EE
我们使用发送数据肯定是使用那个过程的发,在这也可以用那个send的发,但其他游戏加密肯定很复杂,那肯定是要调用本身的加密过程来完成加密的
问:
这段函数干了什么 ,EE咋加上去的,这个循环用C++还原出来
这里又出现EE 而数据包里也有EE..不就解析么
这个esp+edi+37C edi现在是0 表明这个可以变化,这是个典型的数组
一般数组是 esp+edi*4+x
esp+37C 就是 char* buffer[0x800]
因为这前面传了个0x800内容,所以大小应该就是0x800,所以前面压栈压了个b78这么大
0x800-0x37C+4刚好是0x800 esp+37C是局部变量的buf的指针
edi后面肯定会处理让edi+
这地方操作就相当于 cl = buf[0]
if(buf[0]==0xEE)
edi++ 这肯定也是个循环 读取数据包,肯定就读取到EE结束
2个EE结尾了就不跳了
很明显是用的结构体传参
这个函数就是 ebx相当于指针,esi,edi相当于里面两个内容
这个应该是thiscall 但是用 eax来代替了ecx
传了esi edi 肯定就是数据包的内容了
这个时候改一下:接受到的数据也变了
这地方就是接收数据
发现类都是一样的
首先在网络连接建立的时候会创建一个类,它的指针在我们网络连接的时候就能截取到了,截取到了以后,我们发送数据,接收数据都能够用。发送数据刚刚看了是标准的虚函数,但这个接收数据,因为代码优化的情况下,不是虚函数,用起来就比较麻烦。
首先
这个函数里就是用了eax来代替了ecx,这个函数要调用起来,不写汇编代码还比较麻烦。。但用起来肯定没啥太大问题,所以我们关键就是这个点。接下来要做的就是大量数据包分析了,接下来要把所有数据包打印出来,然后看是什么情况,这次核心目的就是尽量少去逆向,尽量用封包看看能不能把游戏构建起来,尽量尝试性的往脱机走走