C语言制作植物大战僵尸无限阳光修改器

最近本人读了《Windows核心编程第五版》,受益匪浅,对操作系统产生了浓厚兴趣。

计划以后会开设相关分类的博客。

这里本人用CE修改器查找 植物大战僵尸 阳光指针永久基址。


关于CE(Cheat Engine)的用法,以后有时间的话 会发表相关博文。

根据查到指针永久基址+偏移地址 就可以获得/设置 阳光值。所以我们可以通过打开游戏进程,改写进程内相应的内存就可以实现游戏的修改。

现在直接上源代码:(控制台版修改器)

#include<stdio.h>
#include<windows.h>
#include<tchar.h>
int main(void)
{
	char input;
	HWND hWnd;  //窗口句柄
	DWORD pid;  //进程句柄
	HANDLE hProcess = 0;
	//阳光的基地址和偏移
	DWORD ba_addr = 0x007794F8;//基地址
	DWORD m_offset1 = 0x868;//偏移1
	DWORD m_offset2 = 0x5578;//偏移2
	printf(" O.打开游戏进程 M.修改阳光 \n");
	printf(" Q.退出外挂 \n");
	printf("------------------------------------------\n");
	while (1)
	{
		
		input = getchar();
		if (input == 'O' || input == 'o')
		{
			hWnd = FindWindow(NULL, _T("Plants vs. Zombies 1.2.0.1073 RELEASE") );
			if (hWnd != 0)
			{
				GetWindowThreadProcessId(hWnd, &pid);
				hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);//打开进程,取得句柄
				if (hProcess == 0)
				{
					printf("打开游戏进程失败\n");
				}
				else
				{
					printf("打开游戏进程成功!!\n");
				}
			}
			else
			{
				printf("游戏没有运行,或者没有使用管理员身份运行本外挂.\n");
			}
		}if (input == 'M' || input == 'm')
		{
			if (hProcess == 0)
			{
				printf("请先打开游戏进程");
			}
			else
			{
				DWORD m_tempadd;
				DWORD YG;
				//读取基址里面的数据(是一个指针)
				ReadProcessMemory(hProcess, (LPVOID)ba_addr, &m_tempadd, 4, 0);
				//基址加偏移地址1的数据(是一个指针)
				ReadProcessMemory(hProcess, (LPVOID)(m_tempadd + m_offset1), &m_tempadd, 4, 0);
			    /**********************
				*这里相当于一个二级指针(加偏移地址)
				*ReadProcessMemory函数 读取相应进程的指定位置 指定大小的数据到缓冲区
				*基址加偏移地址 为二级指针
				*基址加偏移地址取内容  再加 偏移地址为一级指针 (这个指针就是指向了阳光值的内存单元)
				******************/
				//要修改的内存地址是(tempadd+offset4)
				DWORD res = WriteProcessMemory(hProcess, (LPVOID)(m_tempadd+m_offset2), &YG, 4, 0);
				if (res == 0)
				{
					printf("修改失败");
				}
				else
				{
					printf("修改成功");
				}
			}
		}
		if (input == 'Q' || input == 'q')
		{
			break;
		}
	}
	return 0;
}


GUI版本修改器

WG.h

#pragma once
#include<stdio.h>
#include<windows.h>
#include<tchar.h>
extern HWND hWnd;  //窗口句柄
extern DWORD pid;  //进程句柄
extern HANDLE hProcess;
//阳光的基地址和偏移
extern DWORD ba_addr;//基地址
extern DWORD m_offset1;//偏移1
extern DWORD m_offset2;//偏移2
extern int openGameJc();//打开游戏进程
extern int UpdateYG(int value);//修改阳光
WG.C


