DEV Community

Gurkirat Singh
Gurkirat Singh

Posted on

Windows System Programming: Processes

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 ๐Ÿ˜ƒ

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 function declared in Psapi.h header file.

BOOL EnumProcesses(
  DWORD   *lpidProcess,
  DWORD   cb,
  LPDWORD lpcbNeeded
);
Enter fullscreen mode Exit fullscreen mode

Function parameters

  • lpidProcess โ†’ A pointer to an array that receives the list of process ids (aka pid)
  • cb โ†’ The size of the lpidProcess array, in bytes.
  • lpcbNeeded โ†’ 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, declared in processthreadsapi.h 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.

HANDLE OpenProcess(
  DWORD dwDesiredAccess,
  BOOL  bInheritHandle,
  DWORD dwProcessId
);
Enter fullscreen mode Exit fullscreen mode

Function parameters

  • dwDesiredAccess โ†’ The access to the process object. This parameter can be one or more of the process access rights
  • bInheritHandle โ†’ If this value is TRUE, processes created by this process will inherit the handle
  • dwProcessId โ†’ 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 and GetModuleBaseName from Psapi.h

BOOL EnumProcessModules(
  HANDLE  hProcess,
  HMODULE *lphModule,
  DWORD   cb,
  LPDWORD lpcbNeeded
);
Enter fullscreen mode Exit fullscreen mode

Function parameters description as follows

  • hProcess โ†’ A valid handle to the process.
  • *lphModule โ†’ An array that receives the list of module handles.
  • cb โ†’ The size of the lphModule array, in bytes
  • lpcbNeeded โ†’ The number of bytes required to store all module handles in the lphModule array
DWORD GetMappedFileNameW(
  HANDLE hProcess,
  LPVOID lpv,
  LPWSTR lpFilename,
  DWORD  nSize
);
Enter fullscreen mode Exit fullscreen mode

Function parameters description as follows

  • hProcess โ†’ A handle to the process. with PROCESS_QUERY_INFORMATION access right
  • lpv โ†’ The address of the module to be verified.
  • lFilename โ†’ A pointer to the buffer that receives the name of the memory-mapped file to lpv belongs.
  • nSize โ†’ The size of the lpFilename buffer.

So the snippet for this will look like the following

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;
}
Enter fullscreen mode Exit fullscreen mode
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);
}
Enter fullscreen mode Exit fullscreen mode

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 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 from processthreadsapi.h

BOOL TerminateProcess(
  HANDLE hProcess,
  UINT   uExitCode
);
Enter fullscreen mode Exit fullscreen mode

Function parameters description as follows

  • hProcess โ†’ A handle to the process with PROCESS_TERMINATE permissions.
  • uExitCode โ†’ 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

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

Create Process

The CreateProcess function from processthreadsapi.h creates a new process, which runs independently of the creating process. However, for simplicity, the relationship is referred to as a parent-child relationship.

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
);
Enter fullscreen mode Exit fullscreen mode

Function parameter description as follows

  • lpApplicationName โ†’ 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 โ†’ 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 โ†’ A pointer to a SECURITY_ATTRIBUTES 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 โ†’ A pointer to a SECURITY_ATTRIBUTES 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 โ†’ 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 โ†’ The flags that control the priority class and the creation of the process. For a list of values, see Process Creation Flags.
  • lpEnvironment โ†’ 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 โ†’ 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 โ†’ A pointer to a STARTUPINFO or STARTUPINFOEX structure
  • lpProcessInformation โ†’ A pointer to a PROCESS_INFORMATION structure that receives identification information about the new process.

So the snippet will look like this

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;
}
Enter fullscreen mode Exit fullscreen mode

Getting Process Details

In this you will see, I will print the process name, pid, executable path. You can use GetProcessImageFileNameW function from Psapi.h

DWORD GetProcessImageFileNameW(
  HANDLE hProcess,
  LPWSTR lpImageFileName,
  DWORD  nSize
);
Enter fullscreen mode Exit fullscreen mode

Function parameter description

  • hProcess โ†’ A handle to the process with PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION access right
  • lpImageFileName โ†’ A pointer to a buffer that receives the full path to the executable file
  • nSize โ†’ 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

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;
}
Enter fullscreen mode Exit fullscreen mode

So here comes the complete code of all above

#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;
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)