游戏辅助原理与制作02-植物大战僵尸03-冷却时间基址

一、分析思路

    当我们种下一棵植物的时候,通常需要等待一段时间才能再种下一棵。在游戏开发中实现这一过程的,往往是使用一个定时器的变量。对于定时器的变量,一般有两种,一种是递减的,即从某个数开始逐渐减到0;另一种是递增的,即从0开始一直加到某个数。

二、分析过程

对于一开始,游戏的植物没有种植,所以计时器肯定是不变的,所以这时我们用CE搜未知初始化的值“Unknow initial value”,发现搜出很多值。

然后,种下一棵向日葵,计时器开始计时,这时计时器开始变化了。但我们不知道它是递增型的还是递减型的,我们只知道它变化了,所以我们搜变化了的值“Changed value”(注意:游戏暂停时,Changed value只能搜索一次),还是有很多值。

此时,由于游戏暂停,向日葵的冷却时间也暂停了,因此可以搜索未变化的值“Unchanged value”,可以多次点击进行搜索。

回到游戏,让计时器走一下,然后继续搜【一次】变化的值,仍有很多结果,这时我们已经暂停游戏了,所以计时器未变化,我们搜未变化的值。

我们看到,结果少了一半,但还是有很多。我们选择直接让计时器走完,然后搜变化的值,还是有很多结果,这时冷却已经完成了,定时器是不会变了,所以我们搜未变化的值,不过在此之前我们先玩一下游戏,排除一下干扰,我们看到,结果也少了很多,我们继续这种思路,玩一下搜未变化的值。我们尝试几次后,发现它也不怎么管用了,能排除的值也越来越少,这时,我们把向日葵种下去,这样定时器就开始走了,接着我们搜改变的值,我们看到,结果又少了很多,我们继续这种搜索,一直搜索,直到结果只有几个为止。

最后,我们搜到六个结果。我们继续原来的办法进行排除时,发现只能排除两个,剩下四个怎么都排除不掉。但是我们不断种植向日葵的时候发现,最后一个地址的数值是比较小的,其它的几个也太大了(当然,不排除这个计时器变量是一个浮点或者双精度)。而且这个地址的值是逐渐增加的。

总之,在这几个地址里,我们最喜欢的就是这个了,不过我们还是得先验证一下。我们重新种下一棵向日葵,发现它的值在不断增加,这时我们直接把它的值改成三千,发现向日葵的冷却一下子就好了,这也就证明了,它是我们要找的计时器的变量。

向日葵冷却地址(动态)0x0E0B0F0C,上限750

要去掉这个时间,改的不是当前的冷却时间,而是要改与之比较(cmp)的上限值。

cmp 当前冷却时间计数,冷却时间上限

在该地址上右键,选择 Find out what accesses to this address(谁访问了这个地址),弹出的窗口里面是空白的

 此时玩一下游戏,让向日葵冷却时间走动,在看这个窗口

找到一个inc指令,选中这行,点击右边的“Show disassembler”按钮,显示“Memory Viewer”窗口

在下方不远处有一行指令 cmp eax,[edi+28],可能为当前冷却时间与冷却时间上限比较。

比较之后进行跳转 “jle 植物大战僵尸.exe+B3007”

将 “jle 植物大战僵尸.exe+B3007” 指令 nop 掉进行测试

右键选择“Replace with code that does nothing”(使用空指令替换)

此时,再游戏中种植植物,发现所有的冷却时间都已去除。

另外一种方法:去掉某一种植物的冷却时间,需要对冷却时间上限进行进一步分析。

找冷却时间基址 ★★★★★★

004B2FE5 - 38 5F 49 - cmp [edi+49],bl

004B2FE8 - 74 1D - je 植物大战僵尸.exe+B3007

004B2FEA - FF 47 24 - inc [edi+24] <<

004B2FED - 8B 47 24 - mov eax,[edi+24]

004B2FF0 - 3B 47 28 - cmp eax,[edi+28]

