因为集结号棋牌大厅更新的比较频繁,每次更新都需要重新找内存基址,功能call,而本人的脑子不好使,做过的东西一般过几天就会忘,重新做一遍还是要费不少脑筋。经过几番折腾,最后决定把相关的过程记录下来,以便以后游戏再更新时可以少一些工作量。所以,这篇文章不是教程,只是本人的工作笔记,其他人(老手)可以借鉴,但是不适合新手用来学习。
第一步,先用CE搜索到座位的内存地址。一个座位上有人时,其内存值是指向该玩家内存数据的地址;座位上没人时,其内存值是0。根据这个规律可以搜索到某个座位的内存地址,然后查看什么指令写入了该内存,本次得到的结果是:
0044237A - 89 44 8A 54 - mov [edx ecx*4 54],eax
第二步,关掉CE,打开OD,附加游戏进程,定位到0044237A:
集结号抢座分析-第一次下断
可以看到,基址是来自EDX,EDX又来自该call外部的传参。一般EBP 8是第一个参数,EBP 0xC是第二个参数,EBP 0x10是第三个参数。根据0044237A - 89 44 8A 54 - mov [edx ecx*4 54],eax这条指令判断,EDX是基址,ecx是座位号,eax是玩家内存地址。在此处下断,然后随便坐个座位,断下后返回到这个call外部看看:
集结号抢座分析-第2次下断
可以看到,这个call前面的3个push就是他的3个参数,根据之前的分析,第一个push是玩家内存地址,第二个push是座位号(一个桌子有N个座位),因为之前看到座位号是取的WORD,所以只看低位的2个字节,高位的2个字节可以忽略。第三个push是基址。在这个call的地方下断,断下后观察堆栈数据,基本符合判断。
第三步:查找基址来源。在第二部中的结果中看到,edx来自local1(其实真实代码是[ebp-0x4]),然后向上找找local1是来自什么地方。可以看到上面不远处有个:mov [local1],eax。而eax上面有个call,可以确定这个eax应该就上上面这个call的返回值(一般的call都是把返回值存在eax中)。所以要想知道edx是怎么来的,就要看看上面这个call(call dword ptr ds:[eax 0x18])里面做了什么。在这个call上下断,然后F7跟进;看到下面代码:
集结号抢座分析-EDX来源
经过仔细分析,这个call的返回值eax的来源是最下面的:add eax,dword ptr ds:[ecx 0x172C],是个加法。第2个加数[ecx 0x172C]中的ecx是外部第一个参数,比较简单。第1个加数eax来自上面的:imul eax,eax,0x1EC,也就是:eax=eax*0x1EC=[EBP 0xC]*0x1EC,所以edx的实际来源是:[参数2]*0x1EC [参数1 0x172C],这里根据经验可以猜测,参数2应该是桌号,参数1是上一级基址。然后返回到该call外部,并下断查看:
集结号抢座分析-EDX来源call
第4步,进一步查找基址,第2级基址
通过第三步的结果,看到基址ecx来自于arg1,也就是本call的外部参数1。然后下断,CTRL F9返回,看到如下代码:
集结号抢座分析-查找基址2
可以看到,基址是eax,来自上面的local.165,然后做了个 0xF18运算。然后在add eax,0xF18处下断,断下后F7单步一次,看到eax的值:0x132E64E0,然后打开CE尝试搜索这个值。为什么要到这个时候用CE搜索?因为这个过程我以前做过,这是经验。第一次分析时不是这样的。CE搜索结果:
CE搜索基址的结果
搜索结果有绿色的,说明这是可靠基址。
第5步,整理基址
第二级基址是[0x4d07d8] 0xF18,这就是第三步中的代码:add eax,dword ptr ds:[ecx 0x172C]中的ecx的值。第三步中的代码其实就是计算桌子的地址,算法是:桌号*0x1EC [ecx 0x172C],即:桌号*0x1EC [[0x4d07d8] 0xF18 0x172C],该桌子上的每个座位的内存地址是:桌子基址 座位号*4 0x54,即:[[0x4d07d8] 0xF18 0x172C] 桌号*0x1EC 座位号*4 0x54,这样有了桌号和座位号,就能得到这个座位的内存地址,进而可以通过内存判断这个座位上有没有人,然后决定要不要抢这个座位。
至此,抢座工作基本做好了1/3,剩下的工作就是如何实现抢座,然后再把相关逻辑用程序写出来。我更倾向于找到坐下call,然后调用这个call去实现抢座。其实也可以用鼠标模拟点击的方法,简单,找不到call时这是备用的解决方案。但是因为崇拜call的稳定、准确、快速,即使找call的过程会比较繁琐,我也是优先考虑用调用call来实现。具体找call的过程以后有时间了再写。