植物大战僵尸数据修改器
前言
你好! 这是本人第一次使用 Markdown编辑器 所展示的欢迎页。接下来我们一起使用Microsoft Visual C++ 6.0编写经典游戏《Plants V.S. Zombies》外挂,实现改变游戏底层数据的功能,功能实现是简单的,但后面带来的收益却是巨大的,希望观者见微知著,不拘于束。本章涉及到的内容如下:
- 关于visual自带tools的**spy++**的使用。
- 关于Windows错误代码的获取和原因查询。
- 对于基地址和偏移量的获取方法和计算过程。
知识点
本项目使用若干知识点,为了方便未来的查询与使用,本人将知识点提出列开。下面对本项目使用到的知识点展开来讲。
关于spy++的使用
spy++作为一款visual studio自带的一款工具,可以非常方便地获取选定窗口的句柄、标题和类名。
意义:在本项目中配合windows.h中自带的方法,获取窗口句柄,使程序锁定窗口,准备下一步操作。
使用方法:
- 点击tools——>spy++,进入工具窗口
- 点击望远镜,拖动finder tool到指定窗口即可
故此我们便获取到了窗口句柄、标题、类名。为接下来的程序编写准备。
关于windows错误的获取和查询
在程序运行中可能会遇到错误,这时候我们用windows自带的方法GetLastError()即可获取错误代码,将错误代码通过tools中的Error lookup进行原因查找。
例如:
printf("获取一级偏移地址失败,错误代码:%d\n",GetLastError());
这Error lookup只能查找到问题原因,不提供解决办法。
对于基地址的获取方法和计算步骤
以《植物大战僵尸》为例,数据的储存往往是从基地址经历几个偏移量的偏移才得到最终地址。那么如何获取某个数据的基地址呢,一个成熟的程序员要懂得借用已有的工具——Cheat Engine。
- 打开运行Cheat Engine,搜索并获得“阳光”的地址(具体方法请百度,本处不再赘述),这时候的地址是多次偏移以后的地址,我们这里称作最终地址,我查出来的地址为233A9848(地址为16进制)。
- 获取一级偏移量和地址、获取二级偏移量和地址请直接参考下面地址。(太多太麻烦)
- 添加一条说明:基地址的内容是一个静态地址,静态地址+一级偏移量=一级偏移地址;一级偏移地址+二级偏移量=二级偏移地址(到这里就是一开始得到的最终地址)。但是用CE查找的时候是反着找的,先找了最终地址然后找的二级偏移地址,然后是一级,再然后是初地址,初地址存在于绿色的基地址。
注意:不同版本的同款游戏,基地址和偏移量都可能有所不同。建议参考下面链接来获取自己的数据。
参考来自:https://www.cnblogs.com/gd-luojialin/p/7789569.html
推荐视频:http://www.iqiyi.com/w_19rt636lht.html?fromvsogou=1
示例代码
#include<stdio.h>
#include<Windows.h>
#include<String.h>
int main()
{
//1.运行游戏,通过spy++获取窗口信息
HWND hGameWnd = FindWindow("MainWindow","Plants vs. Zombies 1.2.0.1073 RELEASE");
if(hGameWnd == NULL){
printf("没有运行游戏");
getchar();
return 0;
}
//2.根据窗口句柄获取进程PID,Process ID
DWORD dwPID = 0;
GetWindowThreadProcessId(hGameWnd,&dwPID);
if(dwPID == 0){
printf("获取进程PID失败,错误代码:%d\n",GetLastError());
return 0;
}
//3.根据PID获取进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPID);
if (hProcess == NULL){
printf("打开进程失败,错误代码:%d\n",GetLastError());
return 0;
}
//4.从指定的进程内存区域去读取数据
DWORD dwSunshineBaseAddress = 0x7578F8;
DWORD dwSunshineAddressValue = 0;
DWORD dwSize = 0;
if(FALSE == ReadProcessMemory(
hProcess, //哪一个进程
(LPVOID)dwSunshineBaseAddress, //要读取的进程(基地址)
&dwSunshineAddressValue, //接收进程数据
sizeof(DWORD), //要修改的字节数
&dwSize //实际修改的字节数
)){
printf("获取静态地址失败,错误代码:%d\n",GetLastError());
return 0;
}
else printf("静态地址为:%x\n",dwSunshineAddressValue);//静态地址(16进制)
//5.获取一级偏移地址
DWORD dwSunshineOffsetFirst = 0x868;//一级偏移量
DWORD dwSunshineOffsetValue = 0; //一级偏移地址的容器
if(FALSE == ReadProcessMemory(
hProcess,
(LPVOID)(dwSunshineOffsetFirst+dwSunshineAddressValue),//一级偏移地址的计算
&dwSunshineOffsetValue,//一级偏移地址的赋值
sizeof(DWORD),
&dwSize
)){
printf("获取一级偏移地址失败,错误代码:%d\n",GetLastError());
return 0;
}
else printf("一级偏移地址:%x\n",dwSunshineOffsetValue);//一级偏移地址(16进制)
//6.获取二级偏移地址
DWORD dwSunshineOffsetSecond = 0x5578; //二级偏移量
DWORD dwSunshine = 0; //二级级偏移地址(最终地址)的容器
if(FALSE == ReadProcessMemory(
hProcess,
(LPVOID)(dwSunshineOffsetSecond+dwSunshineOffsetValue),//二级偏移地址的计算
&dwSunshine,
sizeof(DWORD),
&dwSize
)){
printf("获取二级偏移地址失败,错误代码:%d\n",GetLastError());
return 0;
}
else printf("二级偏移地址:%x\n",dwSunshine);//二级偏移地址(16进制)
printf("阳光值为%d",dwSunshine);
int NowSunshine=0; //想得到的阳光值
printf("请输入想要修改的阳光值:");
scanf("%d",&NowSunshine);
//修改阳光值
WriteProcessMemory(
hProcess,
(LPVOID)(dwSunshineOffsetSecond+dwSunshineOffsetValue),
&NowSunshine,
sizeof(DWORD),
&dwSize
);
return 0;
}
效果图
总结
在《Plants V.S. Zombies》中,可以通过修改基地址和两个偏移量来实现 金币数、阳光数的修改,还可以修改本游戏的其他基础数据,也能修改其他单机游戏的数据。此方法适合运用在公司或学校这类统一性比较强的地方,相比于每次打开CE修改数据,此方法操作更加方便简单,传播更加便捷。玩过的都说好