关于指针表
指针表是前人总结,今人不断完善,记录PvZ游戏中各类指针的表,可以在PvZClass项目中找到,该项目中的指针表也是流传最广的指针表
该表以树形结构组织,下面我将带领读者阅读指针表,并从中获取信息
┌6A9EC0\\基址
├──C\\[std::string]公司名称
├──28\\[std::string]完整公司名称
├──44\\[std::string]产品名称
├──60\\[std::string]窗口标题
├──7C\\[std::string]注册表路径
├──98\\[std::string]游戏目录
├──C0\\窗口宽
├──C4\\窗口高
├──D0\\[双浮点]音量
├──D8\\[双浮点]音效
├─┬320\\鼠标和窗口
│ ├──28\\不断自增的值
│ ├──5C\\[指针]基址
│ ├──84\\[逻辑值]游戏窗口获得焦点则为true
│ ├──88\\[窗口]顶层窗口
│ ├──8C\\[窗口]鼠标按住的窗口
│ ├──90\\[窗口]鼠标所在的窗口
上面是指针表的一段节选,如果我们要获得"[逻辑值]游戏窗口获得焦点则为true",则需要从最顶上开始看起,首先我们找到基址6A9EC0,然后寻找第一层偏移即"320\鼠标和窗口",然后再通过一层偏移"84\[逻辑值]游戏窗口获得焦点则为true"就可以找到这个值,我们可以将这个过程总结为[[6A9EC0+320]+84]
阅读指针表其实就是如同文件夹索引一般,基址就是根文件夹,而下面有文件也会有子文件夹。
那么,我们来寻找一下阳光的指针
┌6A9EC0\\基址
├─┬768\\[窗口]关卡界面窗口
│ ├──5560\\阳光值
我们将每一层偏移单独找出,就可以得出阳光数的指针为[[[6A9EC0]+768]+5560],这相当重要,我们可以记录下来,以备后面使用
修改阳光数
修改植物大战僵尸,我们一般使用CE,我们进入CE,点击左上角的按钮附加植物大战僵尸的进程
附加后,点击手动添加地址
勾选指针
然后我们将阳光数的偏移输入进去:[[[6A9EC0]+768]+5560
点击确定
将数值修改为一千,进入游戏查看,发现修改成功
让我们再做一个简单的修改:收集阳光减阳光数
右键指针,选择找出是什么改写了这个地址
选择第二个
收集一个阳光,发现弹出窗口中多了一行
选择,点击显示反汇编程序
双击窗口中显示的汇编指令,将add修改为sub
回到游戏验收效果,收集阳光,发现阳光不减反增
让我们更深入地理解:基址、偏移和指针
让我们先看基址是如何产生的,关于这点,让我们先了解系统是如何给一个程序分配内存的。
在程序开始执行后,系统会给程序(32位)分配一块大小为4GB的虚拟内存,这一块内存在物理上也许并不连续,但对这个程序来说,这块内存是【完全连续】的
然后,程序根据自己PE信息中的Image Base和Image Size数据,在这块内存指定的开始位置Image Base开始加载各类参数,这些参数的总大小是Image Size,Image Base便是基址
注意,只有Image Base ~ Image Base + Image Size之间的参数是程序初期可用的,其余的部分仍由操作系统进行分配,程序不可随意更改,否则会导致错误
那么,当程序执行New操作,显然在Image Base ~ Image Base + Image Size之间无空间可用,程序需要向操作系统再申请一块空间,这一块空间会在这4GB的虚拟内存空间的其余位置产生,这个地址是完全随机的,但是,系统在分配完成后,会返回一个地址,这个地址可以由指针指向,程序就会在这块地址中写入类的各个成员,这便是创建对象的过程
那么,现在让我们回顾这一切:
程序根据基址来分配参量,各个参量同基址之间的偏移是固定的(因为这是编译器和运行时决定的),我们可以通过基址和偏移来完整获取它们。其中,参量间会有指针,它会指向在另外空间中的对象,得到对象后,再通过一个固定的偏移,就可以得到对象的各个成员
这便是基址、偏移和指针之间的关系