C语言实现植物大战僵尸自动收集阳光(三) 解决收集不全与收集奖杯卡死的问题

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

前言

上一篇中,我们已经用C语言通过对进程内存的读者操作,达到了自动收集的目的,但是还有两个问题尚未解决:

  1. 无差别收集会自动收集通关奖杯,使游戏卡住
  2. 自动收集有时会落下一下阳光

本篇将着重分析和解决这些问题。

前置内容

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

关于无差别收集的分析与解决思路

问题分析

当前软件会自动收集阳光、金币、甚至游戏的奖杯,我们可以把这些物品归结为“掉落物品”,也就是说当我们把某一内存锁定为1时,会自动收集掉落物品,我们观察到,当收集阳光时,阳光会飞往左上角,而收集金币时,金币飞往左下角,也就是说对于“掉落物品”而言,一定存在某一个变量,存储着掉落物品的类型,是阳光,还是金币,当收集状态置为1时,掉落物品会根据自身类型执行不同的操作,所以想解决无差别收集问题,我们就需要找到这个存在着掉落物品类型的内存。
我们用面向对象的思想假设制作者的思路,将掉落物品抽象为一个“掉落物品类”,收集状态和物品类型,都是这个类的私有变量,每一个掉落物品都是这个类的一个实例,在内存中一个实例的储存地址应该是连续的,所以我们只需要在收集状态的附近进行查找即可。

CE查找掉落物品类型地址
  1. 我们不知道一个实例的具体大小,依靠猜测,现在地址左右128个地址范围内查找,如果找不到,就找256个地址范围。
  2. 在图中种下一颗金银花,当阳光落下时,扫描地址,不断搜索未变化的值,直到稳定,出现金币时,扫描变化的值,搜索变化的值,之后搜索不变的值,直到稳定。重复这个过程,最终可以锁定几个可疑的地址,我们选取与收集状态较近的地址开始尝试,改变其中数值观察游戏变化规律。

最后我们找到地址[[[6A9EC0]+00000768]+E4]+58,当给他赋值为1时,点击阳光,此时阳光已经变为银币,赋值为2,点击阳光,阳光变为了金币,3是钻石,456都是太阳,7太阳消失,8是奖杯,9是铲子,如此我们基本确定这就是所求内存,我们只需要自动收集阳光和金币,所以我们可以在程序中新添代码,保证在每次给“收集状态”赋值为1之前,先判断“物品类型”,若类型<7则更改收集状态,否则跳过。

关于自动收集不完全的问题

问题分析与解决思路

我们通过分析,假设了“掉落物品”类的存在,并认为每一个掉落物品均是该类的一个实例。无差别收集的问题解决,基本可以证明该假设的合理性,已知每一个实例都被分配了一段内存,且“收集状态”只是一个实例的一个私有字段,显然, 一个“收集状态”只能对应场上的一个阳光,当场上同时存在2及2个阳光以上时,只有第一个阳光会被自动收集,第二个阳光会被落下 。同时这也解释了,为什么有时候我们找不到自动收集阳光的内存地址,如果我们要找的话,我们应该保证在场只有一个掉落物品,且该物品掉落时,场上没有别的掉落物品,根据开发经验推测所有掉落物品的实例应当存在于一个按掉落时间排序的线性表内,所以第一个掉落物品的“收集状态”、第二个掉落物品“收集状态”、第三个掉落物品的“收集状态”、第n个掉落物品的“收集状态”所在的地址应该存在某种联系。

CE确定实例大小
  1. 种两个向日葵,将第一个掉落物品的收集状态地址锁死为0(即永不会被收集),参考第一篇的方法,不断查找0和1,最终确定第二个掉落物品的收集状态地址
  2. 将得到的地址与之前地址相减,差为D8,将第二个掉落物品锁为0,用第二个地址+D8算出第三个地址
  3. 将第三个地址锁为1,发现只有第三个掉落的阳光会被自动收集,第一个阳光与第二个阳光无法收集

如此基本可以断定,掉落物品的实例在一个数组实现的线性表中,

第n个掉落物品的收集状态所在地址为:

[[[6A9EC0]+00000768]+E4]+(n-1)*D8+50

第n个掉落物品的物品类型所在地址为:

[[[6A9EC0]+00000768]+E4]+(n-1)*D8+58

完整代码

结合上述信息,对代码进行修改,随后用多线程给这个控制台程序加了一个简单的交互界面,同时输入输出改用C++的cin和cout了,最后得到下面的代码:

#include <iostream>
#include <Windows.h>
#include <string>
#include <pthread.h>

using namespace std;


//退出标识 
int quit=0;

//通过窗口名获取进程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){
	while(!quit||processH==0) {
		DWORD addr=GetMemory(processH,0x006A9EC0);
		addr=GetMemory(processH,addr+0x00000768);
		
		//钱币数目常显,女票特殊要求,要求能时刻看见自己的小钱钱数目(服
		SetMemory(processH,addr+0x000055F4,600);
		
		addr=GetMemory(processH,addr+0xE4);
		
		int Numb = 50;
		for(int i=0;i<Numb;i++) {
			DWORD object= addr+0x50+i*0xD8;
			DWORD objectType=addr+0x58+i*0xD8;
			if(GetMemory(processH,object)==0&&GetMemory(processH,objectType)<7)
				SetMemory(processH,object,1);
		} 
		Sleep(500);
	} 
}
void* OrderListener(void* some){
	char order;
	system("cls");
	cout<<"收集功能已开启,输入q退出程序:"; 
	while(cin>>order){
		if(order=='q'){
			quit=1; 
		}
	}
	
} 

int main(int argc, char** argv) {
	pthread_t pthd;
	char response; 
	cout<<"输入y开启收集功能,输入q退出程序:" ; 
	while(cin>>response){
		if(response=='y'){
			HANDLE processH=GetProcessH("植物大战僵尸中文版");
			if(processH==0){
				system("cls");
				cout<<"错误:请先开启游戏" <<endl;
				cout<<"输入y开启收集功能,输入q退出程序:" ; 
				continue; 
			} 
			pthread_create(&pthd, NULL, &OrderListener, NULL);
			AutomaticCollection(processH); 
			break; 
		}else if(response=='q'){
			return 0;
		}else{
			system("cls");
			cout<<"输入y开启收集功能,输入q退出程序:" ; 
		}
	}
}

可以看到,此时玩冒险模式已经不会因为自动收集奖杯而卡关了,任务完成√,之后的事就是用MFC或者QT加个界面即可,此处不赘述了。在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值