本节讲解,如何让游戏本该要写进它内存里的数据,同时也写一份到我们程序的变量里.并且还让它通知一下我们的子程序去读取新的数值.
下面的内容主要涉及修改测试程序中要运行的反汇编代码.需要大家对汇编指令有点了解.
在本节里有个测试程序,运行,用CE打开它,扫描上面编辑框里的值,找到有效的一个内存地址.右键在该地址菜单中选择 查找写入该地址的代码 .

查找写入该地址的代码 .
把 eax 里的值写到 [ebx] 指向的内存地址中去.
今天我们要解决这里的动态内存问题,要分析,修改涉及到了上面图中红框部份的代码!
004034D2 - 8b 5d fc - mov ebx,[ebp-04]
可以在这条代码上点右键,进行断点设置,然后没多久,程序就会被断下来在这里,使用F7进行单步步入这个调试方法,一步步的调试到 004034F4 处,在这个过程中要注意,右边的各个CPU寄存器的变化,记录下来里面的值,用于分析理解这段代码的作用与功能.
004034D2 - 8b 5d fc - mov ebx,[ebp-04] : EBX=0014C9B4 类型成员内存地址 004034D5 - 89 03 - mov [ebx],eax : 把数值写入成员地址指向的内存里
004034D7 - 8b 1d 50 0d 9a 00 - mov ebx,[009a0d50] : 0014C9B0 EBX=0014CAD0 类型地址 004034DD - 83 c3 04 - add ebx,04 : EBX=0014C9B4 把类型地址+4得到成员的地址 004034E0 - 89 5d fc - mov [ebp-04],ebx : 把成员地址保存到 [ebp-04] 004034E3 - 68 01 03 00 80 - push 80000301 004034E8 - 6a 00 - push 00 004034EA - 8b 5d fc - mov ebx,[ebp-04] : EBX=0014C9B4 004034ED - ff 33 - push [ebx] : 把类型成员所在的内存地址入栈 004034EF - 68 01 00 00 00 - push 00000001 004034F4 - bb 68 01 00 00 - mov ebx,00000168
004034F9 - e8 ec 00 00 00 - call 004035ea
如果你对汇编代码指令有了些了解的话,那么就会发现,上面的代码里,有很多都是重复的操作,特别是对于那个类型成员的内存地址,保存来保存去的,一点意思都没.于是我把这段代码进行初步修改,精简一下.
注意,在修改汇编代码时,要先在 004034D2 或前一条上下断点,这样可以在修改过程中,还没全部改好,程序执行进来时会出错!

精简代码,把不需要的汇编指令改成NOP空指令.
我在004034D2上设置了断点,所以这条代码变成了红色,然后我把下面的一些,不重要的代码给去掉了,只留下了重点的几句.完工后,把004034D2的断点取消,按F9运行程序,没有出现任何异常.显示这次的精简代码工作是比较圆满的!
004034D2 - 8b 5d fc - mov ebx,[ebp-04]
004034D5 - 89 03 - mov [ebx],eax 004034D7 - 90 - nop 004034D8 - 90 - nop 004034D9 - 90 - nop 004034DA - 90 - nop 004034DB - 90 - nop 004034DC - 90 - nop 004034DD - 90 - nop 004034DE - 90 - nop 004034DF - 90 - nop 004034E0 - 90 - nop 004034E1 - 90 - nop 004034E2 - 90 - nop 004034E3 - 68 01 03 00 80 - push 80000301 004034E8 - 6a 00 - push 00 004034EA - 90 - nop 004034EB - 90 - nop 004034EC - 90 - nop 004034ED - ff 33 - push [ebx] 004034EF - 68 01 00 00 00 - push 00000001 004034F4 - bb 68 01 00 00 - mov ebx,00000168
004034F9 - e8 ec 00 00 00 - call 004035ea
注意,在修改代码时,不是可以随便乱改的,要注意一些汇编代码指令会占用多少字节内存.我之所以把它原始的那些汇编进行精简,去掉那几个无聊的内存地址存来存去的目的,就是为了可以结省出来些可用的内存空间,可以为下步进行代码修改用..上面的反汇编窗口中 那些 90 - nop 的就是节省出来的空指令.
上面精简过代码后,现在得进行固定动态内存工作了.首先有个问题,我们要把这个动态内存固定到哪里去呢?就是让演示程序,要把这个数值写到哪里?
使用反汇编窗口上面的 菜单->工具->申请内存 可以申请一段内存,然后它会告诉你,新申请到的内存地址是多少,我申请的地址是 00BE0000 随后我把这个地址加入到了CE主界面下面的地址列表框里去了.
然后就是进行汇编代码的修改,让演示程序把数据写到这个 00BE0000 内存中去.

修改一条指令,让程序把EAX值写到指定的内存里去.然后取消004034D2的断点,点菜单->调试->运行.

