【问题标题】:ReadProcessMemory reads all 0s in a process memoryReadProcessMemory 读取进程内存中的所有 0
【发布时间】:2023-03-12 14:50:01
【问题描述】:

我正在尝试编写一个进程内存扫描程序来扫描内存中的非零值:

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 在每次调用ReadProcessMemory 时都会返回ERROR_ACCESS_DENIED。句柄hProc 连接到游戏进程没有问题,并且找到了正确的 PID。为什么会这样?我们如何增加绕过反作弊的特权?

【问题讨论】:

  • 你只是问,因为你印象深刻,想自己做同样的事情,对吧?
  • readVal 未初始化,在评估readVal 之前,您没有检查ReadProcessMemory() 的返回值是否成功,因此使用readVal 的任何结果都是indeterminate.
  • 添加错误检查,让错误值告诉你问题所在
  • @RemyLebeau 假设 dtype 是一个 POD(它可能是),这不是问题。 ReadProcess 应该写入/初始化它,所以只要ReadProcess 成功,从readVal 读取就不是未定义的。
  • @sba 有问题的代码从不评估从特定内存位置读取是否安全。因此,结果总是未定义。代码有可能不会失败的事实并不意味着代码没有被破坏。正是由于 Remy 指出的原因,它被破坏了。

标签: c++ winapi readprocessmemory


【解决方案1】:

根据这个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), &temp, sizeof(temp), 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;
}

更多详情,您可以上网查询。

【讨论】:

    【解决方案2】:

    您的代码的精简版本大致执行以下操作:

    dtype readVal;
    if (readVal != 0) {
        printf("Found a good value!");
        system("pause");
    }
    

    有一个(可能)未初始化的值 (readVal),一些代码的代码路径不会改变该值,然后是读取访问权限。这样做没有指定的行为。因此,它的行为被暗示为未定义。

    要修复您的代码,您需要进行两项更改:

    • readVal 需要初始化,并在最里面的循环中重置。虽然不知道readVal 是什么数据类型,但不可能知道它是否已初始化。这留作练习。
    • ReadProcessMemory 可能会失败。它通过返回FALSE 来报告失败。您需要检查故障,如果您需要扩展错误信息,还可以选择致电GetLastError

    为什么会这样?我们如何绕过反作弊?

    我们不知道,因为您决定对重要信息保密(例如目标的具体名称和版本)。绕过软件保护被设计为很困难。在二进制开发中度过十年,您可能准备好想出一个方法。 Stack Overflow 无法为您提供帮助。

    【讨论】:

    • 我检查了ReadProcessMemory 的返回值,它在所有函数调用中给出了 5 (ERROR_ACCESS_DENIED)。我们如何增加访问页面的权限?
    • 这需要您首先了解如何实施保护。进程可以通过多种不同的方式保护自己免受其内存读取。对此没有通用的答案。您将不得不研究目标应用程序在做什么。学习如何作弊通常比学习如何玩好游戏要难很多
    猜你喜欢
    • 1970-01-01
    • 2016-09-19
    • 2021-01-30
    • 1970-01-01
    • 2012-05-09
    • 2020-08-29
    • 1970-01-01
    • 2016-03-31
    • 1970-01-01
    相关资源
    最近更新 更多