什么是线程循环发包 ?
介绍
有线程循环发包,就有非线循环程发包(也叫主线程发包)。在游戏中线程循环发包会导致调试器断下后查看调用栈拿不到有用数据(不同的操作触发的发包断点,调用栈都一样),而主线程发包则可以在调用栈中查看是谁调用了发包功能。相比线程发包,逆向主线程发包会更加容易。
如何分辨是不是主线程发包
首先肯定是跳转到3大发包函数,send , sendto ,WSASend 分别下段。 如果还没等你操作断点就马上就断下而且断的非常的频繁,那么基本就是线程发包了。除此之外,是主线程发包的可能性比较大了。
主线程发包逻辑:
1.玩家操作
2.调用功能call
3.整合明文数据包
4.数据包加密
5.系统调用发包函数
这种情况,只需要在发包函数下断点然后一直让调试器执行到返回(也可以直接看栈回溯),最后就能回溯到功能call
线程循环发包逻辑:
**1.玩家操作
2.调用功能call
3.整合明文数据包
4.数据包加密
5.封包入队列
6.发包线程从队列中取出封包并发送
**
这种线程循环发包与真正的功能CALL是独立的,所以如果要分析这种方式发包的游戏就得从发包的数据跟随找数据来源。
因为我们要从数据的来源分析,所以就需要跟着 send 函数的buf 参数开始着手分析:
int send (SOCKET s, const char *buf, int len, int flags);
给 buf 下内存写入断点,这个时候会基本都会断在一个 mov xxx,yyy
的指令上,观察 [yyy] 是否为你找找的包头特征,如果是,就多次执行到返回,最后就可以到达程序领空。
为什么给buf设内存断点就能跳出死循环?
因为给buf设内存断点的时候,你断到了组建buf的过程(也就是步骤 3/4),当你跳出这个过程的时候,那你就找到了组建的开始,也就是功能call执行完后,开始组包 。
为什么bp send会一直都是一样的结果 ?
bp send的时候,断到的是组好包后的结果,发包线程和功能call线程不在同线程,调试的时候只有一个线程是激活状态,其他线程都出去挂起状态,单步跟踪无法跨越线程。
但是这种方法有一个前提,就是 buf 的地址必须是固定的,如果游戏的代码写成这样:
// 如果是在循环外面,就可以使用上面内存写入断点的方法
// char buf[256] = {0};
while(1){
char buf[256] = {0};
// 调用 send 发包函数
}
那么每次调用 send 的时候,buf 都是新申请的内存导致地址变更。但好在在这个大循环里创建变量本身就是很消耗性能的,大部分的游戏不会这么写,但如果真碰到了只能找其他的思路了,比如从封包队列入手。
调皮的空调: 64位的游戏 g_lpNewImage = VirtualAlloc((LPVOID)hDll, g_dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);申请不回来 失败代码487请大神指点下
qiyuanqwq: 其实这种方法还不如直接用CreateProcessA创建进程,申请可执行内存,写入内存什么的反而更容易被KILL,内存杀直接教你做人
m0_65099846: 在Win10下蓝屏了
无药可医�️: 请教下我在stopDvr这步老是报错是什么问题啊