基于visual的单机游戏外挂

前言

你好! 这是本人第一次使用 Markdown编辑器 所展示的欢迎页。接下来我们一起使用Microsoft Visual C++ 6.0编写经典游戏《Plants V.S. Zombies》外挂,实现改变游戏底层数据的功能,功能实现是简单的,但后面带来的收益却是巨大的,希望观者见微知著,不拘于束。本章涉及到的内容如下:

  1. 关于visual自带tools的**spy++**的使用。
  2. 关于Windows错误代码的获取和原因查询。
  3. 对于基地址和偏移量的获取方法和计算过程。

知识点

本项目使用若干知识点,为了方便未来的查询与使用,本人将知识点提出列开。下面对本项目使用到的知识点展开来讲。

关于spy++的使用

spy++作为一款visual studio自带的一款工具,可以非常方便地获取选定窗口的句柄、标题和类名

意义:在本项目中配合windows.h中自带的方法,获取窗口句柄,使程序锁定窗口,准备下一步操作。

使用方法:

  1. 点击tools——>spy++,进入工具窗口
    在这里插入图片描述
  2. 点击望远镜,拖动finder tool到指定窗口即可
    在这里插入图片描述
    故此我们便获取到了窗口句柄、标题、类名。为接下来的程序编写准备。

关于windows错误的获取和查询

在程序运行中可能会遇到错误,这时候我们用windows自带的方法GetLastError()即可获取错误代码,将错误代码通过tools中的Error lookup进行原因查找。

例如:

printf("获取一级偏移地址失败,错误代码:%d\n",GetLastError());

在这里插入图片描述
在这里插入图片描述
这Error lookup只能查找到问题原因,不提供解决办法。

对于基地址的获取方法和计算步骤

以《植物大战僵尸》为例,数据的储存往往是从基地址经历几个偏移量的偏移才得到最终地址。那么如何获取某个数据的基地址呢,一个成熟的程序员要懂得借用已有的工具——Cheat Engine。

  1. 打开运行Cheat Engine,搜索并获得“阳光”的地址(具体方法请百度,本处不再赘述),这时候的地址是多次偏移以后的地址,我们这里称作最终地址,我查出来的地址为233A9848(地址为16进制)。
  2. 获取一级偏移量和地址、获取二级偏移量和地址请直接参考下面地址。(太多太麻烦)
  3. 添加一条说明:基地址的内容是一个静态地址,静态地址+一级偏移量=一级偏移地址;一级偏移地址+二级偏移量=二级偏移地址(到这里就是一开始得到的最终地址)。但是用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修改数据,此方法操作更加方便简单,传播更加便捷。玩过的都说好

猜你喜欢

转载自blog.csdn.net/Norton_Paige/article/details/108284994
今日推荐