跳至主要內容

读写阳光

chanchaw大约 3 分钟game-plugin

创建项目

创建控制台应用程序

地址类

头文件

头文件 Addr.h 的源码:

#pragma once

#ifndef ADDR_H
#define  ADDR_H

#include <iostream>
#include <Windows.h>
using namespace std;

class Addr
{
private:
	UINT_PTR addr;
	Addr* child;
public:
	Addr(UINT_PTR a);
	void setAddr(UINT_PTR a);
	UINT_PTR getAddr();

	void setChild(Addr* a);
	Addr* getChild();
	string toString();
};

#endif

类源码文件

类源码文件 Addr.cpp 的源码

#include "Addr.h"
#include<sstream>
using namespace std;

// 构造函数
Addr::Addr(UINT_PTR a):addr(a) {}

void Addr::setAddr(UINT_PTR a) {
	addr = addr;
}

UINT_PTR Addr::getAddr() {
	return addr;
}

void Addr::setChild(Addr* a) {
	child = a;
}

Addr* Addr::getChild() {
	return child;
}

string Addr::toString() {
	return "addr:"+std::to_string(addr);
}

入口文件

项目入口类文件的全部源码:

#include <iostream>
#include <Windows.h>
#include <string>
#include "Addr.h"
using namespace std;

#pragma region 读取阳光相关的全局变量
HWND gameWindowHwnd = 0;// 游戏窗口句柄
DWORD pid, tid;// 游戏 pid,tid.十进制数字
HANDLE processHandle = 0;// 游戏进程句柄
// 下面的基址和偏移是180天翼云盘家庭共享目录下:教程与资料 > 郁金香2021 > 用到的工具 > 用到的游戏和工具 下的 “GMEPlantsVsZombie练习用”
// 中的数据,已经验证正确
UINT_PTR BASE_ADDR = 0x006A9EC0;
UINT_PTR OFFSET01 = 0x768;
UINT_PTR OFFSET02 = 0x5560;
UINT_PTR directAddr = 0;// 阳光的直接地址
#pragma endregion

unsigned int sunVal = 0;// 阳光值
Addr* addrTree = NULL;// 递归结构内存地址

void showMenu()
{
	cout << "************************" << endl;
	cout << "***** 1. 获取进程句柄 *****" << endl;
	cout << "***** 2. 读取阳光数据 *****" << endl;
	cout << "***** 3. 写入阳光5K *****" << endl;
	cout << "***** 0. 退出系统   *****" << endl;
	cout << "请输入要操作的编号:" << endl;
}

void printl(string msg) {
	cout << msg << endl;
}

#pragma region 读取阳光
// 初始化基址和偏移对象,递归属性对象,读取阳光值时通过递归算法应用该基址+偏移的数据结构
void initAddr() {
	addrTree = new Addr(BASE_ADDR);
	Addr* offset1P = new Addr(OFFSET01);
	Addr* offset2P = new Addr(OFFSET02);

	offset1P->setChild(offset2P);
	addrTree->setChild(offset1P);
	//cout << "已初始化基址和偏移,十进制基址是:" << baseAddrP->toString() << endl;
}

// 读取指定内存地址的4字节数据
unsigned int read(UINT_PTR addr) {
	// 使用进程句柄读取游戏数据
	unsigned int readBuff = 0;
	ReadProcessMemory(processHandle, (LPCVOID)addr, &readBuff, 4, 0);
	//printf("从地址:%d(十进制),%X(十六进制) 读取到的4字节数据的\r\n16进制,10进制,无符号数据依次是:%X,%d,%u\r\n", addr, addr, readBuff, readBuff, readBuff);// 如果读取过程出错使用 GetLastError() 获取并处理
	return readBuff;
}

// 获取游戏进程句柄
int getProcessHandler() {
	if (processHandle > 0) {
		printf("已经获取进程句柄是:%d", processHandle);
		return 1;
	}

	// 获取窗口句柄
	gameWindowHwnd = FindWindowA("MainWindow", "植物大战僵尸中文版");
	printf("游戏窗口句柄:%p\r\n", gameWindowHwnd);

	// 使用窗口句柄获取 pid,tid
	tid = GetWindowThreadProcessId(gameWindowHwnd, &pid);
	printf("十进制:\r\ntid=%d,pid=%d\r\n", tid, pid, pid);
	printf("十六进制:\r\ntid=%X,pid=%X\r\n", tid, pid, pid);

	// 使用 pid 获取进程句柄
	processHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
	printf("游戏进程句柄:%d\r\n", processHandle);

	// 初始化基址对象
	initAddr();
	return 1;
}

// 递归读取内存数据
void recursionAddr(Addr* addr) {
	unsigned int addrVal = addr->getAddr();
	Addr* child = addr->getChild();

	directAddr = sunVal + addrVal;
	sunVal = read(directAddr);
	if (child != NULL) recursionAddr(child);
}


// 执行读取阳光
void getSunNum() {
	//read(read(read(BASE_ADDR) + OFFSET01) + OFFSET02);
	if (processHandle == 0) getProcessHandler();// 先获取进程句柄再获取数据

	sunVal = 0;
	recursionAddr(addrTree);
	cout << "阳光:" << sunVal << endl;
}

void writeSunVal() {
	if (directAddr == 0) getSunNum();// 先读取一次阳光并获得阳光的直接地址
	int newValue = 5000;
	WriteProcessMemory(processHandle, (PBYTE*)directAddr, &newValue, sizeof(newValue), 0);
}
#pragma endregion



int main()
{
	int select = 0;

	while (true)
	{
		showMenu();
		cin >> select;

		system("cls");
		switch (select)
		{
		case 1:
			getProcessHandler();
			break;
		case 2:
			getSunNum();
			break;
		case 3:
			writeSunVal();
			break;
		case 0:
			printl("欢迎再来!");
			system("pause");
			return 0;
		default:
			cout << "请输入0-4进行操作!" << endl;
			break;
		}
		cout << endl << endl << endl;
	}
	system("pause");
	return 0;
}