6.9 植物大战僵尸:分析植物攻击速度

《植物大战僵尸》是一款非常经典的塔防类游戏,由PopCap Games公司开发并先后在多个平台上推出。主要玩法为种植各种攻击性植物,抵御僵尸攻击,该游戏可以说绝大多数九零后都接触或者玩过,本章将通过逆向分析技术对该游戏进行分析,并实现一些游戏之外的功能,以此让用户理解二进制安全技术的应用范围。

在植物大战僵尸中,植物是有攻击速度的,比如每隔一段时间会吐出一些子弹,由此可判断吐出子弹应该是由一个计数器控制的,也就是说只要我们能够找到控制植物攻击的时钟并改写它,也就可以实现植物加速吐出子弹的效果,吐出子弹的速度分析遍历技巧如下。

  • 附加游戏 > 手动种下豌豆射手 > 搜索未知初始化数据(未攻击)
  • 出现僵尸 > 开始攻击的时候 > 使用变速精灵或变速齿轮将攻击速度放慢 > 搜索减少的数值
  • 回到游戏 > 马上回到CE > 搜索减少的数值 > 依次重复进行5-15次左右
  • 等待豌豆射手再次吐出子弹 > 直接搜索增加的数值 > 最后剩下的地址中数值在(0-1000)以内的就是

运行游戏并使用CE直接附加进程,为了方便调试请自行将阳光改为99999,等待僵尸出现后,马上在本行种植一个豌豆射手在豌豆射手没有攻击前,迅速暂停游戏,回到CE搜索未知初始化数据,当吐出子弹之后依次搜索减少的数值,最后读者应该可以排查出如下图所示的几个内存地址;

首先点开第一个内存地址0x13462678并查找是什么写入了这个内存地址,如下图所示;

  • 00464461 - 89 86 B0000000 - mov [esi+000000B0],eax <<

接着继续看第二个内存地址0x138CB000并查找是什么写入了这个内存地址,如下图所示;

  • 00413B7C - 83 86 38550000 FF - add dword ptr [esi+00005538],-01 <<

最后138CB064第三个内存地址,并查找是什么写入了这个内存地址,如下图所示;

  • 00413E45 - 83 87 9C550000 FF - add dword ptr [edi+0000559C],-01 <<

我们以内存地址0x464461为例,定位到此处并依次向上回溯,最终可跳转到0x045F8A9,我们直接在此处下断点,回到游戏豌豆射手攻击时会被直接断下,这里经过不断的测试,笔者已经分析了这段代码片段中的实现细节;

我们可以通过修改mov ecx,dword ptr [esi + 5C]来实现加速植物攻击加速,这里可以将该指令直接改成mov ecx,22即可实现植物攻击加速,但此处如果读者将数值改为小于22则植物将不会攻击,22时可修改的最小范围。

读者可以继续向下寻找代码片段,修改下图中的jnz 0045F935将其改为NOP同样可实现全局植物加速功能,紧随判断其后的call 0x0045EF10则是植物攻击的关键CALL调用。

而实现替换特征码的实现方法相信读者能很容易的实现,如则是完整的替换代码;

#include <iostream>
#include <windows.h>
#include <string>
#include <tlhelp32.h>

// 根据进程名得到进程PID
DWORD GetPidByName(const char* name)
{
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
DWORD pid = 0;

if (Process32First(snapshot, &pe32))
{
do
{
if (_stricmp(pe32.szExeFile, name) == 0)
{
pid = pe32.th32ProcessID;
break;
}
} while (Process32Next(snapshot, &pe32));
}
CloseHandle(snapshot);
return pid;
}

// 写内存字节集
BOOL WriteByteSet(DWORD Pid, DWORD Base, unsigned char *ShellCode, DWORD Size)
{
BYTE *Buff = new BYTE[Size];
memset(Buff, *ShellCode, Size);

HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, 0, Pid);

return WriteProcessMemory(handle, (LPVOID)Base, Buff, Size, NULL);
}

int main(int argc, char *argv[])
{
// 得到进程PID
DWORD pid = GetPidByName("PlantsVsZombies.exe");
printf("[*] 进程PID = %d \n", pid);

// 写出替换
unsigned char shell[] = { 0x90, 0x90 };

BOOL ref = WriteByteSet(pid, 0x45F926, shell, 2);
if (ref == TRUE)
{
printf("[*] 替换数据成功 \n");

for (size_t i = 0; i < sizeof(shell); i++)
{
printf("%x \n", shell[i]);
}
}
else
{
printf("[*] 替换数据失败 \n");
}

system("pause");
return 0;
}

至此植物大战僵尸系列游戏的分析就结束了,读者可将我们所学到的技术应用在一起,例如无限阳光,加速攻击,无冷却,叠加种植等,任何一个简单的技术聚集在一起都将形成一个庞大的体系,如下图所示则是我们所实现的游戏效果;