从上文我们可以知道,69F2C0 地址是植物卡片信息数组的起始地址。
这里我尝试修改了土豆的属性,将其变为1。但土豆没有立刻被僵尸吃掉,时间还是和之前的差不多。
那么信息应该就和没改过的6720496有关,观察这个数字和它前面的67205XX数字,感觉这个应该是指针,数组里面的类的大小应该是18字节。 重新启动几次游戏后发现这个也是静态地址【实际上这个地址是plantName,植物的名字】
class PlantInfo{
int sunCost;
int CD; // every 100 means 1 second
int ???;
int ShootInterval; // 攻击间隔 every 100 means 1 second 设置为50好像是最小值
void* extraInfoPointer; //指向额外的信息,0x12字节 = 18字节
int ???;
int ???;
int ???;
int ???;
}
粗看了一下这个额外的信息,感觉看不出什么,先从攻速入手。
给这个地址下个访问断点,发现0045DCE0
地址的代码访问了ShootInterval [eax+1C]字段。这个时候eax的值是69F2B0,而不是之前观察到的69F2C0 。一定是哪里出了问题。
0045DCE0 | mov edx,dword ptr ds:[eax+1C] | 获取植物的攻击间隔
重新观察内存。发现从69F2B0开始以9个int为差值,呈现 0 ,1 ,2 ,3 ,4的规律。这应该就是植物的Id。所以植物信息的基地址其实是69F2B0而不是69F2C0 。
所以植物的类应该是下面这个样子【我说为什么调了半天有些字段没效果。。。】
class PlantCard{
int plantId;
int ???; //好像是没用的
int skinId/animationId; // 动画的效果 皮肤
int ???;
int sunCost;
int CD; // every 100 means 1 second
bool CanSpwanBullet; //能否发射子弹 如果可以,会去访问子弹的资源,所以如果本身是不可以的,你将其改成可以,那么会访问未分配的地址,会生成一个segment fault
int ShootInterval; // every 100 means 1 second 设置为50好像是最小值
char* pPlantName; //指向额外的信息->植物的名字,0x12字节 = 18字节
}
然后再调试了一下,发现最后一个字段是指针,指向植物的名字。。。。
通过攻击间隔寻找 PlantSceneObject信息
给攻击间隔的地址下访问断点,可以发现在0045DCE0指令处会访问。这里ebx感觉是场上的植物对象PlantSceneObject 的基地址。
这里取几个ebx的值
ebx ; PlantSceneObject
0F47457C
0F4746C8
0F474814
观察差值,可以发现PlantSceneObject 大小是0x14C.而且应该是malloc动态分配的。
在 CE里面 设置数据对齐大小为0x14C,获得场上植物的信息。可以看到这里有8行数据,对应场上8个豌豆射手。
那么怎么才能知道哪几个字节是植物的血量? 放一个新的植物到场上,然后用CE搜索这个植物的0x14C结构体内的数据,先搜未变更的值。过滤掉一部分字段。等僵尸造成伤害后,再搜索变更的值,筛选出3个字段,然后观察。发现偏移为0x40-0x41为植物的血量,类型为ushort。
此时场上植物的类型PlantSceneObj为
class PlantSceneObj{
unsigned short HP; // 植物当前血量 0x40-0x41
bool canSpawnBullet; // 由45DCF2地址的代码得出 0x48
}
得到这个信息之后,我们回去找对应ebx+40赋值的代码,给0x40偏移的字段打上写断点。发现下面的代码赋予了初始值
土豆会在这里再次赋值
被僵尸咬的时候,下面0052FCF0地址的代码会扣掉4点血。因此如果想要植物永远不死,只要设置一个奇数的生命值就行。