C++ ReadProcessMemory receiving 998 / 3E6 Error

拥有回忆 提交于 2020-02-02 16:09:18

问题


So I'm trying to read Memory out of a running exe with ReadProcessMemory() as you can see in the code provided below. The only problem I constantly run into is that I receive the Error 3E6 / 998 which seems to be NOACCESS but I cant find a solution to fix this. And yes I tried to run the exe in Admin Mode without success...

#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;
}

I'm kinda new to c++... so sry? :)


回答1:


There are actually two basic mistakes in your code, both of which, unfortunately for you, me and the rest of the civilised world, generate the same error code. Was it ever thus. There is also a logic error, but you are lucky enough to be getting away with it (just about). I commented the fix in the code I posted below.

There are also a number of 'good practise' shortcomings in your code, specifically:

  • NULL should not be used to represent integer zero
  • All error conditions should be checked for and (sensibly!) reported
  • You use the same string literal in two different places (so if you ever change it, you would need to change it in both places, and you might forget). So don't do that.
  • using namespace std; is widely frowned upon (because it causes such a lot of namespace pollution)
  • Why are id and hProcess global variables? This is just plain unnecessary.
  • You should give your functions more descriptive names, setHandle being the one I particularly have in mind. I got rid of that one completely.
  • When passing a std::string as a read-only function parameter, it is usually best to pass it as const ref, then it doesn't need to be copied.
  • Only use std::endl when you actually want to flush the buffer. It is inefficient.
  • Clean up after you (in this case, close any open handles). I know this is just a throwaway program but it's a good habit to get into.

OK, so here's some code that works (I have posted my own because I cleaned up all of the above). The substantive changes are:

  • To read the memory of another process, you need to give your user token the SE_DEBUG_NAME privilege. This in turn means you need to run your program as Administrator (aka elevated).
  • You cannot (obviously) read from a nonsense address in the target process so I just quietly fixed that.

Like I say, both of these generate the same error code. Huh!

OK, here you go. Enjoy:

#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;
}

Output (when run as Administrator):

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

Output (when run as a normal user):

The token does not have the specified privilege.

You can also grab some code over at GitHub.



来源:https://stackoverflow.com/questions/51288022/c-readprocessmemory-receiving-998-3e6-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!