保证模拟游戏中小老鼠不被打死
- 血量不减
- 判断die跳过
- 血量不变
知识点
- 获得进程PID的方法
- 方法一:CreateToolhelp32Snapshot、Process32First、Process32Next、CloseHandle
- 方法二:FindWindow、GetWindowThreadProcessId
- 获取进程句柄
- OpenProcess
- 搜索要patch的位置
- SearchMemory(自定义):一块一块的查找
- EnumProcessModules:获得指定进程中所有模块的句柄
- VirtualQueryEx:查询地址空间中内存地址的信息(保护属性等)
- ReadProcessMemory:读取内存
- CloseHandle
- SearchMemory(自定义):一块一块的查找
- 读取memory
- ReadProcessMemory
- 写入memory
- WriteProcessMemory
- 获得进程PID的方法
模块(exe或者dll)被加载后,其开始地址就是该模块的句柄值,通常应用程序都是通过模块句柄来访问它的每个进程中的模块(一个应用程序可能启动多个进程),事实上模块句柄的值就是该模块映射到进程中的地址。进程是系统中运行的应用程序,进程句柄中储存用于访问该进程的一些信息,句柄值是一个索引值,通过该索引可以访问句柄中的内容
#include <stdio.h>#include <stdlib.h>#include <windows.h>#include <tlhelp32.h>#include <tchar.h>#include <psapi.h>#include<stdio.h>#include<malloc.h>#include <array>BYTE* SearchMemory(HANDLE hProcess, DWORD pSearchBuffer1, DWORD pSearchBuffer2,DWORD dwSearchBufferSize){// 根据PID, 打开进程获取进程句柄//HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);//if (NULL == hProcess)//{// ShowError("OpenProcess");// return FALSE;//}// 获取进程加载基址HMODULE hModule[1024];DWORD cbNeeded;EnumProcessModules(hProcess, (HMODULE*)hModule, sizeof(hModule), &cbNeeded);// 把加载基址作为遍历内存的起始地址, 开始遍历BYTE* pSearchAddress2 = (BYTE * )hModule[0];//从加载的第一个模块开始printf("%x\n", pSearchAddress2);BYTE* pSearchAddress = (BYTE*)0x00010000;MEMORY_BASIC_INFORMATION mbi = { 0 };DWORD dwRet = 0;BOOL bRet = FALSE;DWORD* pTemp = NULL;DWORD i = 0;DWORD* pBuf = NULL;while (pSearchAddress >= (BYTE*)0 && pSearchAddress <= (BYTE*)pSearchAddress2 && mbi.RegionSize >= 0){// 查询地址空间中内存地址的信息::RtlZeroMemory(&mbi, sizeof(mbi));dwRet = ::VirtualQueryEx(hProcess, (LPVOID)pSearchAddress, &mbi, sizeof(mbi));//printf("%d\n", dwRet);if (0 == dwRet){//printf("hhhh");break;}// printf("0x%p\n", pSearchAddress);// 过滤内存空间, 根据内存的状态和保护属性进行过滤if ((MEM_COMMIT == mbi.State) &&(PAGE_READONLY == mbi.Protect || PAGE_READWRITE == mbi.Protect ||PAGE_EXECUTE_READ == mbi.Protect || PAGE_EXECUTE_READWRITE == mbi.Protect)){// printf("find pages\n");// 申请动态内存//pBuf = new DWORD[mbi.RegionSize / 4];byte* FindArray = new byte[mbi.RegionSize];DWORD dwData;DWORD dwData2;// printf("RegionSize: %d\n", mbi.RegionSize);::RtlZeroMemory(FindArray, mbi.RegionSize);// 读取整块内存if (ReadProcessMemory(hProcess, (LPVOID)pSearchAddress, FindArray, mbi.RegionSize, &dwRet)){// printf("read success\n");int len = 8;byte* intBuff = new byte[len];for (int i = 0; i < mbi.RegionSize - dwSearchBufferSize; i++){int tmp = i;for (int j = 0;j < len;j++){intBuff[j] = FindArray[tmp];tmp += 1;}dwData = intBuff[0];for (int j = 1;j < len/2;j++){dwData= dwData | intBuff[j] << len *j;}dwData2 = intBuff[4];for (int j = 5;j < len;j++){dwData2 = dwData2 | intBuff[j] << len * (j-4);}if (dwData == (DWORD)pSearchBuffer1 && dwData2 == (DWORD)pSearchBuffer2) {return (BYTE*)mbi.BaseAddress + i;}}}// 释放内存delete[]pBuf;pBuf = NULL;}// 继续对下一块内存区域进行遍历pSearchAddress = pSearchAddress + mbi.RegionSize;}// 释放内存, 关闭句柄if (pBuf){delete[]pBuf;pBuf = NULL;}::CloseHandle(hProcess);return 0;}int main(){//获取进程PID_方法一// int pid=0;// PROCESSENTRY32 processentry={0}; //创建一个进程结构体// processentry.dwSize=sizeof(PROCESSENTRY32);// HANDLE hprocessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); //获取进程快照// if(hprocessSnap==INVALID_HANDLE_VALUE){// return -1;// }// int flag=Process32First(hprocessSnap,&processentry); //获取第一个进程// while(flag){// if(lstrcmpi(processentry.szExeFile,"Afkayas.exe")==0){ //lstrcmpi函数用于比较两个字符串,相同时返回0// pid=processentry.th32ProcessID;// }// // printf("%d----%s\n",processentry.th32ProcessID,processentry.szExeFile);// flag=Process32Next(hprocessSnap,&processentry);// }// CloseHandle(hprocessSnap);// if(pid==0){// printf("请先打开进程");// return 0;// }//获取进程PID_方法二HWND hWnd = FindWindow(NULL, _T("MemSearchTest"));if (hWnd == NULL) { //如果无法获取句柄则报错printf("无法获取进程句柄,请检查进程是否存在!");getchar();return -1;}//GetWindowThreadProcessId获取进程ID的变量类型是DWORD,但是GetWindowThreadProcessId在MSDN中并没有明确表示返回值得成功与失败,所以我们定义一个DWORD类型变量赋值为0,在调用GetWindowThreadProcessId之后看一下ID是否还为0如果为0则失败,一般来说不会失败!DWORD Process_ID = 0;GetWindowThreadProcessId(hWnd, &Process_ID); //PID这里传址printf("PID:%d\n", Process_ID);if (Process_ID == 0) { //判断是否与原值相同printf("无法获取进程ID");}//获取进程句柄HANDLE procHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Process_ID);if (procHandle == NULL) {printf("打开进程失败!");return 0;}// 搜索要替换的位置BYTE* address=SearchMemory(procHandle, 0x0000005A, 0x0000425C,8);int blood;int ord;DWORD buffer;/* 永恒方法一// 运算失效: -10 -> -0if(ReadProcessMemory(procHandle,(LPCVOID)0x00400000,&tmp,1,&buffer)){printf("读取成功,读取内容为:%#X\n",tmp);}else{printf("读取进程内容失败!");exit(-1);}tmp=0x00;if(WriteProcessMemory(procHandle,(LPVOID)0x00401E2D,&tmp,1,&buffer)){printf("写入进程内容成功\n");}else{printf("写入进程内容失败!\n");exit(-1);}if(ReadProcessMemory(procHandle,(LPCVOID)0x00401E2D,&tmp,1,&buffer)){printf("读取成功,读取内容为:%#X\n",tmp);}else{printf("读取进程内容失败!");exit(-1);}*//* 永恒方法二*/// 血量不变while(TRUE){blood=0x64;if (!ReadProcessMemory(procHandle, (LPCVOID)address, &ord, 1, &buffer)) {printf("读取进程内容失败!");exit(-1);}if (ord != blood){if (!WriteProcessMemory(procHandle, (LPVOID)address, &blood, 1, &buffer)) {printf("写入进程内容失败!\n");exit(-1);}}//PostMessage(hWnd, WM_PAINT, 0,0);}/*永恒方法三//判断失效if(ReadProcessMemory(procHandle,(LPCVOID)0x00401E36,&tmp,1,&buffer)){printf("读取成功,读取内容为:%#X\n",tmp);}else{printf("读取进程内容失败!");exit(-1);}tmp=0xEB;//jg->jmpif(WriteProcessMemory(procHandle,(LPVOID)0x00401E36,&tmp,1,&buffer)){printf("写入进程内容成功\n");}else{printf("写入进程内容失败!\n");exit(-1);}if(ReadProcessMemory(procHandle,(LPCVOID)0x00401E36,&tmp,1,&buffer)){printf("读取成功,读取内容为:%#X\n",tmp);}else{printf("读取进程内容失败!");exit(-1);}*/return 0;}