#include"WG.h"
HWND hWnd;  //窗口句柄
DWORD pid;  //进程句柄
HANDLE hProcess = 0;
//阳光的基地址和偏移
DWORD ba_addr = 0x007794F8;//基地址
DWORD m_offset1 = 0x868;//偏移1
DWORD m_offset2 = 0x5578;//偏移2
int UpdateYG(int value)
{

	if (hProcess == 0)
	{
		return 0;
	}
	else
	{
		DWORD m_tempadd;
		DWORD YG;
		YG = (unsigned long)value;
		//读取基址里面的数据(是一个指针)
		ReadProcessMemory(hProcess, (LPVOID)ba_addr, &m_tempadd, 4, 0);
		//基址加偏移地址1的数据(是一个指针)
		ReadProcessMemory(hProcess, (LPVOID)(m_tempadd + m_offset1), &m_tempadd, 4, 0);
		/**********************
		*这里相当于一个二级指针(加偏移地址)
		*ReadProcessMemory函数 读取相应进程的指定位置 指定大小的数据到缓冲区
		*基址加偏移地址 为二级指针
		*基址加偏移地址取内容  再加 偏移地址为一级指针 (这个指针就是指向了阳光值的内存单元)
		******************/
		//要修改的内存地址是(tempadd+offset4)
		DWORD res = WriteProcessMemory(hProcess, (LPVOID)(m_tempadd + m_offset2), &YG, 4, 0);
		if (res == 0)
		{
			return 0;
		}
		else
		{
			return 1;
		}
	}
}
int openGameJc()
{
	hWnd = FindWindow(NULL, _T("Plants vs. Zombies 1.2.0.1073 RELEASE"));
	if (hWnd != 0)
	{
		GetWindowThreadProcessId(hWnd, &pid);
		hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);//打开进程,取得句柄
		if (hProcess == 0)
		{
			return 0;
		}
		else
		{
			return 1;
		}
	}
	else
	{
		return 0;
	}
}

main.c

