双击这个窗口:
打开OD,我们先看看记录全屏秒杀的那个空白位置00400E2C:
测试程序,按F2,我们发现OD中这个位置并没有修改,那该怎么办呢?
在00700000那里发现一片可以修改(跳转)的位置:
我们看到这里就可以修改过来了。
记录下这部分的字节集到我们的记录文件中:
其实字节集就是机器码(233的十六进制就是0xE9,189的十六进制就是BD)。
测试完毕后给它恢复,用我们写的辅助来修改:
上图这俩跳转地址肯定错了,正常应该是:
记录下这部分的字节集到我们的记录文件中:
还有一个问题,这里应该写入数值3,数值3才是全屏秒杀僵尸,才会有过关奖励。
这样全屏秒杀就实现了。
汇编调用的通用的框架:
由于进程ID我们是定义的全局变量:
易语言有很多很方便的功能:
把这面这三行代码删除,不需要这样写,ctrl + L 定义一个新变量:
我们修改代码,添加一个编辑框用来输入植物类型:
我们也可以通过一个变量把所有的植物类型都获取出来:
一般我们写辅助的话用Release配置,因为在调用CALL的时候,Debug配置会附加很多调试信息,导致调用CALL的时候游戏会出现问题。
咱们之前找的基址+偏移,是因为数值是变动的;
而上图中花括号中的字节集,这些关键指令是固定的,是模块化的,你在CE中可以找到:
上图字节那一列中的内容,游戏不更新它们也不会改变;咱们找到一些功能用到了这些字节集;
那咱们在VC6中怎么实现呢?
咱们这样写CALL的话肯定会出现问题,咱们得把这个要CALL的地址传到一个寄存器当中:
编译运行后测试,点击远程调用CALL按钮后游戏挂了,就是由于之前所说的Debug配置的调试信息导致的,因为Debug配置会附加很多调试信息,所以要用Release版本编译运行测试。
但是用Release版本我们发现仍然有问题,游戏还是会挂掉。
建立类向导以后,你再双击Button1按钮,就又会进入到类向导:
你选中IDC_BUTTON1控件ID,你得点击一下确定,然后再双击Button1按钮就会进入OnButton1消息响应函数:
通过定时器来实时读取阳光的地址,来得到阳光的数据。
我们看到VC6右边的工具箱里并没有定时器这个控件,它这个控件的功能是通过代码来实现的,我们右击窗体,点击事件:
我们双击上图这个WM_TIMER:
然后再双击上图这个按钮:
点确定,再双击“开启定时器”这个按钮:
开启定时器是用SetTimer函数,第1个参数是随便一个数字(不能是英文,用来标识这个定时器),第二个参数是多少秒响应一次OnTimer里面的代码:
关闭定时器的代码:
当点击读阳光按钮的时候我们就开启定时器。
但是如果我们把游戏退出,重新再打开的话,我们发现这个地址就不对了,重新用CE搜索阳光数值,并改动:
可以看到有效的地址和之前的是不一样的,就要用到基址和偏移来实现,每次动态的把地址读出来。
咱们通过阳光的数值来找到和阳光相关的基址:
我们看到ecx=0x19,十进制是25,也就是一个阳光的数值,然后我们把第一个偏移5560记住;
咱们把eax的值复制一下,然后再CE里面搜搜该十六进制数值:
咱们的可执行程序一般会加载进地址空间中的00400000位置,所以比00400000小的地址咱们不用管,而且我们看到栈地址001292AC的数值还一直在变,所以删除它;
再记录一下这个768偏移,并复制edi的值到CE里搜索:
将堆栈中的数据删除,那些绿色的地址是不会变动的:
这里的基址有4个,它们的数值都一样,所以用哪一个都可以(当然你也可以一个一个测试一下)。
怎么来使用这个基址和那两个偏移呢?点击CE界面上的手动加入地址按钮:
因为这里有两个偏移,所以点击Add Offset再增加一个偏移:
因为咱们找数据是从偏移5560开始、然后是768偏移,最后是基址,所以这里要从下往上按顺序添加地址和偏移:
这就找到阳光数值了,咱们重新开始游戏,我们可以从CE中看到,保留的两个阳光的地址已经不一样了:
我们如果点击游戏中的阳光的话,CE的调试器找到的就是增加阳光的代码,这里咱们要的是减少阳光:
ESI的值0x26A2的十进制是9890,也就是当前阳光的值:
我们之前讲过了,偏移5560这个位置就是阳光数值的地址,点击显示反汇编程序按钮,进入到内存里面:
我们如果把这行mov传送指令用空指令替换的话,阳光就不会减少了,但是这样的话,这种实现方法不科学:
咱们应该把sub这样阳关减少指令用空指令替换就可以了:
还可以把sub改成add的话,就可以实现种植植物的话反而加阳光:
咱们把sub指令恢复,然后右击该行地址,前往地址,把弹出的地址复制一下:
然后解除CE的附加:
到OD中记录一下相关的数据:
注意,这个要转两次:
咱们要再按一次ctrl+G,再转该地址一次:
咱们需要把该行的字节集复制记录一下:
然后咱们给它用空指令替换:
复制这两个nop的字节集到记录文件里面:
然后咱们恢复修改的代码:
把该行地址复制到记录文件里:
解除OD附加,剩下的就是编写代码来实现了。
第一种会有什么样的缺陷呢?我们在操作中来给大家演示一下。
传统的搜索方法是,能种植的时候是1,不能种植的时候是0:
咱们就是这样的反复筛选,得到的几个地址加入进来:
咱们把上图数值为0的地址,把它的值改为1的话看看豌豆射手会不会变亮,我们发现它不亮,说明地址不对;
搜索不出来是因为我们设置的数值类型有问题,我们应该将它改小一些,设置为字节:
这种表示状态的没有必要定义的那么大,所以1字节就够了;这里就是4字节搜索不到那你就换成字节型的再试试:
分析游戏数据,它就是一点一点的试,咱们就是重复这样的操作就可以了。
上图这16条数据虽然还很多,但是这些地址是有规律的,我们一眼就能看到最后面那个地址就是当前它的可种植状态,我们修改它的值再观察看看:
为什么会知道这个地址是CD的呢?我们搜索阳光的时候找到的地址是:
然后接着搜索免CD的地址,豌豆射手亮的时候搜索1,灭的时候搜索0:
重复这样的操作筛选出我们需要的数据:
我们可以看到现在数据就已经比较少了,地址列表里前面15个地址和最后2个地址相差很大,肯定是不正确的,而且最后两个地址和阳光地址比较接近,然后咱们分别修改这两个地址的数值看能不能达到咱们要的效果:
所以上图0BA706F0这个地址没有效果,肯定是不对的,把它删除;
所以通过这种对比法,能够找到第一格CD状态的地址。
这种基址+偏移的找法,因为这个地址也是变动的,咱们还是得通过之前找基址的方法(阳光数值):
上图是豌豆射手变暗的代码,下图是变亮的代码(又增加了一条汇编指令):
注意上面的数值类型要改为4字节,我们发现搜索不到,因为这时候豌豆射手已经变亮了,所以只能先搜索变亮代码的地址:
第一个偏移是70,我们记录下来,并搜索要寻找的地址指针数值:
把比00400000小的堆栈地址(数值都是一直在变动的)都删除掉:
我们看到这个偏移是144,记录下来:
比00400000小的地址咱们不要:
我们看到上图,阳光是基址+768、5560,而第一个免CD状态是的基址+768、144、70;
咱们继续跟,就把基址找出来了:
咱们来测试一下这个基址,第一格CD状态有3个偏移:
类型要改为Byte。
咱们再重新开始游戏,等阳光够100,豌豆射手变量了以后,把第一格CD状态改为0:
它的状态就灭了,咱们给它改成1它就又亮了:
所以咱们用这种基址+偏移的方法也可以,但是它有一个缺陷,这是第一格的,你还得找第2、第3格以及后面的,经过分析第二格和第一格的地址相差了50:
这里只是6个技能栏,假如说有很多的技能栏呢,比如说有60个呢,你不可能一个一个的都给它加一遍吧,这种是不合理的;
通过找到种植不减阳光的关键指令这种反汇编实现的方法来实现这个免CD。
这个是当第一格植物变暗的时候得到的指令,咱们在这条指令上下功夫:
既然这条是关键指令会让植物变灭的话,咱们就不让它变灭:
咱们就是用空指令替换,咱们通过把关键指令NOP掉以后,第一格植物就不会变暗了。
但是这种无CD的方法的缺陷是建立在当前植物可种植的情况下,种植植物不会变暗;还有一个缺陷是,游戏开场以后,有些植物它的CD是冷却的,你得等它冷却时间恢复了、能种植了以后,咱们才能实现免CD,它不亮的话也是种植不了的:
什么是关键跳转呢?
在游戏里面,植物能种植的时候是1,不能种植的时候是0,就是说你在种植的时候不但有一个状态的显示,它还会有一个相关的判断,如果是1的话怎么做,如果是0的话又怎么做。
咱们就找到当a等于0的时候不让它按照正常流程跳转到不可以种植那里,而是要让它依然可以种植植物。
点击豌豆射手,再放回去:
咱们要用到的是cmp指令,那些mov指令暂时先用不到了:
咱们给它改成JMP:
我们看到,虽然显示上看是亮的,但是提示是重新装填中,实际并不能种植。
这个只是显示上的一个状态。
我们发现仍然不能种植,还原第一条代码指令,刚修改的第二条代码指令不变,再看看效果:
我们发现它会一直变暗,不会变亮,我们把第2条汇编指令用nop空指令替换:
好像也不行,我们给它还原回JNE:
所以第一条、第二条指令都不是我们想要的,我们再看第三条:
改为JMP或者nop也不行,所以第三条汇编指令也不是我们想要的。
那就只剩下最后一条应该就是了:
我们可以看到就算植物是不亮的状态也可以种植,再配合上第一条指令显示上的种植就可以了。
咱们还要到OD里面获取一些相关的数据:
记录完正常的字节集后,我们修改jnz为JMP,使得植物都处于亮的状态,并记录修改后的字节集:
再转到真实CD状态:
这是在CE中已经修改过的代码,我们给它记录一下:
把jmp改为jne(jne和jnz的机器码一样),将该原始的字节集记录一下:
注意该字节集长度是6个字节。
思路就是,要找到在建造的同时截取到种植的CALL,然后对比下函数的开头,看看有什么变化;在建造的时候肯定有一个判断。
这时候你不能点游戏里的阳光,要种植一个植物,让阳光减少:
点击阳光让阳光增加后再搜索:
种植一个植物,阳光减少:
然后咱们种植一个植物:
又收集了一个阳光,所以第二个指令不要,咱们要的是第一个指令:
复制该指令的地址,记录一下,然后到OD中转到该地址后下个断点:
回到游戏咱们种植换一个植物:
游戏端下来之后,按OD工具栏上的k键显示调用堆栈:
鼠标双击上图的函数过程,到该函数的开头:
咱们把之前的断点删掉,然后让程序运行,加些阳光,然后在该函数开头下个断点,回到游戏中,在可以种植的位置种植一个植物:
断下来后,咱们按F8单步步过,看一下关键的跳转:
继续F8往下走,凡是不跳的咱们不记录,跳的才记录:
一直按F8往下走,一直走到阳光减少的CALL位置那里就不用记录了:
删除断点运行程序,回到该函数开头下个断点,回到游戏种植一个植物到已有植物那里,咱们按F8往下走分析流程:
上图这是一个关键跳转,之前是有效的,这次无效:
可以种植是这个跳转是跳走的,不可以种植时这个跳转无效。
咱们把这条指令改为JMP,看看游戏中的效果,发现实现了重叠建造。
把这段字节集记录一下:
再记录一下正常的字节集和该指令的地址:
咱们得学习分析数据的能力,你不能光用别人分析出来的方法。
咱们先把之前找到的基址保存的数值记录下来:
阳光能否被搜集肯定是要根据当前有几个阳光相关的,所以咱们需要搜索一下当前阳光的可点击数:
点击几个阳光后,又增加了几个,继续搜:
我们知道保存阳光的数量不可能是一个静态的地址,所以删除绿色的地址,只剩下两个,进游戏里点击阳光再看看这两个地址的数值变化:
现在就只剩下这一个了:
上图EBP的值是8个F,8个F是 -1,所以第二个指令是减少,第一个指令是增加。
复制该地址手动加入地址:
但是上图这个地址不能使用,要给它加0偏移:
这个地址保存的数值就跟咱们基址保存的数值一样了,这个地址就可以用来使用了。
等待一个阳光出现,观察内存区域中的变化,然后点击阳光,内存区域又有变化:
我们看到最大变化位置应该就是到0BDA3638那里。
咱们把这个地址复制一下,粘贴到内存扫描选项中的结束那里,起始地址设置为刚才找到的地址0BDA34C8,然后进到游戏里等出现1个阳光的时候,在这个地址范围内搜索未知的初始数值:
然后再次搜索未变动的数值(因为此时我们没有回到游戏里点击阳光):
回到游戏点击一个阳光以后,再次搜索变动的数值:
上图有几个特别大的数值,我们改变它们的类型为float,这些记录的应该是阳光坐标的位置:
咱们要找的是阳光是否被收集的状态,要么是0,要么是1,咱们回到游戏等待新的一个阳光出现:
这两个特别大的数据肯定也是不对的,删除这两个,只剩下两个数值为0的地址,经过观察,阳光出现它们为1,阳光消失它们为0。
咱们分别改一下它们的数值:
我们发现改成1后阳光消失;
等游戏中又出现一个阳光的时候,把最后那个地址的数值改为1的话,阳光就会自动收集,我们把该地址锁定:
就实现了自动收集阳光这么一个状态;
我们也可以找到自动收集阳光这个地址的基址,然后写程序不断往该地址中写入数值1来实现自动收集;
但是这个方法的缺陷是,如果阳光特别多的话它就不会自动收集了。
我们用反汇编的方式来实现自动收集阳光,就跟之前那个免CD的方式一样,对函数的不同情况进行对比(如果点击阳光是什么样,如果不点击又是什么样),找到关键跳转指令。
因为咱们之前已经找到了它的状态,1就是正在收集,0就是没有收集,所以咱们直接搜索就可以了。
在游戏中等出现一个阳光后,不点击,在阳光没有被收集的时候那个状态肯定是为0,回到游戏中收集了以后,它的状态肯定是为1,我们再搜索数值1:
等又出了一个阳光后(不点击),搜索数值0:
点击阳光收集后,搜索数值1:
还是跟之前一样,一个一个将指令下面的条件跳转指令修改为JMP试试:
回到游戏,发现没有自动收集阳光,没有达到我们要的效果,咱们给它改回去,再给它NOP看看:
发现阳光都变暗了(虚了),不是我们要的数据,给它还原。
以此类推,测试其他几条指令,一直到:
这个改了以后,只要出现阳光就会自动收集,说明这就是咱们要的指令,复制该指令的地址记录一下:
到OD中转到该地址,记录该地址的字节集:
咱们给它改成JMP,回到游戏可以看到自动收集阳光的效果,记录修改过的字节集。
机器码0xEB的十进制就是235。
解除OD附加,剩下就是写程序完成功能了。
我们等待出现僵尸后放置一个豌豆射手,修改游戏速度为0.1,等待豌豆射手射出一颗子弹,搜索未知的初始数值。
这个攻击间隔应该是一个倒数的数值,当减少到某个数值后就会再发射一颗炮弹,所以要搜索减少的数值:
回到游戏让子弹移动几步,再回到CE点击再次扫描,重复这个过程;即在子弹不断移动,离僵尸越来越近的过程中,点击再次扫描,不断地重复这个操作:
反复多次后我们发现留下来的数值还是很多,那么我们再等豌豆射手发射一颗炮弹,间隔肯定就是最大值,咱们再搜索增加的数值:
然后再次重复搜索减少的数值:
最后只剩下一个地址,咱们给它数值改成1发现,豌豆射手又会发射一颗炮弹,将变动的数值再改成1的话又会发射一颗炮弹。
咱们把该指令的地址复制下来,解除CE附加,进OD转到该地址:
这个加 -1就是减1,然后传给eax,比较eax的值,如果为0就执行上图中的发射炮弹的CALL,我们可以多减些数值:
我们发现豌豆射手不攻击了,我们给它改成-4的话就快速发射炮弹了,让它攻击更快些(这是经过反复测试过了的数值):
改成-5的话,豌豆射手只会有一直吐炮弹的动作,却没有炮弹射出来,炮弹发射不出来,也就是说如果减得数值过大,炮弹还没发射出来呢,就又执行了一次攻击的间隔,这个动作还没执行完又再次发射,炮弹还没发射出来就又开始一次新的发射,就会造成炮弹发射不出来了。
这种攻击加速属于一般的效果,还有一种更变态的方法。