文章中的文字可能存在语法错误以及标点错误,请谅解;
如果在文章中发现代码错误或其它问题请告知,感谢!
1.cheat engine:
https://www.cheatengine.org/
2.Visual Studio 2010
3.植物大战僵尸
我们打开的每一个程序中,存储数据的内存地址都会发生改变,但是程序的基址和偏移不会变。所谓基址,可以简单理解为不会随程序运行的起始时间和环境改变的地址,即全局数据区的数据的地址。 所以,我们关闭游戏再启动,存放游戏关键值的变量的地址不变。
所以现在我们利用cheat engine工具找到游戏的基址和偏移,获取到之后,再利用C语言中ReadProcessMemory()和WriteProcessMemory()进行修改地址所指向的值就可以了。
首先打开植物大战僵尸游戏:
然后打开cheat engine,将植物大战僵尸附加到该应用上:
接下来看一下游戏左上角的太阳值,将太阳值写入value中,然后单击First Scan:
此时在cheat engine中左侧的内容区出现包含和刚才输入值相同的数据行,然后回到游戏,等待新的小太阳的出现,当捡到小太阳后,太阳值将发生改变,现在回到cheat engine中,观察内容区Value列,看该列数据是否出现变化的行(出现变化的行也会自动标红显示,方便查找):
上图中变化的行已经标记出来,为了确定该行和游戏中的太阳值相关,多在游戏中捡几个小太阳,看看标记的Value列是否跟着游戏改变,确定该地址和太阳值有关后,双击左键,将该行标记在最下对话框中,然后单击右键,选择“Find out what writes to this address”:
此时弹出一个对话框,对话框中没有内容,此时回到游戏中进行使用或收集太阳的操作,现在,对话框中出现数据操作的信息。注意,当你捡太阳值是“add……”信息,当你使用太阳值是“mov……”信息,双击以观察详细信息:
现在,在弹出对话框中观察红字部分:mov [edi + 00005560] ,esi,该指令意思是将esi的值传递给[edi + 00005600]所指的地址中,其中00005560为偏移量,为十六进制数值。通过查看edi的值得知,edi为1476A5B0(或者直接查看The value……probaply 1476A5B0那一行提示),这样,我们获得了上级地址0x1476A5B0和偏移00005560。然后在The value……probaply 1476A5B0那一行提示右键复制上级地址值。
关闭两个对话框,点击New Scan重新扫描,将上级地址粘贴到value中,勾选Hex,点击First Scan:
此时左侧列表出现很多值,仔细观察一会儿,会发现有些行数据一直变红变黑,表明数据值在变化,点击“Next Scan”将这些变化的值过滤掉(因为我们查找的value值一直没变是0x1476A5B0)。
回到游戏,继续捡太阳或者使用太阳值,然后选择左侧对话框中从第一行开始选择,右键选择“Find out what accessses this address”,直到在弹出的对话框中有类似mov……的指令为止。
同前,此时得到偏移0000768,上级地址为0x0283A1F8,将左侧对话框中的该行标记在最下对话框,关闭弹出的两个对话框,开始新一次Scan,复制0283A1F8到value中,勾选Hex,然后单击“First Scan”:
此时观察左侧对话框中有绿色的行,证明我们找到了一个绿色的静态地址(绿色显示的是基址),现在选择“Add Address Manually”,在弹出对话框中选择 “Pointer”:
在弹出的对话框中将基址输入的框中,然后单击两次“Add offset”(因为我们经过两次的寻址),依次填入偏移,一次偏移为0x0000768,一次为0x00005560(从最近一次的偏移开始填),填写完成后,查看Address中的数值是否为游戏中小太阳的值,如下图:
我们现在理清一次思路:
由基址0x006A9EC0里的值+0x768后是个地址,
该地址里的值+0x5560就是存储小太阳值的内存单元。
所以,现在我们找到了基址,然后在编写代码时将该基址加上0x768以及0x5560就是小太阳的内存单元。
#include
#include
#define SUN_SHINE_BASE_ADDR 0x006A9EC0 //阳光基址
#define SUN_SHINE_OFFSET_FIRST 0x768 //一级偏移
#define SUN_SHINE_OFFSET_SECOND 0x5560 //二级偏移值
int main()
{
int modifySunshine = 0;
DWORD ErrorInfo = 0;
DWORD Size = 0;
DWORD SunShineNum=0; //最后值
DWORD PID = 0;
DWORD SunShineBaseAddress = SUN_SHINE_BASE_ADDR; //阳光基址
DWORD SunShineBaseAddressValue = 0; //阳光基址值
DWORD SunShineOffsetFirst = SUN_SHINE_OFFSET_FIRST; //一级偏移
DWORD SunShineOffsetFirstValue = 0; //一级偏移值
DWORD SunShineOffsetSecond = SUN_SHINE_OFFSET_SECOND; //二级偏移
HANDLE Process = 0;
HWND hWinmine = FindWindowW(NULL, L"植物大战僵尸中文版"); //找到窗口
GetWindowThreadProcessId(hWinmine, &PID); //获取进程标识
if (0 == PID)
{
printf_s("获取PID失败\n");
return -1;
}
Process = OpenProcess(PROCESS_ALL_ACCESS, 0, PID);
if (NULL == Process )
{
printf_s("进程打开失败\n");
ErrorInfo = GetLastError();
return -1;
}
if (0 == ReadProcessMemory(Process, (LPVOID)SunShineBaseAddress, &SunShineBaseAddressValue, sizeof(DWORD), &Size))
{
printf_s("获取基址失败\n");
ErrorInfo = GetLastError();
return -1;
}
if (0 == ReadProcessMemory(Process, (LPVOID)(SunShineBaseAddressValue + SunShineOffsetFirst), &SunShineOffsetFirstValue, sizeof(DWORD), &Size))
{
printf_s("获取一级偏移失败\n");
ErrorInfo = GetLastError();
return -1;
}
if (0 == ReadProcessMemory(Process, (LPVOID)(SunShineOffsetFirstValue + SunShineOffsetSecond), &SunShineNum, sizeof(DWORD), &Size))
{
printf_s("获取二级偏移失败\n");
ErrorInfo = GetLastError();
return -1;
}
printf_s("SunShineNum:%d\n", SunShineNum);
printf_s("输入你要修改的值:");
scanf_s("%d", &modifySunshine);
WriteProcessMemory(Process, (LPVOID)(SunShineOffsetFirstValue + SunShineOffsetSecond), &modifySunshine, sizeof(DWORD), &Size);
CloseHandle(Process); //关闭句柄
system("pause");
return 0;
}
运行结果如下图,成功修改了小太阳的值:
因为获取的是静态基址,所以就算游戏关闭了,再开,也可以运行该代码进行修改。
以上。
参考文档:
https://blog.csdn.net/qq78442761/article/details/54670630
https://blog.csdn.net/xiaokangdream/article/details/79366176
https://www.cnblogs.com/gd-luojialin/p/7789569.html
https://blog.csdn.net/ChristmasYe/article/details/8757547
https://blog.csdn.net/xiaokangdream/article/details/79356831
https://blog.csdn.net/wangqingchuan92/article/details/82417454