Windows System Programming: Processes
Tags: windows, systemprogramming, cpp
Published: 2020-09-11 17:32:55

Hello readers, Today you will learn about the most interesting topic; "Processes". So before working on creating processes, you should know a little bit about processes. 

A process is a program which is currently in execution. You might also see many information with associated with a process, so on system level, a process is a data structure which stores all the information about process and resources used by it.

In this I will show you how to create a program that does the following
1. list processes
2. kill process by pid
3. create process
4. get process details

This post gonna be a bit more lengthy than others :smiley:

In this you will see me using _tchar.h_ and _wmain_ (instead of main). This is because from now onward, I will be working with wide characters.  `char` is used for so called ANSI family of functions (ends with _A_), whereas `wchar_t` is used for new so called Unicode (or Wide) family of functions (ends with _W_)

## List Processes
You can enumerate all the processes in your system by using [**EnumProcesses**](https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocesses) function declared in [_Psapi.h_](https://docs.microsoft.com/en-us/windows/win32/api/psapi/) header file.


```cpp
BOOL EnumProcesses(
  DWORD   *lpidProcess,
  DWORD   cb,
  LPDWORD lpcbNeeded
);
```
Function parameters
+ **lpidProcess** &rarr; A pointer to an array that receives the list of process ids (aka `pid`)
+ **cb** &rarr; The size of the `lpidProcess` array, in bytes.
+ **lpcbNeeded** &rarr; Bytes returned in the `lpidProcess` array.
 
You will get process ids of the running process in the `lpidProcess`. To get the name of the process, you need to first open the process using [**OpenProcess**](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess), declared in [_processthreadsapi.h_](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/) with `PROCESS_QUERY_INFORMATION | PROCESS_VM_READ` permissions, enumerate the modules and at last get the base module name. This base module will contain the name of the process.

```cpp
HANDLE OpenProcess(
  DWORD dwDesiredAccess,
  BOOL  bInheritHandle,
  DWORD dwProcessId
);
```
Function parameters
+ **dwDesiredAccess** &rarr; The access to the process object. This parameter can be one or more of the [process access rights](https://docs.microsoft.com/en-us/windows/desktop/ProcThread/process-security-and-access-rights)
+ **bInheritHandle** &rarr; If this value is `TRUE`, processes created by this process will inherit the handle
+ **dwProcessId** &rarr; The identifier of the local process to be opened.

If function will run successfully, it will return you a valid process handle.

To get the name of executable, you need to enumerate the process modules and then get the base module name

A loadable module of the process that let's process function is known as **process module**. It can either be a `.dll` or `.exe`. A process can load multiple modules into memory.

So to get the process name, you will be needing [**EnumProcessModules**](https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocessmodules) and [**GetModuleBaseName**](https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmappedfilenamew) from [_Psapi.h_](https://docs.microsoft.com/en-us/windows/win32/api/psapi/)

```cpp
BOOL EnumProcessModules(
  HANDLE  hProcess,
  HMODULE *lphModule,
  DWORD   cb,
  LPDWORD lpcbNeeded
);
```
Function parameters description as follows
+ **hProcess** &rarr; A valid handle to the process.
+ **\*lphModule** &rarr; An array that receives the list of module handles.
+ **cb** &rarr; The size of the `lphModule` array, in bytes
+ **lpcbNeeded** &rarr; The number of bytes required to store all module handles in the `lphModule` array

```cpp
DWORD GetMappedFileNameW(
  HANDLE hProcess,
  LPVOID lpv,
  LPWSTR lpFilename,
  DWORD  nSize
);
```
Function parameters description as follows
+ **hProcess** &rarr; A handle to the process. with `PROCESS_QUERY_INFORMATION` access right
+ **lpv** &rarr; The address of the module to be verified.
+ **lFilename** &rarr; A pointer to the buffer that receives the name of the memory-mapped file to `lpv` belongs.
+ **nSize** &rarr; The size of the `lpFilename` buffer.

So the snippet for this will look like the following
```cpp
int ListProcesses() {
	DWORD processes[1024], cbNeeded, cbProcesses;
	// enumerating all processes
	// https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocesses
	BOOL procBool = EnumProcesses(processes, sizeof(processes), &cbNeeded);
	if (!procBool) {
		_tprintf(_T("Unable to fetch processes\n"));
		return 1;
	}

	cbProcesses = cbNeeded / sizeof(DWORD); // getting actual number of processes that function has returned

	_tprintf(_T("Total Processes: %d\n\n"), cbProcesses);
	for (int i = 0; i < cbProcesses; i++) {
		GetProcessNameById(processes[i]); // printing process name
	}

	return 0;
}
```
```cpp
void GetProcessNameById(DWORD pId)
{
	// TCHAR is same as WCHAR (wchar_t)
	TCHAR procName[MAX_PATH] = _T("<Unknown>");

	// opening the process
	// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
	HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pId);
	
	// checking if process is opened
	// the processes running in same privilege will only be opened
	if (process != NULL) {
		HMODULE hMod;
		DWORD cbNeeded;

		// enumerating the process modules
		// https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocessmodules
		if (EnumProcessModules(process, &hMod, sizeof(hMod),
			&cbNeeded))
		{
			// getting module base name, which is the name of the process
			// https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmappedfilenamew
			GetModuleBaseName(process, hMod, procName,
				sizeof(procName) / sizeof(TCHAR));
		}
	}
	
	// closing process handle
	// https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
	CloseHandle(process);
	_tprintf(_T("[%d] %s\n"), pId, procName);
}
```
You will see many processes (even the one running with same user) have name `<Unknown>`. This is because your process does not have privileges to read the process memory and therefore enumerate all the modules. 

In case you want to read their names, you need to set the [_SeDebugPrivilege_](https://support.microsoft.com/en-in/help/131065/how-to-obtain-a-handle-to-any-process-with-sedebugprivilege) for the current process.



## Kill a Process by PID
You might have killed a running process from the task manager that is either lagging or corrupted. So programmatically, you can kill a process by [**TerminateProcess**](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess) from [_processthreadsapi.h_](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/)

```cpp
BOOL TerminateProcess(
  HANDLE hProcess,
  UINT   uExitCode
);
```
Function parameters description as follows
+ **hProcess** &rarr; A handle to the process with `PROCESS_TERMINATE` permissions.
+ **uExitCode** &rarr; The exit code to be used by the process and threads terminated as a result of this call.

Snippet of killing the process would look like the following
```cpp
int KillProcess(DWORD pId) {
	// opening process
	// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
	HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, pId);

	if (hProc == NULL) {
		_tprintf(_T("Unable to open process %d\n"), pId);
		return 1;
	}

	// terminating the process
	// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess
	BOOL tpBool = TerminateProcess(hProc, 2);  // exit the program with non zero return code

	if (!tpBool) {
		_tprintf(_T("Unable to kill the process %d\n"), pId);
		// closing process handle
		// https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
		CloseHandle(hProc);
		return 1;
	}
	else {
		_tprintf(_T("Killed Process %d\n"), pId);
		// closing process handle
		// https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
		CloseHandle(hProc);
		return 0;
	}
}
```

## Create Process
The [**CreateProcess**](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw) function from [_processthreadsapi.h_](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/) creates a new process, which runs independently of the creating process. However, for simplicity, the relationship is referred to as a parent-child relationship.

```cpp
BOOL CreateProcessW(
  LPCWSTR               lpApplicationName,
  LPWSTR                lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL                  bInheritHandles,
  DWORD                 dwCreationFlags,
  LPVOID                lpEnvironment,
  LPCWSTR               lpCurrentDirectory,
  LPSTARTUPINFOW        lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);
```
Function parameter description as follows
+ **lpApplicationName** &rarr; The name of the module to be executed. This module can be a Windows-based application. It can be some other type of module. The string can specify the full path and file name of the module to execute. If this is `NULL`,  the module name must be the first white space–delimited token in the `lpCommandLine` string.
+ **lpCommandLine** &rarr; The command line to be executed. The maximum length of this string is 32,767 character and  the module name portion of `lpCommandLine` is limited to `MAX_PATH` characters.
+ **lpProcessAttributes** &rarr; A pointer to a [`SECURITY_ATTRIBUTES`](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560\(v=vs.85\)) structure  that determines whether the returned handle to the new process object can be inherited by child processes. If this is `NULL`, the process handle can't be inherited.
+ **lpThreadAttributes** &rarr;  A pointer to a [`SECURITY_ATTRIBUTES`](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560\(v=vs.85\)) structure  that determines whether the returned handle to the new thread object can be inherited by child processes. If this is `NULL`, the process handle can't be inherited.
+ **bInheritHandles** &rarr; If this parameter is `TRUE`, each inheritable handle in the calling process is inherited by the new process. If the parameter is `FALSE`, the handles are not inherited
+ **dwCreationFlags** &rarr; The flags that control the priority class and the creation of the process. For a list of values, see [Process Creation Flags](https://docs.microsoft.com/en-us/windows/desktop/ProcThread/process-creation-flags).
+ **lpEnvironment** &rarr; A pointer to the environment block for the new process. If this parameter is `NULL`, the new process uses the environment of the calling process.
+ **lpCurrentDirectory** &rarr; The full path to the current directory for the process. If this parameter is `NULL`, the new process will have the same current drive and directory as the calling process.
+ **lpStartupInfo** &rarr; A pointer to a [`STARTUPINFO`](https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/ns-processthreadsapi-startupinfoa) or [`STARTUPINFOEX`](https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-startupinfoexa) structure
+ **lpProcessInformation** &rarr; A pointer to a [`PROCESS_INFORMATION`](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_information) structure that receives identification information about the new process.

So the snippet will look like this
```cpp
int _CreateProcess()
{
	std::wstring path(L"C:\\Windows\\notepad.exe");

	STARTUPINFO si;
	si.cb = sizeof(si);

	PROCESS_INFORMATION pi;

	// initializing the variables
	ZeroMemory(&si, sizeof(si));
	ZeroMemory(&pi, sizeof(pi));
	
	BOOL cpBool = CreateProcessW(
		NULL, // creating an independent process
		&path[0], // passing in the path of the executable 
		NULL, // don't want the process handle to be inherited
		NULL, // don't want the thread handle to be inherited
		FALSE, // setting inheritance handle to false
		0x0, // normal creation flags
		NULL, // using parent process environment config
		NULL, // the new process will have the same current drive and directory as the calling process
		&si, // passing in the startup information
		&pi // passing in the process information
	);

	if (cpBool) {
		_tprintf(_T("Process Created: %d"), pi.dwProcessId);
	}
	else {
		_tprintf(_T("Unable to create process\n"));
		return 1;
	}

	return 0;
}
```

## Getting Process Details
In this you will see, I will print the process name, pid, executable path. You can use [**GetProcessImageFileNameW**](https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getprocessimagefilenamew) function from [_Psapi.h_](https://docs.microsoft.com/en-us/windows/win32/api/psapi/)

```cpp
DWORD GetProcessImageFileNameW(
  HANDLE hProcess,
  LPWSTR lpImageFileName,
  DWORD  nSize
);
```
Function parameter description
+ **hProcess** &rarr; A handle to the process with `PROCESS_QUERY_INFORMATION` or `PROCESS_QUERY_LIMITED_INFORMATION` access right
+ **lpImageFileName** &rarr; A pointer to a buffer that receives the full path to the executable file
+ **nSize** &rarr; The size of the lpImageFileName buffer, in characters.

If the function succeeds, it will return length of the string copied to the buffer.

The snippet for this module would be
```cpp
int GetProcess(DWORD pId)
{
	// opening process
	// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
	HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pId);

	if (hProc == NULL || hProc == INVALID_HANDLE_VALUE)
	{
		_tprintf(_T("Unable to open process"));
		return 1;
	}

	// initializing the variable
	LPWSTR procName = (LPWSTR)malloc(MAX_PATH);

	// getting process name by id
	GetProcessNameById(pId);

	// get process executable path
	// https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getprocessimagefilenamew
	GetProcessImageFileNameW(hProc, &procName[0], MAX_PATH);
	_tprintf(_T("Executable Path: %s\n"), procName);

	// deallocate the variables
	free(procName);
	CloseHandle(hProc);
	return 0;
}
```


So here comes the complete code of all above
```cpp
#include <Windows.h>
#include <tchar.h>
#include <Psapi.h>
#include <iostream>
#include <processthreadsapi.h>
#include <handleapi.h>

/*
Function prototypes
*/
int ListProcesses();
int KillProcess(DWORD pId);
int _CreateProcess();
int GetProcess(DWORD pId);
void GetProcessNameById(DWORD pId);

int wmain(int argc, WCHAR** argv) {
	
	int option;
	_tprintf(_T("1. list processes\n"));
	_tprintf(_T("2. kill process by pid\n"));
	_tprintf(_T("3. create process\n"));
	_tprintf(_T("4. get process details\n"));
	_tprintf(_T("> "));
	DWORD pid;

	std::wcin >> option; // use wcin when you want to get wide charaters

	switch (option) {
	case 1:
		return ListProcesses();
	case 2:
		_tprintf(_T("Enter Process PID: "));
		std::wcin >> pid;
		return KillProcess(pid);
		break;
	case 3:
		return _CreateProcess();
		break;
	case 4:
		_tprintf(_T("Enter Process PID: "));
		std::wcin >> pid;
		return GetProcess(pid);
		break;
	default:
		_tprintf(_T("Please select options 1 2 3 4 only\n"));
		return 1;
	}
}

int ListProcesses() {
	DWORD processes[1024], cbNeeded, cbProcesses;
	// enumerating all processes
	// https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocesses
	BOOL procBool = EnumProcesses(processes, sizeof(processes), &cbNeeded);
	if (!procBool) {
		_tprintf(_T("Unable to fetch processes\n"));
		return 1;
	}

	cbProcesses = cbNeeded / sizeof(DWORD); // getting actual number of processes that function has returned

	_tprintf(_T("Total Processes: %d\n\n"), cbProcesses);
	for (int i = 0; i < cbProcesses; i++) {
		GetProcessNameById(processes[i]); // printing process name
	}

	return 0;
}

void GetProcessNameById(DWORD pId)
{
	// TCHAR is same as WCHAR (wchar_t)
	TCHAR procName[MAX_PATH] = _T("<Unknown>");

	// opening the process
	// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
	HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pId);
	
	// checking if process is opened
	// the processes running in same privilege will only be opened
	if (process != NULL) {
		HMODULE hMod;
		DWORD cbNeeded;

		// enumerating the process modules
		// https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocessmodules
		if (EnumProcessModules(process, &hMod, sizeof(hMod),
			&cbNeeded))
		{
			// getting module base name, which is the name of the process
			// https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmappedfilenamew
			GetModuleBaseName(process, hMod, procName,
				sizeof(procName) / sizeof(TCHAR));
		}
	}
	
	// closing process handle
	// https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
	CloseHandle(process);
	_tprintf(_T("[%d] %s\n"), pId, procName);
}

int KillProcess(DWORD pId) {
	// opening process
	// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
	HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, pId);

	if (hProc == NULL) {
		_tprintf(_T("Unable to open process %d\n"), pId);
		return 1;
	}

	// terminating the process
	// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess
	BOOL tpBool = TerminateProcess(hProc, 2);  // exit the program with non zero return code

	if (!tpBool) {
		_tprintf(_T("Unable to kill the process %d\n"), pId);
		// closing process handle
		// https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
		CloseHandle(hProc);
		return 1;
	}
	else {
		_tprintf(_T("Killed Process %d\n"), pId);
		// closing process handle
		// https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
		CloseHandle(hProc);
		return 0;
	}
}

int _CreateProcess()
{
	std::wstring path(L"C:\\Windows\\notepad.exe");

	STARTUPINFO si;
	si.cb = sizeof(si);

	PROCESS_INFORMATION pi;

	// initializing the variables
	// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa366920(v=vs.85)
	ZeroMemory(&si, sizeof(si));
	ZeroMemory(&pi, sizeof(pi));
	
	// creating process
	// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw
	BOOL cpBool = CreateProcessW(
		NULL, // creating an independent process
		&path[0], // passing in the path of the executable 
		NULL, // don't want the process handle to be inherited
		NULL, // don't want the thread handle to be inherited
		FALSE, // setting inheritance handle to false
		0x0, // normal creation flags
		NULL, // using parent process environment config
		NULL, // the new process will have the same current drive and directory as the calling process
		&si, // passing in the startup information
		&pi // passing in the process information
	);

	if (cpBool) {
		_tprintf(_T("Process Created: %d"), pi.dwProcessId);
	}
	else {
		_tprintf(_T("Unable to create process\n"));
		return 1;
	}

	return 0;
}

int GetProcess(DWORD pId)
{
	// opening process
	// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
	HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pId);

	if (hProc == NULL || hProc == INVALID_HANDLE_VALUE)
	{
		_tprintf(_T("Unable to open process"));
		return 1;
	}

	// initializing the variable
	LPWSTR procName = (LPWSTR)malloc(MAX_PATH);

	// getting process name by id
	GetProcessNameById(pId);

	// get process executable path
	// https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getprocessimagefilenamew
	GetProcessImageFileNameW(hProc, &procName[0], MAX_PATH);
	_tprintf(_T("Executable Path: %s\n"), procName);

	// deallocate the variables
	free(procName);
	CloseHandle(hProc);
	return 0;
}
```

Thanks for reading this post. Follow the links to reach me

+ Email: tbhaxor@gmail.com
+ Twitter: [@tbhaxor](https://twitter.com/tbhaxor)
+ Facebook: [@tbhaxor](https://fb.com/tbhaxor)
+ GitHub: [@tbhaxor](https://github.com/tbhaxor)
+ LinkedIn: [@gurkirat--singh](https://www.linkedin.com/in/gurkirat--singh)
+ Instagram: [@_tbhaxor_](https://instagram.com/_tbhaxor_)