ReadProcessMemory 的缓冲区应该是什么数据类型?

What data type should the buffer be for ReadProcessMemory?

在所有示例中,我都看到了第三个参数或 buffer 是一个整数,但我不太确定这个值应该代表什么,或者它有什么用,所以我尝试了char 数组并得到随机垃圾所以我很好奇这个值可以用于什么以及放入它的最佳容器以及数据类型是否取决于我们正在查询的值。

查看我的代码后,我意识到我犯了一个非常愚蠢的错误,即在声明过程句柄时没有初始化它。这是我的代码,现在我得到 ERROR_PARTIAL_COPY。并且 dwRead 为 0。wtf

#include <iostream>
#include <windows.h>
using namespace std;

int main()
{
    system("tasklist");

    SYSTEM_INFO SysInfo;
    GetSystemInfo(&SysInfo);

    DWORD proc_Id = 0;
    cin >> proc_Id;
    HANDLE hProc = INVALID_HANDLE_VALUE;
    char value[500];
    SIZE_T dwRead;

    hProc = OpenProcess(PROCESS_ALL_ACCESS, TRUE, proc_Id);
    if(hProc == NULL)
    {
        cout << "Error when trying to retrieve process handle" << endl;
    }

    void *baseAddr = (void*)hProc;

    if(VirtualAllocEx(hProc, NULL, SysInfo.dwPageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READ) == NULL)
    {
        cout << "VirtualAllocEx error" << endl;
    }

    if(ReadProcessMemory(hProc, baseAddr, &value, sizeof(value), &dwRead) == 0)
    {
        cout << "ReadProcessMemory failed: " << GetLastError() << endl;
    }
    cout << "Value is: " << value << endl;
    cout << "Amount read successfully: " << dwRead << endl;
}
BOOL WINAPI ReadProcessMemory(
    HANDLE hProcess,
    LPCVOID lpBaseAddress,
    LPVOID lpBuffer,
    SIZE_T nSize,
    SIZE_T *lpNumberOfBytesRead
);

参数声明为LPVOIDtypedef 代表void*)。任何指针都可以隐式转换为指向 void 的指针——这意味着您提供的缓冲区实际上可以是您喜欢的任何类型。唯一的限制是它必须足够大以容纳 nSize 字节的数据。

SIZE_T dwRead;
int iValue;
if (ReadProcessMemory(hProcess, lpAddress, &iValue, sizeof(iValue), &dwRead)) // read an int
{
    if (dwRead == sizeof(iValue))
    {
        // got int successfully
    }
}

char buf[256];
if (ReadProcessMemory(hProcess, lpAddress, buf, sizeof(buf), &dwRead))  // read 256 chars
{
    // got dwRead bytes successfully
}

如果您在 returned 缓冲区中收到垃圾,这可能是因为读取失败,或者没有读取您请求的那么多数据,并且您没有检查 return 值适当。如果函数成功,lpNumberOfBytesRead 参数可让您找出成功读取了多少字节。

如果函数 returns 0 这意味着它完全失败了 - 在这种情况下,您提供的缓冲区的内容未定义,不应使用。

您应该使用函数要您使用的数据类型。

https://msdn.microsoft.com/en-us/library/windows/desktop/ms680553(v=vs.85).aspx

至于第三个参数,你可以使用一个指向任何你喜欢的数据的指针,只要你相应地调整第四个参数。

例如将指向 int 的指针作为第三个参数传递,将 sizeof(int) 作为第四个参数传递可能完全没问题。

我建议将您传递的数据初始化为零(至少出于调试原因)并检查函数返回的所有数据(尤其是您作为第 5 个变量传递的指针的变量值。)

哪种数据类型真正适合您的用例取决于您。

如果您使用 char 数组,这将是一种方法:

#include <iostream>
#include <Windows.h>


int main()
{
    HANDLE hProcess = NULL;     //to be initialized
    LPCVOID lpBaseAddress = NULL;  //to be initialized

    char buffer[256];
    SIZE_T bufferSize = sizeof(buffer);
    SIZE_T bytesRead = 0;
    if (::ReadProcessMemory(hProcess, lpBaseAddress, buffer, bufferSize, &bytesRead))  // read 256 chars
    {
        std::cout << "ReadProcessMemory succeeded by reading " << bytesRead << " bytes";
        //Do something with the buffer....
    }
    else
    {
        char messageBuffer[256];
        DWORD messageBufferSize = sizeof(messageBuffer);
        DWORD lastError = ::GetLastError();
        DWORD realMessageSize = ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
                                        NULL,
                                        lastError,
                                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                        messageBuffer,
                                        messageBufferSize,
                                        NULL);

        //If FormatMessageA fails it returns 0. So we make sure that the string is empty
        messageBuffer[realMessageSize] = 0;

        std::cerr << "ReadProcessMemory failed with " << lastError << ": " << messageBuffer;
    }
}

因为 "David Heffernan" 指出不清楚我用零初始化缓冲区的意思我将尝试解释:

这意味着 "hint" 用于调试,因为我认为与初始化数据相比,在调试器中查看哪些数据发生了变化更容易。 绝对不需要

假设您的 ReadProcessMemory 调用成功返回并且您想检查缓冲区中的数据。 您需要先检查有多少字节写入缓冲区,然后查看数组的前 bytesWritten 个字节。

如果您作为缓冲区传递的数据未使用 0 初始化,则可能更难找到 "real data" 与未初始化数据之间的中断 - 因此可能会导致误解。特别是对于大错误。

这只是作为开发过程中使用的某种工具,以便更轻松地检查数据,应该在一切按预期工作后删除。