EnumProcesses
Introduction
Process enumeration can be used alongside process injection, to identify a target process and perform injection by utilising CreateToolHelp32Snapshot
. We can perform process enumeration using EnumProcesses
.
EnumProcesses
We start by reviewing Microsoft’s documentation on EnumProcesses and notice that the function returns a Process Ids (PIDs) as an array, without the associated process name. The problem is that only having PIDs without the associated process name makes it difficult to identify the process from a human perspective.
The solution is to use the OpenProcess, GetModuleBaseName and EnumProcessModules WinAPIs.
OpenProcess
will be used to open a handle to a PID withPROCESS_QUERY_INFORMATION
andPROCESS_VM_READ
access rights.EnumProcessModules
will be used to enumerate all the modules within the opened process.GetModuleBaseName
will determine the name of the process, given the enumerated process modules.
EnumProcesses Advantage
Using the CreateToolhelp32Snapshot
process enumeration method, a snapshot is created and a string comparison is performed to determine whether the process name matches the intended target process. The issue with that method is when there are multiple instances of a process running at different privilege levels, there’s no way to differentiate them during the string comparison. For example, some svchost.exe
processes run with normal user privileges whereas others run with elevated privileges. There is no way to determine the privilege level of svchost.exe
during the string comparison. Therefore the only indicator as to whether it’s privileged is if the OpenProcess
call fails (assuming that the implementation is running with normal user privileges).
On the other hand, using the EnumProcesses
process enumeration method provides the PID and handle to the process, and the objective is to obtain the process name. This method is guaranteed to be successful since a handle to the process already exists.
PrintProcesses Function
PrintProcesses
is a custom function that prints the process name and PID of the enumerated processes. Only processes running with the same privileges as the implementation can have their information retrieved. Information about elevated processes cannot be retrieved, again, assuming the implementation is running with normal user privileges. Attempts to open a handle to high-privileged processes using OpenProcess
will result in ERROR_ACCESS_DENIED
error.
It’s possible to use OpenProcess
’s response as an indicator to determine if the process can be targeted. Processes that cannot have a handle open to them cannot be targeted whereas the ones with a handle successfully opened can be targeted.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
BOOL PrintProcesses() {
DWORD adwProcesses [1024 * 2],
dwReturnLen1 = NULL,
dwReturnLen2 = NULL,
dwNmbrOfPids = NULL;
HANDLE hProcess = NULL;
HMODULE hModule = NULL;
WCHAR szProc [MAX_PATH];
// Get the array of PIDs
if (!EnumProcesses(adwProcesses, sizeof(adwProcesses), &dwReturnLen1)) {
printf("[!] EnumProcesses Failed With Error : %d \n", GetLastError());
return FALSE;
}
// Calculating the number of elements in the array
dwNmbrOfPids = dwReturnLen1 / sizeof(DWORD);
printf("[i] Number Of Processes Detected : %d \n", dwNmbrOfPids);
for (int i = 0; i < dwNmbrOfPids; i++) {
// If process is not NULL
if (adwProcesses[i] != NULL) {
// Open a process handle
if ((hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, adwProcesses[i])) != NULL) {
// If handle is valid
// Get a handle of a module in the process 'hProcess'
// The module handle is needed for 'GetModuleBaseName'
if (!EnumProcessModules(hProcess, &hModule, sizeof(HMODULE), &dwReturnLen2)) {
printf("[!] EnumProcessModules Failed [ At Pid: %d ] With Error : %d \n", adwProcesses[i], GetLastError());
}
else {
// If EnumProcessModules succeeded
// Get the name of 'hProcess' and save it in the 'szProc' variable
if (!GetModuleBaseName(hProcess, hModule, szProc, sizeof(szProc) / sizeof(WCHAR))) {
printf("[!] GetModuleBaseName Failed [ At Pid: %d ] With Error : %d \n", adwProcesses[i], GetLastError());
}
else {
// Printing the process name & its PID
wprintf(L"[%0.3d] Process \"%s\" - Of Pid : %d \n", i, szProc, adwProcesses[i]);
}
}
// Close process handle
CloseHandle(hProcess);
}
}
// Iterate through the PIDs array
}
return TRUE;
}
GetRemoteProcessHandle Function
The code snippet below is an update to the previous PrintProcesses
function. GetRemoteProcessHandle
will perform the same tasks as PrintProcesses
except it will return a handle to the specified process.
The updated function uses wcscmp
to verify the target process. Furthermore, OpenProcess
’s access control is changed from PROCESS_QUERY_INFORMATION | PROCESS_VM_READ
to PROCESS_ALL_ACCESS
to provide more access to the returned process object.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
BOOL GetRemoteProcessHandle(LPCWSTR szProcName, DWORD* pdwPid, HANDLE* phProcess) {
DWORD adwProcesses [1024 * 2],
dwReturnLen1 = NULL,
dwReturnLen2 = NULL,
dwNmbrOfPids = NULL;
HANDLE hProcess = NULL;
HMODULE hModule = NULL;
WCHAR szProc [MAX_PATH];
// Get the array of PIDs
if (!EnumProcesses(adwProcesses, sizeof(adwProcesses), &dwReturnLen1)) {
printf("[!] EnumProcesses Failed With Error : %d \n", GetLastError());
return FALSE;
}
// Calculating the number of elements in the array
dwNmbrOfPids = dwReturnLen1 / sizeof(DWORD);
printf("[i] Number Of Processes Detected : %d \n", dwNmbrOfPids);
for (int i = 0; i < dwNmbrOfPids; i++) {
// If process is not NULL
if (adwProcesses[i] != NULL) {
// Open a process handle
if ((hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, adwProcesses[i])) != NULL) {
// If handle is valid
// Get a handle of a module in the process 'hProcess'.
// The module handle is needed for 'GetModuleBaseName'
if (!EnumProcessModules(hProcess, &hModule, sizeof(HMODULE), &dwReturnLen2)) {
printf("[!] EnumProcessModules Failed [ At Pid: %d ] With Error : %d \n", adwProcesses[i], GetLastError());
}
else {
// If EnumProcessModules succeeded
// Get the name of 'hProcess' and save it in the 'szProc' variable
if (!GetModuleBaseName(hProcess, hModule, szProc, sizeof(szProc) / sizeof(WCHAR))) {
printf("[!] GetModuleBaseName Failed [ At Pid: %d ] With Error : %d \n", adwProcesses[i], GetLastError());
}
else {
// Perform the comparison logic
if (wcscmp(szProcName, szProc) == 0) {
wprintf(L"[+] FOUND \"%s\" - Of Pid : %d \n", szProc, adwProcesses[i]);
// Return by reference
*pdwPid = adwProcesses[i];
*phProcess = hProcess;
break;
}
}
}
CloseHandle(hProcess);
}
}
}
// Check if pdwPid or phProcess are NULL
if (*pdwPid == NULL || *phProcess == NULL)
return FALSE;
else
return TRUE;
}
Demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Defining target process to enumerate by its name
#define TARGET_PROCESS L"svchost.exe"
int EnumProcesses_example1_basic() {
DWORD Pid = NULL;
HANDLE hProcess = NULL;
if (!GetRemoteProcessHandle(TARGET_PROCESS, &Pid, &hProcess)) {
return -1;
}
wprintf(L"[+] FOUND \"%s\" - Of Pid : %d \n", TARGET_PROCESS, Pid);
//PrintProcesses();
printf("[#] Press <Enter> To Quit ... ");
(void)getchar();
return 0;
}