Search code examples
cwindowsprocess

Enumerating Windows processes without using CreateToolhelp32Snapshot


I know how to use CreateToolhelp32Snapshot or EnumProcesses to enumerate processes but I was trying to experiment with different ways of doing it (I'm still learning and am trying to experiment with different ways of doing things and different ways of approaching problems).

I thought perhaps using for (DWORD i = 0; i < 1000; i++) {OpenProcess(PROCESS_VM_READ, FALSE, i)} to iterate over the processes and if OpenProcess succeeds I know I have a valid PID. I picked 1000 just for testing purposes to limit the results. Code as follows:

VOID getProcessList() {

    LPSTR procName[MAX_PATH];
    HANDLE pHandle = NULL;

    //try and open a handle to the process ID. Returns NULL if not a valid process ID
    for (DWORD i = 0; i < 1000; i++) {
        pHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, i);
        
        //if process ID does not exist try the next one
        if (pHandle == NULL) {
            continue;
        }

        //get module name
        GetModuleBaseName(pHandle, NULL, procName, MAX_PATH);

        //if process exists print it's details
        wprintf(L"%s                %d\n", procName, i);

        CloseHandle(pHandle);
    }

    printf("All processes enumerated");

}


int main() {

    printf("Process Name        Process ID\n\n");
    getProcessList();
}

It works, but when compared to task manager it does not return all of the processes that are listed on task manager - only the ones that I have started (as opposed to CreateToolhelp32Snapshot which returns everything). I assume it has something to do with permissions and the PROCESS_VM_READ permission, but I was looking through the other available permissions and couldn't find anything else to use.


Solution

  • It is possible to do with NtQuerySystemInformation using the SYSTEM_PROCESS_ID_INFORMATION class. The output formatting would need some work but this gets you what you want:

    #include <stdio.h>
    #include <windows.h>
    #include <winternl.h>
    
    #define STATUS_SUCCESS   ((NTSTATUS)0x00000000L)
    
    typedef struct _SYSTEM_PROCESS_ID_INFORMATION {
        PVOID ProcessID;                    //PID
        UNICODE_STRING ImageName;           //Process name
    } SYSTEM_PROCESS_ID_INFORMATION;
    
    typedef NTSTATUS(NTAPI* pNtQuerySystemInformation)(
        SYSTEM_INFORMATION_CLASS SystemInformationClass,        //internal reference to systeminfoclass SYSTEM_PROCESS_ID_INFORMATION
        PVOID                    SystemInformation,             //the systeminformation we want
        ULONG                    SystemInformationLength,       //buffer to hold the information
        PULONG                   ReturnLength                   
        );
    
    
    
    void getProcess(int pid) {
    
        pNtQuerySystemInformation NtQuerySystemInformation;   //handle to NtQuerySystemInformation
        SYSTEM_PROCESS_ID_INFORMATION pidInfo;                //SYSTEM_PROCESS_ID_INFORMATION stucture to be used
        WCHAR processName[256];                               //buffer to hold process name (pidInfo.ImageName.Buffer) - must be WCHAR to match PWSTR UNICODE_STRING structure
        ULONG returnLength = 0;                               //from above - not sure what it is ?????
        NTSTATUS status;                                      //to test success
        HANDLE hNtDLL;                                        //handle for loading NTDLL.DLL
        SYSTEM_INFORMATION_CLASS sysProcIdInfo = 0x58;        //SYSTEM_INFORMATION_CLASS for SYSTEM_PROCESS_ID_INFORMATION
    
        //load ntdll.dll
        hNtDLL = GetModuleHandleA("ntdll.dll");
        if (hNtDLL == NULL) {
            printf("[!] Failed to load ntdll.dll with error %d\n", GetLastError());
            return -1;
        }
    
        //get a handle to NtQuerySystemInformation
        NtQuerySystemInformation = (pNtQuerySystemInformation)GetProcAddress(hNtDLL, "NtQuerySystemInformation");
        if (NtQuerySystemInformation == NULL) {
            printf("[!] Failed to get address of NtQuerySystemInformation with error %d\n", GetLastError());
            return -1;
        }
    
        //initialize the SYSTEM_PROCESS_ID_INFORMATION structure
        pidInfo.ProcessID = (PVOID)pid;
        pidInfo.ImageName.Buffer = processName;
        pidInfo.ImageName.Length = 0;
        pidInfo.ImageName.MaximumLength = sizeof(processName);
    
        //Query the PID for information
        status = NtQuerySystemInformation(sysProcIdInfo, &pidInfo, sizeof(pidInfo), &returnLength);
        if (status == STATUS_SUCCESS) {
            wprintf(L"[+] The process ID %d is %s\n", pid, processName);
        }
    
    }
    
    int main() {
        
        for (int i = 0; i < 1000; i++) {
            getProcess(i);
        }
    
        return 0;
    }