1

i wanted to try to build a little program which can read the memory of other programs and dump it into a text file(if it has access obviously). But the access seems to be a problem right beforehand to me. I first tried a bit around and wanted to print a list of all processes currently running but apparently i haven't even access to open some of the processes. However if i open the list in a program like Cheat Engine, it shows all process names (the system process for example mainly PID 4 if i have seen correctly). Now have i just messed up the desired access level while opening the process, or does Cheat Engine just use some tricks or reads the names from somewhere else? I tried both QueryFullProcessImageName and GetBaseModuleName where the latter requires PROCESS_VM_READ access thats why i used QueryFullProcessImageName because i tried to minimize my access level.

main.cpp

#include <cstring>
#include <iostream>
#include <iomanip>
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0600 /* Define so QueryFullProcessImageName can be used */
#include <windows.h>
#include <psapi.h>


using namespace std;


WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int cmdShow) {
    DWORD processIds[256];  /* Buffer for the process IDs */
    DWORD cbNeeded; /* Space needed from EnumProcesses() to store all IDs */
    /* 
     * I will not check if the space was sufficient or not because this is just a local experiment 
     * and i can insure it is enough space
     */
    if (EnumProcesses(processIds, sizeof(processIds), &cbNeeded) == 0) {
        cout << "Error while enumerating processes(" << GetLastError() << ")" << endl;
        return 0;
    }
    for (unsigned int i = 0; i < cbNeeded / sizeof(DWORD); i++) {
        DWORD nameBufferSize = 128;
        LPSTR processBaseName = new CHAR[128];
        /* Open the process; here is where i get access denied */
        HANDLE openProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processIds[i]);
        if (openProcess == NULL) {
            if(GetLastError() == 5) strcpy(processBaseName, "<denied>");
            else strcpy(processBaseName, "<unknown>");
        } else if (QueryFullProcessImageName(openProcess, NULL, processBaseName, nameBufferSize) == 0) {
            if(GetLastError() == 5) strcpy(processBaseName, "<denied>");
            else strcpy(processBaseName, "<unknown>");
        }
        cout << "PID: " << setw(6) << left << processIds[i] << "\t" << processBaseName << endl;
        delete processBaseName;
        CloseHandle(openProcess);
    }
    return 0;
}
Community
  • 1
  • 1
Yastanub
  • 1,227
  • 8
  • 19
  • 1
    Are you running this program as administrator? Without elevation Windows will deny access to quite a lot of stuff, for obvious security reasons. – Havenard Oct 13 '18 at 00:09
  • 1
    you need use `PROCESS_QUERY_LIMITED_INFORMATION` instead `PROCESS_QUERY_INFORMATION | PROCESS_VM_READ` in call `OpenProcess` and use `QueryFullProcessImageName` – RbMm Oct 13 '18 at 00:12
  • @Havenard i tried running as administrator now and i do get alot more access. Despite there are still some processes which are denied like csrss.exe which Cheat Engine does list – Yastanub Oct 13 '18 at 00:18
  • @RbMm sry i forgot to correct that when making the final copy into this post i now edited it the way it is. – Yastanub Oct 13 '18 at 00:19
  • and you need process name only or full image path ? if name only need use `Process32First/Process32Next` or better `SystemProcessInformation` with `NtQuerySystemInformation`. if get full process path by id - `SystemProcessIdInformation` information class – RbMm Oct 13 '18 at 00:21
  • @RbMm i only need the names for now. I saw the method with the snapshots in a couple of forums by now i think ill give it some attention it may fit my needings better for now. – Yastanub Oct 13 '18 at 00:25
  • if only names - `Process32First/Process32Next` or better `NtQuerySystemInformation` with `SystemProcessInformation` – RbMm Oct 13 '18 at 00:30
  • What is your question. The title suggests you want to know why the behaviour is as it is? But the question body reads as you not caring about the reason why very much. – David Heffernan Oct 13 '18 at 07:20
  • Some processes grant no access even to administrators. In these cases we need to enable SeDebugPrivilege in order to get `PROCESS_QUERY_LIMITED_INFORMATION` access. For example, csrss.exe (session server) grants discretionary access only to SYSTEM at System Mandatory Level (no read up, no write up). dwm.exe (window manager) grants access to SYSTEM and DWM-[session number] at System Mandatory Level (no read up, no write up). fontdrvhost.exe grants access to SYSTEM and UMFD-[session number] at Low Mandatory Level (no read up, no write up). – Eryk Sun Oct 13 '18 at 14:02
  • @DavidHeffernan the question does not imply i am not caring much. My question is why windows denies access when i use those functions and then i provide my thoughts as well so that people can build on that and have it easier to explain to me. I find that appropriate for a question. – Yastanub Oct 14 '18 at 00:42