#include<Windows.h>
#include"WG.h"
#include<stdlib.h>
HINSTANCE appInstance;
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, PSTR szCmdLine, int iCmdShow)
{
	//
	appInstance = hInstance;
	//
	static TCHAR szAppName[] = TEXT("MyWindows");
	HWND hwnd;
	MSG msg;
	WNDCLASS wndclass;
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0; //额外空间
	wndclass.cbWndExtra = 0; //额外空间
	wndclass.hInstance = hInstance; //实例句柄
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);//图标
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);//光标
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//白色
	wndclass.lpszMenuName = NULL;//菜单
	wndclass.lpszClassName = szAppName;//窗口类名

									   //注册窗口类
	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("这个程序需要在 Windows NT 才能执行"), szAppName, MB_OK);
		return 0;
	};
	//创建窗口 设置窗口属性
	hwnd = CreateWindow(szAppName, //窗口类名称
		TEXT("植物大战僵尸外挂v1.0"), //窗口标题
		WS_OVERLAPPEDWINDOW, //窗口风格
		200,   //CW 创建窗口选项 初始 x坐标
		200, //初始y坐标
		300, //初始 x方向尺寸
		300, //初始y方向尺寸
		NULL,    //父窗口句柄
		NULL,     //窗口菜单句柄
		hInstance,  //程序实例句柄
		NULL   //创建参数
	);
	//CreateWindow后 会产生几个 非队列消息  直接调用过程函数(重点)
	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);//(重点) UpdateWindow会 直接发送一个非队列的 WM_PAINT消息 让窗口绘制
					   //因为 WM_PAINT默认是在 队列最后的 但是这里在最后的话 就不好,一个窗口创建 后 ShowWindow后就应该立即显示上面的文字
					   //所以调用UpdateWIndow 让它立即绘制



					   /******************
					   *消息循环,操作系统 会把所有 消息 分配给 每个应用程序
					   * 所以每个应用程序都有一个消息队列,GetMessage就是向当前程序的 消息队列内获取消息
					   *lpMsg第一个参数 用于存放当前获取的消息
					   *hWnd需要获取消息的 窗口句柄,为NULL时 获取当前程序所有窗口的消息(必须属于当前线程)
					   *wMsgFilterMin 指定获取消息值的最小整数(消息其实就是整数)
					   *wMsgFilterMax 指定获取小的最大值
					   *********************/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);//函数将虚拟键消息 转换为字符消息,
		DispatchMessage(&msg);//函数分派一个消息给窗口过程函数,实际上是给操作系统操作系统调用的


	}
	return msg.wParam;
	/***************
	*1、在WinMain主函数中,最后的返回值是msg.wParam,这个参数是传递给void PostQuitMessage(int nExitCode); 这个函数的参数nExitCode的。
	* 2、nExitCode:指定应用程序退出代码。此值被用作消息WM_QUIT的wParam参数。
	*3、总之,当接收到一个WM_QUIT消息时,程序就中止。这时,WinMain函数应退出应用程序,并且返回传递给WM_QUIT消息的wParam参数的值。如果由于调用PostQuitMessage函数而接收到WM_QUIT消息,此时WM_QUIT消息的wParam的值即是PostQuiMessage函数的nExitCode的值。nExitCode一般为0。
	***************/
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HWND hButton;
	HWND hEdit;
	HDC hdc; //Handle Device Context
	HWND hbtnUpdate;//修改阳光
	PAINTSTRUCT ps;
	RECT rect;
	LPTSTR result[128]; //结果
	switch (uMsg)
	{
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		GetClientRect(hWnd, &rect);
		DrawText(hdc, TEXT("叫我涵涵"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
		EndPaint(hWnd, &ps);
		break;
	case WM_CREATE:
	
		hEdit = (HWND)CreateWindow(TEXT("edit"), NULL, WS_CHILD | WS_VISIBLE  | WS_BORDER | ES_LEFT  ,
			120, 0,150, 20, hWnd, (HMENU)111, appInstance, NULL);
		hButton = (HWND)CreateWindow(TEXT("Button"),  //Button是预定义 窗体类
			TEXT("打开游戏进程"),
			WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
			10, 10, 100, 50,
			hWnd,
			(HMENU)110,  //(重点)这里设置按钮id,但是 原本是设置菜单的 所以需要HMENU
			appInstance,
			NULL);
		//修改阳光
		hbtnUpdate = (HWND)CreateWindow(TEXT("Button"),  //Button是预定义 窗体类
			TEXT("修改阳光"),
			WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
			120, 40, 150, 40,
			hWnd,
			(HMENU)112,  //(重点)这里设置按钮id,但是 原本是设置菜单的 所以需要HMENU
			appInstance,
			NULL);
		break;
		/*******************
		* 当用户点击菜单、按钮、下拉列表框等控件时候,
		*会触发WM_COMMAND.LOWORD(wParam) 是控件或菜单或加速键的ID
		*如果LOWORD(wParam) 是控件ID,HIWORD(wParam)是notification code,
		*比如BN_CLICKED, BN_DBLCLK等,标志用户对控件的操作,双击,单击
		*lparam 是子窗体句柄
		*********************/
	case WM_COMMAND:
		//按钮
		if (LOWORD(wParam) == 112 && HIWORD(wParam) == BN_CLICKED)
		{
			//根据父窗口句柄 以及 控件id, 获取 空间句柄,然后用GetWindowText获取 控件内的文本
			GetWindowText(GetDlgItem(hWnd, 111), result, 128);
			if (UpdateYG(_ttoi(result)) == 1)
			{
				MessageBox(hWnd, TEXT("修改成功"), TEXT("涵涵提示"), MB_OK);
			}
			else {
				MessageBox(hWnd, TEXT("修改失败"), TEXT("涵涵提示"), MB_OK);
			}
			
		}
		if (LOWORD(wParam) == 110 && HIWORD(wParam) == BN_CLICKED)
		{
			if(openGameJc()==1){

				MessageBox(hWnd, TEXT("开启游戏进程成功"), TEXT("提示"), MB_OK);
			}
			else {
				MessageBox(hWnd, TEXT("开启游戏进失败"), TEXT("提示"), MB_OK);
			}
				
		}
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
		/********成功退出 ****
		****************
		*用户通过点击关闭程序按钮后,消息队列增加一条消息WM_CLOSE,
		*然后程序从消息队列中取走WM_CLOSE,调用DestroyWindow(),
		*消息队列增加WM_DESTROY,应用程序再次取走,并调用PostQuitMessage(),
		*最终得到WM_QUIT而使消息循环退出,程序退出。
		************/
	default:
		//我们不想关心的都用 默认处理方式
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
		break;
	}
	return 0;
}

作者原创,QQ:1031893464.,请勿转载。


©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值