EAX=01089338

EBX=00000000

ECX=183A1018

EDX=00000119

ESI=00000000

EDI=0E0B0EE8

ESP=0012FA30

EBP=0012FAAC

EIP=004B2FED

根据上面找到的向日葵冷却动态地址:0x0E0B0F0C

[edi+24] = 0x0E0B0F0C

EDI=0E0B0EE8

在CE中搜这个EDI值,如下图,发现搜索结果为0

此时,需要借助OD来找,关掉CE,打开OD,附加游戏进程

在OD中,按 Ctrl + G,输入地址 004B2FEA (该地址为指令 inc [edi+24] 的地址值)

下图中004B2FEA - 004B2FF3这段汇编代码,表示植物的冷却时间每次增加一个数值,当这个数值达到冷却时间上限[edi+28]时,植物的冷却完成。

004B2FED . 8B47 24 mov eax, dword ptr [edi+0x24] 植物当前冷却时间

004B2FF0 . 3B47 28 cmp eax, dword ptr [edi+0x28] 植物冷却时间上限

在OD中汇编窗口,向上或向下翻动,找到地址 edi + 24 和 edi + 28处,并下一个断点

此时,运行游戏在断点处断下

此时,EDI的值为 0E0B0EE8,运行OD,以便让游戏正常运行

打开CE,搜索0E0B0EE8,看是否能找到,此时发现也未找到

那么,在OD汇编窗口中继续往上找,找到EDI的来源

根据 mov edi,eax 发现EDI来源于EAX

即向日葵冷却动态地址:0x0E0B0F0C = [edi+24] = [eax+24]

继续跟踪EAX,在mov edi,eax处下个断点并断下,

通过堆栈窗口可以找到调用函数的上一层,继续找EAX的来源

也可以选中 004B2FB0 $ 51 push ecx 行

在信息窗口上“本地调用来自于 00428E54”上右键,选择“转到 CALL 来自 00428E54” 找到调用函数的上一层

 

通过 lea eax, dword ptr [ebx+eax+0x28]

EAX来源于 ebx+eax+0x28

那么, ebx+eax+0x28 + 0x24 为CD的计数时间, ebx+eax+0x28 + 0x28 为CD上限时间

即向日葵冷却动态地址:

0x0E0B0F0C = [edi+24] = [eax+24]

= [ebx+eax+0x28+24]

这时,我们需要找EBX和EAX两个值的来源:

在汇编窗口中,找到EAX来源

00428E40 |. 8B87 5C010000 mov eax, dword ptr [edi+0x15C]

向日葵冷却动态地址:

0x0E0B0F0C = [edi+24] = [eax+24]

= [ebx+eax+0x28+24]

= [ebx+[edi+0x15C]+0x28+24]

在OD汇编窗口中继续往上找,找EBX的来源:xor ebx, ebx

在函数的开始出有一条EBX清零操作

在0x00428E50 到 0x00428E66这个循环体中,发现EBX每次增加0x50

因此, EBX来源于 n * 0x50 (n >= 0)

此循环共执行 dword ptr [eax+0x24] 次

结合 call 004B2FB0 (访问当前植物冷却时间),可以猜想此循环是遍历植物的冷却时间

即植物对象数组的冷却时间

n = 0向日葵 1樱桃炸弹 2坚果 3寒冰射手……

当前植物卡槽总数为 00428E63 |. 3B70 24 |cmp esi, dword ptr [eax+0x24]

中的 [eax+0x24]

向日葵冷却动态地址:

0x0E0B0F0C = [edi+24] = [eax+24]

= [ebx+eax+0x28+24]

= [ebx+[edi+0x15C]+0x28+24]

= [[edi+0x15C] + n* 0x50 + 0x28 + 24] (n >= 0)

在OD上0x00428E50处下个断点,找到EDI值为0x183A1018,并运行游戏

向日葵冷却动态地址:

0x0E0B0F0C = [edi+24] = [eax+24]

