保证模拟游戏中小老鼠不被打死
- 血量不减
- 判断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 -> -0
if(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->jmp
if(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;
}