前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PVZ逆向分析与C#内存操作(含源文件)

PVZ逆向分析与C#内存操作(含源文件)

作者头像
DearXuan
发布2022-01-19 17:54:10
5640
发布2022-01-19 17:54:10
举报

查找阳光地址

首先打开植物大战僵尸,进入游戏,初始阳光为50.

DearXuan
DearXuan

打开CE修改器,搜索50

DearXuan
DearXuan

进入游戏,种植向日葵,阳光变成0,再次搜索

DearXuan
DearXuan

再进入游戏,收取阳光,阳光变成25,搜索25

DearXuan
DearXuan

显然地址0x144344C8保存的就是阳光,现在退出游戏重新打开,重复上面步骤

DearXuan
DearXuan

现在地址变成0x1408EDC0了,说明阳光的地址并不是固定的。对这个地址进行检测,查看是哪条指令在修改

DearXuan
DearXuan

我刚刚进行了种植和收集阳光两个步骤,显然mov是减少阳光,add是增加阳光。查看详细信息

DearXuan
DearXuan

得到EAX = 0x14089860,这个0x5560其实就是二级偏移.然而EAX也是动态变化的,我们需要在内存中搜索EAX,来查找它到底保存在哪个地方

DearXuan
DearXuan

由于这个地址保存了阳光的地址,所以它应该是不变的,否则就找不到阳光的地址了,所以可以多次重复扫描,确保把会改变的量排除。 逐一查看哪个操作码访问了上面地址,发现地址0x028CA730很有趣

DearXuan
DearXuan

列表里清一色的都是0x768,证明[EAX + 0x028CA730]保存了结构体地址,查看EAX地址

DearXuan
DearXuan

这里不管是查看EAX还是ECX,结果肯定是一样的,因为它们都指向同一个地址,且偏移也相同。EAX = 0x028C9FC8,而0x768就是一级偏移。继续搜索EAX

DearXuan
DearXuan

列表里出现绿色的基址,查找结束。

内存读写

开始写代码,C#无法直接修改内存,需要动态调用kernel32.dll

代码语言:javascript
复制
[DllImport("kernel32.dll", EntryPoint = "OpenProcess")]
public static extern IntPtr OpenProcess(int desiredAccess, bool heritHandle, int pocessID);
 
[DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
public static extern void CloseHandle(IntPtr hObject);
 
[DllImport("kernel32.dll", EntryPoint = "ReadProcessMemory")]
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr baseaddress, IntPtr buffer, int nsize, IntPtr bytesread);
 
[DllImport("kernel32.dll", EntryPoint = "WriteProcessMemory")]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr baseaddress, long[] buffer, int nSize, IntPtr byteswrite);

读写内存需要用到OpenProcess,官方文档里告诉我们第一个参数是访问权限,PROCESS_ALL_ACCESS指所有能获得的最高权限,但是PROCESS_ALL_ACCESS是在C++里定义的,C#里却没有,注意到这个值的类型是int,我们可以在C++里打印出这个值,然后直接写在C#里

DearXuan
DearXuan

所以我们只要输入0x1F0FFF就行了

代码语言:javascript
复制
private int ReadMemory(int pid,IntPtr toBase)
{
    byte[] bytes = new byte[4];
    IntPtr address = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0);
    IntPtr process = OpenProcess(0x1F0FFF, false, pid);
    ReadProcessMemory(process, toBase, address, 4, IntPtr.Zero);
    CloseHandle(process);
    return Marshal.ReadInt32(address);
}
private void WriteMemory(int pid,IntPtr toBase,int num)
{
    IntPtr process = OpenProcess(0x1F0FFF, false, pid);
    WriteProcessMemory(process, toBase, new long[] { num }, 4, IntPtr.Zero);
    CloseHandle(process);
}

获取进程PID

代码语言:javascript
复制
private int GetPid()
{
    Process[] processes = Process.GetProcessesByName(ProcessName);
    if(processes.Length == 0)
    {
        ShowDialog("没有检测到游戏进程.");
        return -1;
    }
    if(processes.Length > 1)
    {
        ShowDialog("检测到多个进程,这可能是因为您开启了多个相同进程名的软件,请关闭多余软件.");
        return -1;
    }
    return processes[0].Id;
}