= [ebx+eax+0x28+24]

= [ebx+[edi+0x15C]+0x28+24]

= [[0x183A1018+0x15C] + n* 0x50 + 0x28 + 24] (n >= 0)

验证,在OD的命令窗口输入 dd [0x183A1018+0x15C] + n* 0x50 + 0x28 + 24 并回车

(注意:下图测试 EDI= 17FA6838)

dd [0x17FA6838+0x15C] + n* 0x50 + 0x28 + 24 当前植物冷却时间

dd [0x17FA6838+0x15C] + n* 0x50 + 0x28 + 28 当前植物冷却时间上限

(n=0...当前植物卡槽总数 [eax+0x24])

在CE中搜这个EDI的值0x183A1018,找到大量的地址

图中看到0012开头的地址为堆栈中的地址,因此可以排除

其他,各地址段的地址随机取一个(当发现一个地址找不到基址时,换另外一个地址试)

下面以0x01089BA0为例来分析:

现在CE中搜索0x01089BA0这个地址 ,发现并未找到

在OD中用 dd 01089BA0 命令找到01089BA0 在内存中的位置,并下一个内存访问断点

这是,会找到一个偏移 eax+0x868, EAX = 0x01089338

向日葵冷却动态地址:

0x0E0B0F0C = [edi+24] = [eax+24]

= [ebx+eax+0x28+24]

= [ebx+[edi+0x15C]+0x28+24]

= [[[eax+0x868]+0x15C] + n* 0x50 + 0x28 + 24] (n >= 0)

在CE中搜索EAX=0x01089338,找到4个绿色的地址,都为基址

向日葵冷却动态地址:

0x0E0B0F0C = [edi+24] = [eax+24]

= [ebx+eax+0x28+24]

= [ebx+[edi+0x15C]+0x28+24]

= [[[[0x007794F8]+0x868]+0x15C] + n* 0x50 + 0x28 + 24] (n >= 0)

dd [[[0x007794F8]+0x868]+0x15C] + n* 0x50 + 0x28 + 0x24 植物当前CD时间

dd [[[0x007794F8]+0x868]+0x15C] + n* 0x50 + 0x28 + 0x28 植物CD时间上限

dd [[[0x007794F8]+0x868]+0x15C] + n* 0x50 + 0x28 + 0x48

dd [[[0x007794F8]+0x868]+0x15C] + n* 0x50 + 0x28 + 0x49

简化

dd [[[0x007794F8]+0x868]+0x15C] + n* 0x50 + 0x4C

dd [[[0x007794F8]+0x868]+0x15C] + n* 0x50 + 0x50

在OD中输入 dd [[[0x007794F8]+0x868]+0x15C] + 4C,

查看地址值 0x0E0B0F0C 即为向日葵的当前冷却时间,

查看地址值 0x0E0B0F10 即为向日葵的冷却时间上线,0x2EE = 十进制750

[[[0x007794F8]+0x868]+0x15C] + n* 0x50 + 0x28 + 0x24 为当前冷却时间

[[[0x007794F8]+0x868]+0x15C] + n* 0x50 + 0x28 + 0x28 为冷却时间上限

CE中验证:

注意:

冷却时间需要结合这2个标志位进行,请自行分析:

 edi+24 当前CD时间

edi+28 CD时间上限

edi+48 byte 0

edi+49 byte 1 CD计数开始

48 word 00 01 表示 开始CD计时,卡片不可选

48 word 01 00 表示未开始CD计时,卡片可选中

 

三、代码实现

// 禁用CD

void CFZ_PlantsVsZombiesDlg::OnBnClickedCheckCd()

