植物大战僵尸——任意阳光修改

简介

  这是植物大战僵尸系列的第一个练习,目的是实现任意阳光修改功能。

原理

  该功能的原理其实很简单,大致步骤就是:

1. 开始任意一局游戏;
2. 找到该局游戏的阳光值在内存中的地址;
3. 根据该地址,找到游戏阳光的基地址;
4. 编写一个手动修改该值的小程序。

准备

  • 系统:Windows 7 SP1 x64 ultimate

  • 游戏:植物大战僵尸

  • 工具:CheatEngine v6.7、VisualStudio 2017

分析

确定阳光位置

  启动游戏,打开任意一局游戏。

启动一局游戏

可以看到此时游戏中的阳光值为50。

  CE加载游戏,使用精确搜索,定位阳光在内存中的位置。因为一般情况下第一次的搜索结果会很多,所以多次改变阳光值,并多次搜索,最终确定位置。

确定阳光位置

  试着修改该地址的值,确定该地址是否正确,如果不正确会影响后面的分析。

确定阳光位置—修改前

确定阳光位置—修改后

可以看出来阳光的变化,可以确定这就是阳光在内存中存储的位置了。

确定阳光基地址

  确定阳光在内存中位置就可以实现该局中阳光值任意修改,但是只限于该局中,因为这个位置是动态变化的,每一句有不一样。那我们总不能每一次都用CE搜索一次吧,那样就太麻烦了。我们希望可以使用程序来替我们完成计算阳光位置的功能,但是我们无法确定阳光每次的位置。这时就需要看游戏本身如何确定这个位置了。

  对于游戏本身而言,在每局游戏前,都会根据一个基地址来计算出本局游戏的阳光存储位置,然后游戏对这个地址进行一系列写入与读取的操作,代表了游戏中阳关的修改和显示。而这之中的基地址就是我们需要找到的。基地址是不变的,所以我们可以利用基地址计算出每一局游戏阳光的存储位置。

  利用刚才确定的阳光地址,定位哪些位置对这个地址进行了访问操作。

第一级访问指令

可以看到 esi 和 edx 两个位置偏移 00005560 的位置被访问,esi 和 edx 就是阳光位置的指针,所以我们需要找到哪些地址存储了 esi 和 edx 的值,即0x0F3C4FC8。

确定阳光基地址—搜索第一级指针

搜索后发现有很多结果,这时需要一个一个排除,但是这里根据经验,与其他地址差别最大的可能性最大,而这里有两个差别很大,所以一个一个排除。经过分析和测试,确定是0x026630A0。查看哪些地址访问。

第二级访问指令

可以看到有一个指针,寄存器+00000768,而该寄存器的值都为0x026630A0,可以知道 0x026630A0 就是我们要找的第二级指针。

  搜索哪些位置存储第二级指针,和上次的方法一样排除。确定位置 0x006A9EC0。查看哪些地址访问。

确定阳光基地址

发现竟然是固定地址,那么可以确定这就是阳光的基地址了。

  关于如何排除问题,我的方法就是查看哪些地址访问该地址,然后去收阳光或者种植物,看访问列表有没有什么动静,然后看看每个访问指令中。我们搜索的地址的值是否是我们之前确定的值。有些不明白,我现在也说不清楚,自已多意会吧。

  在确定基地址后,可以测试下正不正确。添加一个基地址指针,

基地址指针

然后新开一局游戏,修改基地址指针的值,看是否成功。

测试基地址

编写阳光修改器

  确定了阳光的基地址后,就可以开始编写阳光修改器了。程序大致分为几步:

1. 获取游戏进程ID(我电脑上的进程名为Plants.vs.Zombies.exe)
2. 获取游戏进程内存权限
3. 计算阳光位置
4. 将目标值写入阳光位置

  参照这三步,即可完成阳光修改器的编写。下面是一些核心代码:

获取游戏进程ID和游戏进程权限。

    //拍摄系统进程快照,获取游戏进程
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE)
    {
        MessageBox(0, L"系统快照拍摄失败!", L"错误", MB_OK);
        return false;
    }
    bRet = Process32First(hProcessSnap, &pe32);
    while (bRet)
    {
        if (!wcscmp(pe32.szExeFile, L"Plants.vs.Zombies.exe"))
        {
            targetid = pe32.th32ProcessID;
            break;
        }
        bRet = Process32Next(hProcessSnap, &pe32);
    }
    CloseHandle(hProcessSnap);
    if (targetid == 0)
    {
        MessageBox(0, L"没有找到游戏进程", L"失败", MB_OK);
        return false;
    }

    //打开游戏进程
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetid);
    if (hProcess == INVALID_HANDLE_VALUE)
    {
        MessageBox(0, L"打开游戏进程失败", L"错误", MB_OK);
        return false;
    }

计算当前阳光位置,修改阳光值。

    //计算阳光地址
    //读取第一层地址
    if (!ReadProcessMemory(hProcess, (LPCVOID)dwBaseAddr, (LPVOID)&dwTempAddr, sizeof(DWORD), 0))
    {
        MessageBox(L"读取第一层地址失败", L"失败", MB_OK);
        return;
    }
    //读取阳光地址
    if (!ReadProcessMemory(hProcess, (LPCVOID)(dwTempAddr + 0x768), (LPVOID)&dwTempAddr, sizeof(DWORD), 0))
    {
        MessageBox(L"读取阳光地址失败", L"失败", MB_OK);
        return;
    }
    //读取阳光
    dwYangguangAddr = dwTempAddr + 0x5560;
    if (!ReadProcessMemory(hProcess, (LPCVOID)dwYangguangAddr, (LPVOID)&dwYangValue, sizeof(DWORD), 0))
    {
        MessageBox(L"读取阳光失败", L"失败", MB_OK);
        return;
    }

    //修改阳光值
    if (!WriteProcessMemory(hProcess, (LPVOID)dwYangguangAddr, (LPCVOID)&m_nYangguang, sizeof(int), 0))
    {
        MessageBox(L"阳光修改失败", L"失败", MB_OK);
        return;
    }
    MessageBox(L"阳光修改成功", L"成功", MB_OK);

启动修改器,输入期望阳光值。

测试-期望阳光值

测试-阳光修改成功提示

返回游戏查看。

测试-修改效果

  到此,我们已经实现了我们的目的,任意修改阳光并编写了我们自己的阳光修改器。

参考

猜你喜欢

转载自www.cnblogs.com/Roachs/p/9365240.html