首页 > 解决方案 > C++ 仅读取进程范围内的内存地址

问题描述

所以我一直在尝试用 C++ 进行内存读取,我认为一个很酷的项目是读取进程正在使用的所有地址(类似于 Cheat Engine 的工作方式)。

我从阅读开始

Link1: 读取进程 C++ 的内存

Link2: 读取64bit进程地址的内存

链接3: http ://www.cplusplus.com/forum/general/42132/

我还在 youtube 上观看了一个教程,他解释了一个过程(游戏)如何处理地址。链接到 youtube 视频: https ://www.youtube.com/watch?v=wiX5LmdD5yk

这导致我采用了三种不同的方法:

DWORD GetProcId(const wchar_t* procName) {
    DWORD pid = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(hSnap != INVALID_HANDLE_VALUE) {
        PROCESSENTRY32 procEntry;
        procEntry.dwSize = sizeof(procEntry);
        if(Process32First(hSnap, &procEntry)) {
            do {
                if(!_wcsicmp(procEntry.szExeFile, procName)) {
                    pid = procEntry.th32ProcessID;
                    break;
                }
            } while (Process32Next(hSnap, &procEntry));
        }
    }
    CloseHandle(hSnap);
    return pid;
}

这种方法是获取进程ID,我也可以通过在任务管理器中找到相同的PID(稍后给我相同的基地址)来手动输入它。

uintptr_t GetModuleBaseAddress(DWORD procId, const wchar_t* modName) {
    uintptr_t modBaseAddr = 0;
//I use 0x10 instead of TH32CS_SNAPMODULE32 since it didnt work and according to documentation 
// this is the value it should have.
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | 0x10, procId); 
    if(hSnap != INVALID_HANDLE_VALUE) {
        MODULEENTRY32 modEntry;
        modEntry.dwSize = sizeof(modEntry);
        if(Module32First(hSnap, &modEntry)) {
            do {
                if(!_wcsicmp(modEntry.szModule, modName)) {
                    modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
                    break;
                }
            } while(Module32Next(hSnap, &modEntry));
        }
    }
    CloseHandle(hSnap);
    return modBaseAddr;
}

这个方法(我假设)将返回进程的基地址。例如,在我的代码中,我试图找到 discord.exe 进程的基地址。当 discord.exe 没有运行时,我得到了 0,而当它运行时,我得到了一个地址(我相信这是正确的基地址,如果我错了,请纠正我)。

还有我的main方法:

int main() {
    DWORD procId = GetProcId(L"Discord.exe");
    uintptr_t moduleBase = GetModuleBaseAddress(procId, L"Discord.exe");

    HANDLE hProcess = 0;
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, procId);

    uintptr_t dynamicPtrBaseAddr = moduleBase;

    std::cout << "Dynamic: " << dynamicPtrBaseAddr << std::endl;

    int value = 0;
    int arr [10000] = {};
    for (int i = 0; i < 100000; i++) {
        ReadProcessMemory(hProcess, (BYTE*)dynamicPtrBaseAddr, &value, sizeof(value),0);
        dynamicPtrBaseAddr += 1;
        arr[i] = value;
    }
}

我尝试将所有 100000 个地址的所有值放入一个数组中。

所以我的问题是:

g++ main.cpp -o test -lpsapi -DUNICODE我用(MinGW)编译。

标签: c++winapicheat-engine

解决方案


您必须以管理员身份运行,并且必须编译为与目标进程相同的位数。

您不应该一次读取 1 个字节,尤其是在外部 hack 中。

您应该使用 VirtualQueryEx() 正确循环仅正确的内存区域:

    DWORD procid = GetProcId("ac_client.exe");

    unsigned char* addr = 0;

    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procid);

    MEMORY_BASIC_INFORMATION mbi;

    while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)))
    {
        if (mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS && mbi.Protect != PAGE_GUARD)
        {
            std::cout << "base : 0x" << std::hex << mbi.BaseAddress << " end : 0x" << std::hex << (uintptr_t)mbi.BaseAddress + mbi.RegionSize << "\n";
        }
        addr += mbi.RegionSize;
    }

这可以确保您只读取正确的内存。接下来,您将一次性将每个区域到达本地缓冲区,这将显着提高速度,因为您不会有为每个字节调用 API 的开销。

您看起来正在尝试做的是模式扫描。您可以在找到您所遵循的原始教程的同一位置找到我们的模式扫描教程。


推荐阅读