{

UpdateData(true); // 更新窗口状态至变量



UCHAR buf[2];

if(m_b_cd == true)

{

// 去掉冷却时间

//004B2FE8 - 74 1D - je 植物大战僵尸.exe+B3007

//90 90

buf[0] = 0x90;

buf[1] = 0x90;

}

else

{

// 启用冷却时间

//004B2FE8 - 74 1D - je 植物大战僵尸.exe+B3007

buf[0] = 0x74;

buf[1] = 0x1D;

}

//把buf内数据写入地址 0x004B2FE8

HANDLE hp = GetGameProcessHandle();

DWORD bywrite;

if(hp == NULL)

{

MessageBox("打开进程出错", "提示", MB_OK);

return ;

}

::WriteProcessMemory(hp, (LPVOID)0x004B2FE8, buf, sizeof(buf), &bywrite);



::CloseHandle(hp); // 释放句柄

}



// 获取当前关卡植物卡片总数

DWORD GetPlantsCardCount()

{

HANDLE hp = GetGameProcessHandle();

DWORD buf = 0, byread, bywrite;

// 007794F8 +868 +15C + 24

::ReadProcessMemory(hp, (PVOID)0x007794F8, &buf, sizeof(buf), &byread);

::ReadProcessMemory(hp, (PVOID)(buf + 0x868), &buf, sizeof(buf), &byread);

::ReadProcessMemory(hp, (PVOID)(buf + 0x15C), &buf, sizeof(buf), &byread);

::ReadProcessMemory(hp, (PVOID)(buf + 0x24), &buf, sizeof(buf), &byread);

::CloseHandle(hp); // 释放句柄

return buf;

}



// CD时卡片可选

void CFZ_PlantsVsZombiesDlg::OnBnClickedCheckCdcard()

{

UpdateData(true); // 更新窗口状态至变量

UCHAR data[2];

if(m_b_cdcard == true)

{

data[0] = 0x01;

data[1] = 0x00;

}

else

{

data[0] = 0x00;

data[1] = 0x01;

}

HANDLE hp = GetGameProcessHandle();



// 当前关卡植物卡片总数

DWORD iPlantCardCount = GetPlantsCardCount();

DWORD buf = 0, byread, bywrite;



// dd [[[0x007794F8]+0x868]+0x15C] + n* 0x50 + 0x28 + 0x48

::ReadProcessMemory(hp, (PVOID)0x007794F8, &buf, sizeof(buf), &byread);

::ReadProcessMemory(hp, (PVOID)(buf + 0x868), &buf, sizeof(buf), &byread);

::ReadProcessMemory(hp, (PVOID)(buf + 0x15C), &buf, sizeof(buf), &byread);



UpdateData(true);



for(int i = 0; i < iPlantCardCount; i++)

{

::WriteProcessMemory(hp, (PVOID)(buf + i * 0x50 + 0x28 + 0x48 ), data, sizeof(data), &byread);

}

::CloseHandle(hp); // 释放句柄

}

四、延伸分析

根据上面的分析,可以得到植物卡片在内存的数组数据

 第一个卡片的地址:

起始地址: [[[0x007794F8]+0x868]+0x15C] + 0* 0x50 + 0x28 (图中为0DCBB3D8)

当前CD: [[[0x007794F8]+0x868]+0x15C] + 0* 0x50 + 0x28 + 0x24 (图中为0DCBB3FC)

CD上限: [[[0x007794F8]+0x868]+0x15C] + 0* 0x50 + 0x28 + 0x28 (图中为0DCBB400)

48、49: [[[0x007794F8]+0x868]+0x15C] + 0* 0x50 + 0x28 + 0x48 (图中为0DCBB420)

第二个卡片的地址:

起始地址: [[[0x007794F8]+0x868]+0x15C] + 1* 0x50 (图中为0DCBB428)

当前CD: [[[0x007794F8]+0x868]+0x15C] + 1* 0x50 + 0x28 + 0x24 (图中为0DCBB44C)

CD上限: [[[0x007794F8]+0x868]+0x15C] + 1* 0x50 + 0x28 + 0x28 (图中为0DCBB450)

48、49: [[[0x007794F8]+0x868]+0x15C] + 1* 0x50 + 0x28 + 0x48 (图中为0DCBB470)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值