植物大战僵尸——无限种植

简述

  这次实现的植物在同一位置的无限种植。分析这个功能纯属意外,原本是想分析植物无CD的,但是却意外发现了这个破解点,就临时改为分析种植限制了。

原理

  其实这是破解,单纯的只实现了该功能,却不是分析清楚了整个种植流程,毕竟我的水平实在有限,还是太菜。

  破解原理是发现了一个函数,猜测作用应该是根据判断结果来决定是否种下植物,因为正常与非法种植的区别在传入的参数中就已经区分,而且十分明显,所以修改的地址很简单。

  不得不说,植物大战僵尸真是一个好游戏,不仅玩法好玩,新手练手也是十分适合的。

准备

  • 系统:Windows 7 x64 SP1 ultimate

  • 游戏:植物大战僵尸

  • 工具:CheatEngine v6.7、吾爱破解OllyDbg、Visual Studio 2017

分析

定位关键代码

明确寻找的代码功能:可以决定是否种植植物。

仔细观察游戏流程,可以发现,我们在种下植物的同时,阳光也会减少,所以种植植物必然会改变阳光值,那么可以知道,阳光值修改的代码一定在种植植物的流程中。所以这里我们就用阳光的位置作为突破点。

启动一局游戏,使用CE定位到阳光的位置。

定位阳光存储地址

查找哪些地方修改了阳光值。可以发现在种下植物时,出现了一行代码mov [edi+00005560],esi。

CE定位修改阳光的代码

那么该代码就是用于减少阳光值的代码,地址为:0x0041BA76。使用OD附加游戏进程,并跳转到该地址处。

OD定位修改阳光的代码

整理下植物种植流程:选择植物->选择种植位置->判断该位置是否合法->减少阳光->种下植物。

这个流程是大致流程,我自己也自己暂时也不清楚。但有一点很清楚,判断位置一定在减少阳光前面,因为它得认定这里合法才会减去阳光,否则就不合理了。

可以看出这个函数没有这样的判断,回到上层函数。

如果放在之前分析小程序时,我会先找函数头,然后下断从头逐步分析。不过这种游戏也不小,我大概看了下,发现这个函数很长,存在许多判断,从头逐步分析估计什么也分析不出来。所以我就以减少阳光函数为起点倒着分析。

倒着分析时要记得减少阳光的函数在种下植物的逻辑中,所以跳转代码一定要跳转到可以执行该函数的流程上才是正确的。

最后定位到最上面的第一个跳转。

第一个跳转

第一个跳转距离函数头很近,我们在函数头下断,分析下执行到减少阳光函数的过程中所有判断需要满足的条件。

经过一遍的分析,我们就可以知道这个过程中只有一个地方需要修改条件去满足判断,那么这个位置就是我们要找的关键代码了。

关键代码

分析关键代码

进行简单的测试,来判断该处是否可以实现我们所需的功能。

在test eax,eax下断,修改寄存器eax,满足jz跳转的条件,即eax=0。

F9运行游戏,查看结果。

简单测试结果

可以看到成功在一块地面种下两株向日葵。

那么我们如何在代码中实现这个功能呢?直接修改寄存器,有些麻烦。eax是上函数的返回值,那么不如分析函数,看如何让其返回0。

首先分析分析下传入参数。该函数有三个参数,都不知道是什么作用。但是我们可以通过记录正常种植与非法种植情况下的参数进行比较。

正常情况与非法情况的参数

只有第二个参数不同,那么我们只需要使得第二个参数为0x1,即可实现我们的目标。

那现在的问题是如何使得第二个参数为0x1。我们可以看到第三个参数始终为0x1,而第三参数保存在edi中。

第三个参数

这样可以利用edi中的值来作为第二个参数,以实现目的。所以现在只需将push ebx改为push edi即可,也就是将0x0040FE26处的53改为57。

编写实现代码

核心实现代码,其思想主要是修改目标地址的机器码。

boolean ModifyCode(int flag)
{
    int i;
    DWORD dwCodeAddr = 0x0040FE26;
    BYTE desCode[] = { 0x57 };
    BYTE srcCode[] = { 0x53 };

    switch (flag)
    {
        case 0:
        {
            //关闭
            if (!WriteProcessMemory(hProcess, (LPVOID)dwCodeAddr, (LPCVOID)(srcCode + i), sizeof(BYTE), 0))
            {
                MessageBox(NULL, L"指令修改失败", L"失败", MB_OK);
                return false;
            }
            break;
        }
        case 1:
        {
            //开启
            if (!WriteProcessMemory(hProcess, (LPVOID)(dwCodeAddr + i), (LPCVOID)desCode, sizeof(BYTE), 0))
            {
                MessageBox(NULL, L"指令修改失败", L"失败", MB_OK);
                return false;
            }
            break;
        }
    }


    return true;
}

参考

猜你喜欢

转载自www.cnblogs.com/Roachs/p/9365246.html
今日推荐