【问题标题】:C++ ReadProcessMemory receiving 998 / 3E6 ErrorC++ ReadProcessMemory 收到 998 / 3E6 错误
【发布时间】:2018-07-11 14:25:12
【问题描述】:

所以我正在尝试使用 ReadProcessMemory() 从正在运行的 exe 中读取内存,如下面提供的代码所示。 我经常遇到的唯一问题是我收到错误 3E6 / 998,这似乎是NOACCESS,但我找不到解决此问题的解决方案。 是的,我尝试在管理员模式下运行 exe,但没有成功...

#include <Windows.h>
#include <iostream>
#include <string>
#include <tlhelp32.h>
#include <Psapi.h>
#include <tchar.h>

using namespace std;

int id = NULL;
HANDLE hProcess = NULL;
int getPID(const string name);
bool setHandle(int id, HANDLE &out);
DWORD64 GetModule(const string name);

int main()
{
    bool success = false;
    id = getPID("sample.exe");
    string name = "SAMPLE";
    cout << "Process Name: " << name << endl;
    cout << "Process ID: " << id << endl;

    success = setHandle(id, hProcess);
    if (success)
    {
        cout << "Handle set..." << endl;
    }
    else if (!success) 
    {
        cout << "You need to have SOMETHING opened..." << endl;
        cout << "ERROR CODE: " << GetLastError() << endl;
        system("pause");
        return 1;
    }
    success = false;

    DWORD64 baseAddress = GetModule("sample.exe");
    DWORD64 ammo = 0x24ED13273A8;
    DWORD64 addr = baseAddress + ammo;

    cout << "Base Address: " << hex << uppercase << "0x" << baseAddress << endl;
    cout << "Ammo Address: " << hex << uppercase << "0x" << ammo << endl;
    cout << "Complete Address: " << hex << uppercase << "0x" << addr << endl;

    int buffer = 0;

    success = ReadProcessMemory(hProcess, (LPCVOID)addr, (LPVOID)&buffer, sizeof(&buffer), NULL);

    if (success) 
    {
        cout << "ReadProccess succeeded..." << endl;
        system("pause");
        return 0;
    }
    else if (!success) 
    {
        cout << "ERROR CODE: " << GetLastError() << endl;
        system("pause");
        return 1;
    }

    system("pause");
    return 0;

}

bool setHandle(int id, HANDLE &out)
{
    out = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);
    if (!out) return false;

    return true;
}

int getPID(const string name)
{
    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (!Process32First(snapshot, &entry)) return NULL;

    do
    {
        if (strcmp(entry.szExeFile, name.c_str()) == 0)
        {
            CloseHandle(snapshot);
            return entry.th32ProcessID;
        }
    } while (Process32Next(snapshot, &entry));

    CloseHandle(snapshot);
    return NULL;
}

DWORD64 GetModule(const string name)
{
    HMODULE hMods[1024];
    DWORD cbNeeded;

    if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
    {
        for (int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
        {
            TCHAR szModName[MAX_PATH];
            if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR)))
            {
                string modName = szModName;
                if (modName.find(name) != string::npos)
                {
                    return (DWORD64)hMods[i];
                }
            }
        }
    }
    return NULL;
}

我对 c++ 有点陌生...所以不好意思? :)

【问题讨论】:

  • 你打电话给GetLastError太晚了。 documentation 告诉你什么时候调用它。
  • 嗯,我什么时候应该调用它......因为我想得到错误,如果成功(ReadProcessMemory)给出了错误,因为成功是一个布尔值?
  • addr 错误(无效)。这里不清楚什么?更具体的0x24ED13273A8; 当然是无效的 RVA
  • 这不是问题所在。弹药地址已经包含基地址,所以我尝试获取地址空间之外的东西
  • 文档说调用GetLastError立即,在API的返回值表明它将返回一个有效值之后。中间没有代码。你没有这样做。

标签: c++ winapi readprocessmemory


【解决方案1】:

您的代码中实际上存在两个基本错误,不幸的是,对于您、我和文明世界的其他人来说,这两个错误都会生成相同的错误代码。是否曾经如此。还有一个逻辑错误,但你很幸运能够逃脱它(差不多)。我在下面发布的代码中评论了修复。

您的代码中还有一些“良好实践”的缺点,具体来说:

  • 不应使用NULL来表示整数
  • 应检查所有错误情况并(明智地!)报告
  • 您在两个不同的地方使用相同的字符串文字(因此,如果您更改它,则需要在两个地方都更改它,您可能会忘记)。所以不要那样做。
  • using namespace std; 被广泛反对(因为它会造成如此多的命名空间污染)
  • 为什么idhProcess 是全局变量?这完全没有必要。
  • 你应该给你的函数起更多描述性的名字,setHandle 是我特别想到的那个。我完全摆脱了那个。
  • std::string作为只读函数参数传递时,通常最好将其传递为const ref,这样就不需要复制了。
  • 仅当您真正想要刷新缓冲区时才使用std::endl。效率低下。
  • 之后进行清理(在这种情况下,关闭所有打开的句柄)。我知道这只是一个一次性程序,但这是一个养成的好习惯。

