C语言实现植物大战僵尸自动收集阳光(二) C语言控制台程序的实现

C语言实现植物大战僵尸自动收集阳光(一) 问题分析与寻找基址
C语言实现植物大战僵尸自动收集阳光(二) C语言控制台程序的实现
C语言实现植物大战僵尸自动收集阳光(三) 解决收集不全与收集奖杯卡死的问题

前言

在上一篇,我已经在巧合下找到了实现自动收集的基地址
在本篇将着重讲解如何用c语言控制台程序实现植物大战僵尸的自动收集

前置内容

C语言实现植物大战僵尸自动收集阳光(一) 问题分析与锁定基址

C语言调用windows api : 简单的说,直接#include即可,更多请详见百度

windows api读内存与写内存:WriteProcessMemory与ReadProcessMemory

BOOL ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead
);

ReadProcessMemory

hProcess [in]远程进程句柄。 被读取者

pvAddressRemote [in]远程进程中内存地址。 从具体何处读取

pvBufferLocal [out]本地进程中内存地址. 函数将读取的内容写入此处

dwSize [in]要传送的**字节数。**要写入多少

pdwNumBytesRead [out]实际传送字节数. 函数返回时报告实际写入多少

BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesWritten
);

hProcess

由OpenProcess返回的进程句柄。

如参数传数据为 INVALID_HANDLE_VALUE 【即-1】目标进程为自身进程

lpBaseAddress

要写的内存首地址

在写入之前,此函数将先检查目标地址是否可用,并能容纳待写入的数据。

lpBuffer

指向要写的数据的指针。

nSize

要写入的字节数。

返回值

非零值代表成功。

完整代码

#include
#include 
#include 
#include 

//通过窗口名获取进程id 
HANDLE GetProcessH(char windowName[]){
	HWND hwnd=FindWindow(NULL,windowName);   
	DWORD processid;
	GetWindowThreadProcessId(hwnd, &processid);  
	return OpenProcess(PROCESS_ALL_ACCESS,false,processid);
}

//读进程内存 
DWORD GetMemory(HANDLE processid,DWORD addr){
	DWORD res;
	LPCVOID pbase=(LPCVOID)addr; 
	LPVOID buffer=(LPVOID)&res; 
	ReadProcessMemory(processid,pbase,buffer,sizeof(DWORD),NULL);
	return res;
}

//写进程内存 
int SetMemory(HANDLE processid,DWORD addr,DWORD value){
	LPVOID pbase=(LPVOID)addr; 
	LPCVOID buffer=(LPCVOID)&value; 
	SIZE_T res;
	return 	WriteProcessMemory(processid,pbase,buffer,sizeof(DWORD),&res);
}

//自动收集阳光
void AutomaticCollection(HANDLE processH){
	DWORD addr=GetMemory(processH,0x006A9EC0);
	addr=GetMemory(processH,addr+0x00000768);	
	addr=GetMemory(processH,addr+0xE4);
	DWORD sun=addr+0x50;
	if(GetMemory(processH,sun)==0)
		SetMemory(processH,sun,1);

}


int main(int argc, char** argv) {
	HANDLE processH=GetProcessH("植物大战僵尸中文版");
	while(1){
		AutomaticCollection(processH);
		Sleep(500);
	}
	
}

关键代码讲解

使用 windows api 读写进程内存的流程大体如下:

  1. 使用FindWindow(),通过窗口名获得窗口句柄
  2. 使用GetWindowThreadProcessId(),通过窗口句柄获得进程ID
  3. 使用OpenProcess(),打开指定进程ID,获得进程句柄
  4. 使用WriteProcessMemory()、ReadProcessMemory(),对进程进行读写

上述函数均为windows api,可查阅MSDN进行了解。

GetProcessH()函数

GetProcessH()函数对上述流程的1、2两点进行封装,输入窗口名,即可获得对应的进程句柄

HANDLE GetProcessH(char windowName[]){
	HWND hwnd=FindWindow(NULL,windowName);   
	DWORD processid;
	GetWindowThreadProcessId(hwnd, &processid);  
	return OpenProcess(PROCESS_ALL_ACCESS,false,processid);
}

读写内存进程函数

用两个函数封装了读写内存的过程,只需要输入进程句柄和对应地址即可。(参数名用processid其实描述的并不准确,此处应该用参数名processh)

//读进程内存 
DWORD GetMemory(HANDLE processid,DWORD addr){
	DWORD res;
	LPCVOID pbase=(LPCVOID)addr; 
	LPVOID buffer=(LPVOID)&res; 
	ReadProcessMemory(processid,pbase,buffer,sizeof(DWORD),NULL);
	return res;
}

//写进程内存 
int SetMemory(HANDLE processid,DWORD addr,DWORD value){
	LPVOID pbase=(LPVOID)addr; 
	LPCVOID buffer=(LPCVOID)&value; 
	SIZE_T res;
	return 	WriteProcessMemory(processid,pbase,buffer,sizeof(DWORD),&res);
}

对收集状态进行修改

我们在上一篇已经确定阳光的"收集状态"的地址为:[[[6A9EC0]+00000768]+E4]+50,将该地址的内容锁定为1时,阳光会被自动收集。

//自动收集阳光
void AutomaticCollection(HANDLE processH){
	DWORD addr=GetMemory(processH,0x006A9EC0); // 即addr=[6A9EC0]
	addr=GetMemory(processH,addr+0x00000768);  // 即addr=[[6A9EC0]+00000768]
	addr=GetMemory(processH,addr+0xE4);        // 即addr=[[[6A9EC0]+00000768]+E4]
	DWORD sun=addr+0x50;                       // 即sun=[[[6A9EC0]+00000768]+E4]+50
	if(GetMemory(processH,sun)==0)             // 如果该地址是0
		SetMemory(processH,sun,1);             // 就把它改为1

}

随后将上述代码循环执行即可实现自动收集。

运行测试

虽然现在已经实现自动收集了,但是在该系列的第一篇中也说了,目测的自动收集存在两个问题
1.无差别收集会自动收集通关奖杯,使游戏卡住
2.存在一写阳光阳光没被自动收集
针对这两个问题的分析和解决方案我将在下一篇给出,敬请期待。

你可能感兴趣的:(自制工具)