很快就看到了效果,修改汇编指令成功.
这里的动态内存固定成功了,同时演示程序里自身所使用的内存里,也是能得到这个数据值的,因为,把EAX即有保存到了原程序的那个地址,也有保存到了我们提供的 00BE0000 地址了.
现在我们已经分析完毕,下面是如何编写代码,详细的代码可以查看本节例子包里的 mydll.dll 里.
这里根据上面的修改后汇编的方法
004034D5 - 89 03 - mov [ebx],eax 004034D7 - 89 05 00 00 be 00 - mov [00be0000],eax : 00003563 004034DD - 90 - nop 004034DE - 90 - nop 004034DF - 90 - nop 004034E0 - 90 - nop 004034E1 - 90 - nop 004034E2 - 90 - nop 004034E3 - 68 01 03 00 80 - push 80000301 004034E8 - 6a 00 - push 00 004034EA - 90 - nop 004034EB - 90 - nop 004034EC - 90 - nop 004034ED - ff 33 - push [ebx] 004034EF - 68 01 00 00 00 - push 00000001 004034F4 - bb 68 01 00 00 - mov ebx,00000168
把相应的机器指令抄下来
89 03 89 05 00 00 be 00 90 90 90 90 90 90 68 01 03 00 80 6a 00 90 90 90 ff 33 68 01 00 00 00 bb 68 01 00 00
在第五,六,七,八这4字节就是要使用的内存地址.就是 00BE0000 .当然,我们得把这个地址改成,我们的某个整数型变量所处的内存地址的话,以后,测试程序就会自动把这个值写进我们的变量里了,最后我们只需要用一个时钟,来读这个变量值就行了.
上在的机器码指令可以写成这样的格式
89 03 89 05 + 到字节集(取整数型指针(整数型变量)) + 90 90 90 90 90 90 68 01 03 00 80 6a 00 90 90 90 ff 33 68 01 00 00 00 bb 68 01 00 00
把计算出来的字节集数据,写到内存 004034D5 就可以达成我们的目的了.

把DLL注入测试程序,通过修改它的一些汇编指令,达到自动获取数据到我变量中的目的.
目前为止我们一直是用一个时钟去读取数据的,有没有别的方法让游戏在写内存的时候并且同时通知一下我们呢?如果能那么的话,就可以不用时钟外还有更多的好处,如,得到了最佳读取数据的时机,不再因为时钟而浪费过多的系统资源.
现在我们要在上面分析并且修改好的固定内存代码上,再做改进,让它能有回调我们的子程序的功能实现自动通知,只需要再增加一条CALL的代码即可.

修改指令,让测程序程序把新值的内存地址送到我们的带一个参数的子程序里,该参数设置为参考.
经过查看,EDX没有被使用,所以这里把我们的子程序指针存入EDX,然后再 CALL EDX 就会跳到我们的子程序里.并且参数一,正是这个新的内存值
004034D5 - 89 03 - mov [ebx],eax 004034D7 - 53 - push ebx 004034D8 - ba 00 00 00 00 - mov edx,00000000 004034DD - ff d2 - call edx 004034DF - 90 - nop 004034E0 - 90 - nop 004034E1 - 90 - nop 004034E2 - 90 - nop 004034E3 - 68 01 03 00 80 - push 80000301 004034E8 - 6a 00 - push 00 004034EA - 90 - nop 004034EB - 90 - nop 004034EC - 90 - nop 004034ED - ff 33 - push [ebx] 004034EF - 68 01 00 00 00 - push 00000001 004034F4 - bb 68 01 00 00 - mov ebx,00000168
push ebx 把新值的内存地址入栈 mov edx,00000000 把我们的有一个整数型参考参数,无返回值的易语言子程序指针存入EDX call edx 让演示程序自动调用咱们的子程序,然后我们的子程序里的参数一里的值就是这个数据了.
通过这个方式后,我们可以省去了时钟的麻烦.这个的效率远远的高于时钟~,,
机器码是 89 03 53 ba 00 00 00 00 ff d2 90 90 90 90 68 01 03 00 80 6a 00 90 90 90 ff 33 68 01 00 00 00 bb 68 01 00 00
我们只需要把这段机器码转换为十进制的易语言字节集格式,然后把第五,六,七,八这4字节换成你的有一个整数型参数无返回值的子程序指针.
然后就可以把这段机器码字节集写入演示程序的 004034D5 内存地址处了.
详细的代码见本节例子包里的 mydll.dll 这里

内存事件回调代码
需要注意,当DLL要被卸载时,是需要恢复原始的那些汇编指令代码的.
下面的本节例子的运行效果

先运行测试程序,再运行安装钩程序,把mydll.dll注入到测试程序中,会呼出呼出窗口.
点击下载本节源代码
本节要求大家对反汇编指令能懂一点.还不懂汇编的需要先学习一下本站的汇编那部份教材.
|