C语言实现植物大战僵尸自动收集阳光(一) 问题分析与寻找基址
C语言实现植物大战僵尸自动收集阳光(二) C语言控制台程序的实现
C语言实现植物大战僵尸自动收集阳光(三) 解决收集不全与收集奖杯卡死的问题
上一篇中,我们已经用C语言通过对进程内存的读者操作,达到了自动收集的目的,但是还有两个问题尚未解决:
本篇将着重分析和解决这些问题。
C语言实现植物大战僵尸自动收集阳光(一) 问题分析与锁定基址
C语言实现植物大战僵尸自动收集阳光(二) C语言控制台程序的实现
当前软件会自动收集阳光、金币、甚至游戏的奖杯,我们可以把这些物品归结为“掉落物品”,也就是说当我们把某一内存锁定为1时,会自动收集掉落物品,我们观察到,当收集阳光时,阳光会飞往左上角,而收集金币时,金币飞往左下角,也就是说对于“掉落物品”而言,一定存在某一个变量,存储着掉落物品的类型,是阳光,还是金币,当收集状态置为1时,掉落物品会根据自身类型执行不同的操作,所以想解决无差别收集问题,我们就需要找到这个存在着掉落物品类型的内存。
我们用面向对象的思想假设制作者的思路,将掉落物品抽象为一个“掉落物品类”,收集状态和物品类型,都是这个类的私有变量,每一个掉落物品都是这个类的一个实例,在内存中一个实例的储存地址应该是连续的,所以我们只需要在收集状态的附近进行查找即可。
最后我们找到地址[[[6A9EC0]+00000768]+E4]+58
,当给他赋值为1时,点击阳光,此时阳光已经变为银币,赋值为2,点击阳光,阳光变为了金币,3是钻石,456都是太阳,7太阳消失,8是奖杯,9是铲子,如此我们基本确定这就是所求内存,我们只需要自动收集阳光和金币,所以我们可以在程序中新添代码,保证在每次给“收集状态”赋值为1之前,先判断“物品类型”,若类型<7则更改收集状态,否则跳过。
我们通过分析,假设了“掉落物品”类的存在,并认为每一个掉落物品均是该类的一个实例。无差别收集的问题解决,基本可以证明该假设的合理性,已知每一个实例都被分配了一段内存,且“收集状态”只是一个实例的一个私有字段,显然, 一个“收集状态”只能对应场上的一个阳光,当场上同时存在2及2个阳光以上时,只有第一个阳光会被自动收集,第二个阳光会被落下 。同时这也解释了,为什么有时候我们找不到自动收集阳光的内存地址,如果我们要找的话,我们应该保证在场只有一个掉落物品,且该物品掉落时,场上没有别的掉落物品,根据开发经验推测所有掉落物品的实例应当存在于一个按掉落时间排序的线性表内,所以第一个掉落物品的“收集状态”、第二个掉落物品“收集状态”、第三个掉落物品的“收集状态”、第n个掉落物品的“收集状态”所在的地址应该存在某种联系。
如此基本可以断定,掉落物品的实例在一个数组实现的线性表中,
第n个掉落物品的收集状态所在地址为:
[[[6A9EC0]+00000768]+E4]+(n-1)*D8+50
第n个掉落物品的物品类型所在地址为:
[[[6A9EC0]+00000768]+E4]+(n-1)*D8+58
结合上述信息,对代码进行修改,随后用多线程给这个控制台程序加了一个简单的交互界面,同时输入输出改用C++的cin和cout了,最后得到下面的代码:
#include
#include
#include
#include
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加个界面即可,此处不赘述了。