问题描述
我正在尝试编写一个进程内存扫描器来扫描内存中的非零值:
MEMORY_BASIC_informatION mbi;
char* addr = 0;
dtype readVal = 0;
while (VirtualQueryEx(hProc,addr,&mbi,sizeof(mbi))) { // HANDLE hProc is defined earlier
if (mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS) {
for (int i = 0; i < mbi.RegionSize; i += sizeof(readVal)) {
BOOL ret = ReadProcessMemory(hProc,(char*) mbi.BaseAddress + i,&readVal,sizeof(readVal),0);
printf("ReadProcessMemory returns %d\n",ret); // returns 5 (ERROR_ACCESS_DENIED)
if (readVal != 0) {
printf("Found a good value!");
system("pause");
}
}
}
addr += mbi.RegionSize;
}
我在 chrome.exe
上尝试过,扫描仪发现了大量非零值。但是当我在带有反作弊功能的游戏进程上尝试它时,它无法工作,ReadProcessMemory
在每次调用 ERROR_ACCESS_DENIED
时都返回 ReadProcessMemory
。句柄 hProc
附加到游戏进程没有问题,并且找到了正确的 PID。为什么会这样?我们如何才能增加绕过反作弊的特权?
解决方法
根据这个thread,你需要enumerate all modules for a process来获取你感兴趣的模块的有效句柄。然后你可以像这样以管理员身份运行ReadProcessMemory
来阅读多级指针:
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <tchar.h>
using namespace std;
DWORD_PTR dwGetModuleBaseAddress(DWORD dwProcID,TCHAR *szModuleName);
int main()
{
HWND hwnd = FindWindowA(NULL,"Game");
if (hwnd == NULL)
{
cout << "Cannot find window." << endl;
Sleep(3000);
exit(-1);
}
else
{
DWORD procID;
GetWindowThreadProcessId(hwnd,&procID);
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,procID);
DWORD_PTR ModuleBaseAddress = 0;
ModuleBaseAddress = dwGetModuleBaseAddress(procID,_T("Game.exe"));
if (procID == NULL)
{
cout << "Cannot find process.";
Sleep(3000);
exit(-1);
}
else {
while (true)
{
DWORD temp;
ReadProcessMemory(handle,(LPCVOID)(ModuleBaseAddress + 0x43021C),&temp,sizeof(temp),NULL);
ReadProcessMemory(handle,(LPCVOID)(temp + 0xAC),NULL);
int curhp;
ReadProcessMemory(handle,(LPCVOID)(temp + 0x4C),&curhp,sizeof(curhp),NULL);
cout << "Current HP: " << curhp << endl;
Sleep(100);
system("CLS");
}
}
}
system("PAUSE");
}
DWORD_PTR dwGetModuleBaseAddress(DWORD dwProcID,TCHAR *szModuleName)
{
DWORD_PTR dwModuleBaseAddress = 0;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,dwProcID);
if (hSnapshot != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 ModuleEntry32;
ModuleEntry32.dwSize = sizeof(MODULEENTRY32);
if (Module32First(hSnapshot,&ModuleEntry32))
{
do
{
if (_tcsicmp(ModuleEntry32.szModule,szModuleName) == 0)
{
dwModuleBaseAddress = (DWORD_PTR)ModuleEntry32.modBaseAddr;
break;
}
} while (Module32Next(hSnapshot,&ModuleEntry32));
}
CloseHandle(hSnapshot);
}
return dwModuleBaseAddress;
}
更多详情,可上网查询。
,您的代码的精简版本大致如下:
dtype readVal;
if (readVal != 0) {
printf("Found a good value!");
system("pause");
}
有一个(可能)未初始化的值 (readVal
),一些代码的代码路径不会改变该值,然后是读取访问。这样做没有特定的行为。因此,它的行为被暗示为未定义。
要修复您的代码,您需要进行两项更改:
-
readVal
需要初始化,并在最内层循环中重置。虽然不知道readVal
是什么数据类型,但不可能知道它是否被初始化。这留作练习。 -
ReadProcessMemory 可能会失败。它通过返回
FALSE
来报告失败。您需要检查是否失败,如果需要扩展错误信息,还可以选择调用 GetLastError。
为什么会这样,我们如何绕过反作弊?
我们不知道,因为您决定对重要信息保密(例如目标的具体名称和版本)。绕过软件保护是很困难的。在二进制开发中花费十年时间,您可能已经准备好想出一个办法。 Stack Overflow 无法为您提供帮助。