使用的软件:
Cheat Engine什么是基址
内存基址在CE中也有静态地址的说法,它永远不会改变。程序通过对基址的偏移(指针)创建一个新的内存地址,也就是动态地址。
通常使用CE或者其他内存修改工具修改例如金币、血量等数据时经常会发现对应的地址会随着程序的重新启动而变动,我们在不知道基址和偏移量的情况下,无法预知它在下一次启动时的地址。
我们可以通过在CE反编译的汇编中得到的偏移量,逆向推算出它的基址是什么,在软件开发当中只需要将基址按照偏移量重新计算就可以得到它的动态地址了。
1.通过“谁访问”和“谁改写”索引到基址
还是用经典的植物大战僵尸的阳光举例子,因为他并没有对变量进行奇怪的加密。
首先通过最基本的数值查找,找到阳光的动态地址,这里就不再演示了,如果不清楚这一步操作是怎么实现的,我想你也许去会看一下CE的基础使用教程
打开“找出是什么访问了这个地址”(注意,选择“是什么改写了这个地址”也是差不多的,但是打开可能什么内容都没有,需要重新回到游戏中对阳光进行修改等操作)
我们看到这边出现了一个add和一个mov指令,双击打开mov查看详细信息。
这一段mov eax.[esi+00005560]意思是将esi+00005560的值存入eax中。此时esi的值为1DB11B90
从新搜索出的51个值中筛选,去除上面的动态值,先看看0277A908,右键“找出是什么访问了这个地址”
发现一条mov指令,它将edi+00000768的值存入esi,此时发现esi就是我们搜索的1DB11B90,看起来第一个就找对了。它是通过对edi进行运算得到的,所以继续向上搜索edi的值0277A1A0
继续搜索edi(0277A1A0)的值出现了1972个结果,不用管,因为我们发现出现了4个绿色的静态地址(绿色在CE中代表静态地址,黑色代表动态地址),选择一个试试。
通过手动添加地址,添加我们刚刚选择下来的第一个绿色静态地址的地址,然后添加偏移,还记得我们之前的偏移是多少吗。首先是edi+00000768 然后是 esi+00005560,天上偏移量。我们可以看到最终它会将值指向我们1DB170F0的值,地址也指向了1DB170F0,这个地址就是我们最开始添加的阳光的动态地址,那么说明,006A9EC0就是我们要找的静态基址
有了这个基质我们下一次就可以直接通过访问这个基址+偏移量来访问到这个进程的这段地址
例如我这边可以通过C#来简单编写一个修改器的小程序
using System;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
int dataAddress;
string gamename = "PlantsVsZombies";
IntPtr SecondAddress;
IntPtr hAddress;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
IntPtr defaultAddress = new IntPtr(0x006A9EC0);//基址
hAddress = OpenProcess(0x1F0FFF, false, GetPidByProcessName(gamename));
MessageBox.Show(GetPidByProcessName(gamename).ToString());
MessageBox.Show(hAddress.ToString());
ThreadPool.QueueUserWorkItem(I =>
{
while (true)
{
ReadProcessMemory((int)hAddress, defaultAddress, out dataAddress, 4, 0);
IntPtr FristAddress = new IntPtr(dataAddress);
FristAddress = IntPtr.Add(FristAddress, 0x768);//加上偏移
ReadProcessMemory((int)hAddress, FristAddress, out dataAddress, 4, 0);
SecondAddress = new IntPtr(dataAddress);
SecondAddress = IntPtr.Add(SecondAddress, 0x5560);//加上偏移
ReadProcessMemory((int)hAddress, SecondAddress, out dataAddress, 4, 0);
label2.Text = dataAddress.ToString();
Thread.Sleep(500);
}
});
}
private void button1_Click(object sender, EventArgs e)
{
WriteMemoryValue((int)SecondAddress, gamename, int.Parse(textBox1.Text));
bool aa = ReadProcessMemory((int)hAddress, SecondAddress, out dataAddress, 4, 0);
label2.Text = dataAddress.ToString();
}
//将值写入指定内存地址中
public static void WriteMemoryValue(int baseAddress, string processName, int value)
{
IntPtr hProcess = OpenProcess(0x1F0FFF, false, GetPidByProcessName(processName)); //0x1F0FFF 最高权限
WriteProcessMemory(hProcess, (IntPtr)baseAddress, new int[] { value }, 4, IntPtr.Zero);
CloseHandle(hProcess);
}
//根据进程名获取PID
public static int GetPidByProcessName(string processName)
{
Process[] arrayProcess = Process.GetProcessesByName(processName);
foreach (Process p in arrayProcess)
{
return p.Id;
}
return 0;
}
[DllImportAttribute("kernel32.dll", EntryPoint = "ReadProcessMemory")]
static extern bool ReadProcessMemory(int hProcess, IntPtr lpBaseAddress, out int lpBuffer, int nSize, int lpNumberOfBytesRead);
[DllImportAttribute("kernel32.dll", EntryPoint = "OpenProcess")]
public static extern IntPtr OpenProcess
(
int dwDesiredAccess,
bool bInheritHandle,
int dwProcessId
);
[DllImport("kernel32.dll")]
private static extern void CloseHandle
(
IntPtr hObject
);
//写内存
[DllImportAttribute("kernel32.dll", EntryPoint = "WriteProcessMemory")]
public static extern bool WriteProcessMemory
(
IntPtr hProcess,
IntPtr lpBaseAddress,
int[] lpBuffer,
int nSize,
IntPtr lpNumberOfBytesWritten
);
}
}
2.通过指针扫描寻找基址
这一段我想以王国保卫战(Kingdom Rush)举例子
通过两次搜索就能获取到我们当前金币的地址1A887D10,修改它就能修改我金币的数量,但是我们需要的是基址
右键“对这个地址进行指针扫描”
点击确定,并选择一个存储位置,开始扫描
查找到1319个值,问题不大,我们像正常使用CE搜索数值那样,点击“重新扫描内存”,我们回到游戏,修改一下金币数量,选择“要查找的数值”,输入我们刚刚修改完后的数量。注意,这里要选择正确的数据类型,因为我这边是双浮点,所以选择Double。点击确定,再次搜索
还剩下221个,我们再反复这一步操作即可
还剩下207个
反复操作数量并没有显著的减少,我们可以尝试重启游戏再来搜索试试
重启后,再次选择游戏进程,(不要抛弃之前扫描结果)打开指针扫描器,打开之前保存的扫描结果
随便修改一下游戏内金币数量后,再次搜索一下,去除那些空值,最终只留下一个,我们来看一下它是不是就是我们想要的基址
很明显,这就是我们想要的基址"THREADSTACK0"-000000F8,双击一下显示详细信息,我们还可以看到它的偏移量
这个方法更接近于我们正常使用CE的操作,但是在一些大型游戏中,使用这个方法可能需要非常长的时间来一遍遍的筛选结果