好的,这里有一些有效的代码(我已经发布了自己的代码,因为我清理了以上所有内容)。实质性变化是:

  • 要读取另一个进程的内存,您需要给您的用户令牌SE_DEBUG_NAME 权限。这反过来意味着您需要以管理员身份(也称为提升权限)运行您的程序。
  • 您不能(显然)读取目标进程中的无意义地址,所以我只是悄悄地修复了它。

就像我说的那样,这两者都会生成相同的错误代码。嗯!

好的,给你。享受:

#include <Windows.h>
#include <iostream>
#include <string>
#include <tlhelp32.h>
#include <Psapi.h>
#include <tchar.h>

int getPID(const std::string& name);
DWORD64 GetModule(HANDLE hProcess, const std::string& name);

// Stolen from: https://docs.microsoft.com/en-gb/windows/desktop/SecAuthZ/enabling-and-disabling-privileges-in-c--
BOOL SetPrivilege(
    HANDLE hToken,          // access token handle
    LPCTSTR lpszPrivilege,  // name of privilege to enable/disable
    BOOL bEnablePrivilege   // to enable or disable privilege
    ) 
{
    TOKEN_PRIVILEGES tp;
    LUID luid;

    if ( !LookupPrivilegeValue( 
            NULL,            // lookup privilege on local system
            lpszPrivilege,   // privilege to lookup 
            &luid ) )        // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if (bEnablePrivilege)
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.

    if ( !AdjustTokenPrivileges(
           hToken, 
           FALSE, 
           &tp, 
           sizeof(TOKEN_PRIVILEGES), 
           (PTOKEN_PRIVILEGES) NULL, 
           (PDWORD) NULL) )
    { 
          printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); 
          return FALSE; 
    } 

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)

    {
          printf("The token does not have the specified privilege. \n");
          return FALSE;
    } 

    return TRUE;
}


constexpr const char* theProcess = "notepad.exe";

int main()
{
    HANDLE hToken;
    BOOL ok = OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
    if (!ok)
    {
         std::cout << "OpenProcessToken failed, error " << GetLastError() << "\n";
         return 255;
    }

    ok = SetPrivilege (hToken, SE_DEBUG_NAME, TRUE);
    if (!ok)
    {
        CloseHandle (hToken);
        return 1;
    }

    int pid = getPID (theProcess);

    HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
    if (hProcess == NULL)
    {
         std::cout << "OpenProcess failed, error " << GetLastError() << "\n";
         CloseHandle (hToken);
         return 1;
    }

    DWORD64 baseAddress = GetModule(hProcess, theProcess);
    std::cout << "Base Address: " << std::hex << std::uppercase << "0x" << baseAddress << "\n";
    int buffer = 0;  // Note: sizeof (buffer) below, not sizeof (&buffer)
    ok = ReadProcessMemory(hProcess, (LPCVOID)baseAddress, (LPVOID)&buffer, sizeof(buffer), NULL);

    CloseHandle (hProcess);
    CloseHandle (hToken);

    if (ok) 
    {
        std::cout << "ReadProcessMemory succeeded, buffer = " << buffer << "\n";
        system("pause");
        return 0;
    }

    std::cout << "ReadProcessMemory failed, error " << GetLastError() << "\n";
    system("pause");
    return 1;
}

int getPID(const std::string& name)
{
    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (!Process32First(snapshot, &entry)) return NULL;

    do
    {
        if (strcmp(entry.szExeFile, name.c_str()) == 0)
        {
            CloseHandle(snapshot);
            return entry.th32ProcessID;
        }
    } while (Process32Next(snapshot, &entry));

    CloseHandle(snapshot);
    return NULL;
}

DWORD64 GetModule(HANDLE hProcess, const std::string& name)
{
    HMODULE hMods[1024];
    DWORD cbNeeded;

    if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
    {
        for (int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
        {
            TCHAR szModName[MAX_PATH];
            if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR)))
            {
                std::string modName = szModName;
                if (modName.find(name) != std::string::npos)
                {
                    return (DWORD64)hMods[i];
                }
            }
        }
    }
    return NULL;
}

输出(以管理员身份运行时):

Base Address: 0x7FF6D8470000
ReadProcessMemory succeeded, buffer = 905A4D

输出(以普通用户身份运行时):

The token does not have the specified privilege.

您也可以在GitHub 获取一些代码。

【讨论】:

  • 非常感谢,这绝对是我在这里找到的问题的最佳答案!
猜你喜欢
  • 1970-01-01
  • 2017-02-05
  • 2021-11-23
  • 2012-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多