2 Answers2

1

Protected process are your main reason reason. For example, you can't open handles to csrss.exe, smss.exe, or System. Second, try to enable debug priviledge SeDebugPriviledge. Then also run as admin to see if you get anymore processes. But you can't access protected processes, even with debug priviledge. For that, you need a kernel mode driver that uses SeLocateProcessImageFileName or PsGetProcessFileName.

Arush Agarampur
  • 1,340
  • 7
  • 20
  • 1
    `PROCESS_QUERY_LIMITED_INFORMATION` and `QueryFullProcessImageName` were added due to protected processes, so that is not the problem. It may appear to be the case because csrss.exe executes as a protected process; however, an administrator can't open non-protected dwm.exe and fontdrvhost.exe processes with limited-query access, not unless SeDebugPrivilege is enabled. This is simply a matter of discretionary access not being granted to administrators in the Process object's security descriptor. I think it's an oversight or bad design that admins have to enable SeDebugPrivilege in these cases. – Eryk Sun Oct 15 '18 at 11:16
  • Ok, I'll take note of that. – Arush Agarampur Oct 16 '18 at 03:48
0

There where many helpful information commented here so i can answer the question myself. The problem was, as stated, that i will never have sufficient permission to open protected system processes. The solution is to gather the information on another way. As commented this can be done much easier with CreateToolhelp32Snapshot to get a snapshot of all current running processes. Then i can iterate over them via Process32First and then all following with Process32Next and then just read the PROCESSENTRY32::szExeFile of the structure to get the name of the process.

The code i now use:

HANDLE processSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (processSnapshot == INVALID_HANDLE_VALUE) {
      std::cout << "Error while taking process snapshot(" << GetLastError() << ")" << std::endl;
      return 0
}
PROCESSENTRY32 process;
process.dwSize = sizeof(PROCESSENTRY32); /* This is neccessary as the Process32First/Next function expects the size of the class in this member before the first call */
if ( ! Process32First(processSnapshot, &process) ) {
      std::cout << "Error while accessing first entry of snapshot(" << GetLastError() << ")" << std::endl;
      return 0;
}
do {
      std::cout << "PID: " << process.th32ProcessID << "\t" << process.szExeFile << std::endl;
} while( Process32Next(processSnapshot, &process) );
if (GetLastError() != ERROR_NO_MORE_FILES) { /*  The Process32Next function throws the ERROR_NO_MORE_FILES error code when there is no more entry to read. If this is not the last error message something went wrong. */
        std::cout << "Error while enumerating processes(" << GetLastError() << ")" << std::endl;
}

Note that the snapshot is a snapshot of the current state and if any processes are opened or closed, the snapshot needs to be taken again to get the information of the new state and new processes.

Yastanub
  • 1,227
  • 8
  • 19
  • 1
    As an administrator with SeDebugPrivilege enabled, you can open every process on the system with `PROCESS_QUERY_LIMITED_INFORMATION` and call `QueryFullProcessImageName` to query the full path of the process. Wihout the `PROCESS_NAME_NATIVE` flag, this call succeeds only if the process is backed by an executable in the file system, which excludes unbacked system processes such as "System", "Registry", and "Memory Compression". With the `PROCESS_NAME_NATIVE` flag, their names are respectively "" (anonymous), "Registry", and "MemCompression". – Eryk Sun Oct 15 '18 at 15:25
  • The toolhelp snapshot used in this answer has the advantage over `QueryFullProcessImageName` in the case of these unbacked system processes, since it uses their official, special-cased names as returned by `NtQuerySystemInformation`: `SystemProcessInformation`. – Eryk Sun Oct 15 '18 at 15:27
  • `PROCESSENTRY32::szExeFile` only contains the exe name, not the full path. – MuaazH Jan 17 '23 at 23:52