定义全局变量

代码语言:javascript
复制
private const string ProcessName = "PlantsVsZombies";//进程名称
private const int sun = 9990;//每次修改的阳光数值
 
private int pid;//进程PID
private IntPtr intPtr;//阳光的地址

获取进程信息

代码语言:javascript
复制
private void GetInfo()
{
    pid = GetPid();
    if (pid == -1) return;
    int num1 = ReadMemory(pid, (IntPtr)0x006A9EC0);
    int num2 = ReadMemory(pid, (IntPtr)(num1 + 0x768));
    intPtr = (IntPtr)(num2 + 0x5560);
}

添加两个按钮,第一个按钮用来读取进程信息,第二个按钮用来修改阳光

代码语言:javascript
复制
private void button1_Click(object sender, EventArgs e)
{
    GetInfo();
}
private void button2_Click(object sender, EventArgs e)
{
    WriteMemory(pid, intPtr, sun);
}

修改成功

DearXuan
DearXuan

关闭植物大战僵尸,重新打开,再次尝试。

DearXuan
DearXuan

修改成功.

优化

DearXuan
DearXuan

MemoryIO.cs

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
 
namespace PVZ_Cheater
{
    static class MemoryIO
    {
        [DllImport("kernel32.dll", EntryPoint = "OpenProcess")]
        public static extern IntPtr OpenProcess(int desiredAccess, bool heritHandle, int pocessID);
 
        [DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
        public static extern void CloseHandle(IntPtr hObject);
 
        [DllImport("kernel32.dll", EntryPoint = "ReadProcessMemory")]
        public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr baseaddress, IntPtr buffer, int nsize, IntPtr bytesread);
 
        [DllImport("kernel32.dll", EntryPoint = "WriteProcessMemory")]
        public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr baseaddress, long[] buffer, int nSize, IntPtr byteswrite);
 
        public static int GetPid(string ProcessName)
        {
            Process[] processes = Process.GetProcessesByName(ProcessName);
            if (processes.Length == 0)
            {
                throw new Exception("没有检测到游戏进程.");
            }
            if (processes.Length > 1)
            {
                throw new Exception("检测到多个进程,这可能是因为您开启了多个相同进程名的软件,请关闭多余软件.");
            }
            return processes[0].Id;
        }
 
        public static int ReadMemory(int pid, IntPtr toBase)
        {
            byte[] bytes = new byte[4];
            IntPtr address = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0);
            IntPtr process = OpenProcess(0x1F0FFF, false, pid);
            ReadProcessMemory(process, toBase, address, 4, IntPtr.Zero);
            CloseHandle(process);
            return Marshal.ReadInt32(address);
        }
        public static void WriteMemory(int pid, IntPtr toBase, int num)
        {
            IntPtr process = OpenProcess(0x1F0FFF, false, pid);
            WriteProcessMemory(process, toBase, new long[] { num }, 4, IntPtr.Zero);
            CloseHandle(process);
        }
    }
}

PVZInfo.cs

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace PVZ_Cheater
{
    static class PVZInfo
    {
        public const string ProcessName = "PlantsVsZombies";
 
        public static int Pid;
 
        public static IntPtr Sun_Address;
        public static int Sun_Value = 9990;
 
        public static bool isReady = false;
        public static void GetInfo()
        {
            isReady = false;
            Pid = MemoryIO.GetPid(ProcessName);
            int num1 = MemoryIO.ReadMemory(Pid, (IntPtr)0x006A9EC0);
            int num2 = MemoryIO.ReadMemory(Pid, (IntPtr)(num1 + 0x768));
            Sun_Address = (IntPtr)(num2 + 0x5560);
            isReady = true;
        }
 
        public static void SetSun()
        {
            if(isReady) MemoryIO.WriteMemory(Pid, Sun_Address, Sun_Value);
        }
    }
}

增加一个Timer计时器,当选中”锁定阳光”选项时,每隔1秒将阳光赋值为9990,成功实现无限阳光

DearXuan
DearXuan

源文件

https://dearx.lanzoui.com/ibVLJrm95be

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021年7月19日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 查找阳光地址
  • 内存读写
  • 优化
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档