Diving into win32 Processes

A process from the Windows perspective is a kernel object that characterizes a certain address space within which threads execute. As with any other kernel object, a process has a reference count indicating how many open handles there are to itself. A process is created via a call to the CreateProcess() function, and remains in existence until all open handles to the process have been closed, at which point the reference count for the process object is zero, and there are no more active threads in the process.

The GetCurrentProcess() function returns a pseudo handle to the current process. A handle is typically an integer value, although technically speaking we ought to consider a handle as opaque, meaning that we only really need to focus on how to us it, not what it is, as such. A pseudo handle is a special integral value, which again we should consider opaque, which is why we acquire it via the GetCurrentProcess() function, rather than hard-coding it as a constant value.

Once we have a handle to the process, there are a number of functions we can call to provide us with information about the process. The GetProcessId() function takes the handle to the process and returns the ID of the process.

#include "stdafx.h"
#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hCurrProc;

    hCurrProc = GetCurrentProcess();

    //pseudo handle will be (HANDLE)-1
    _tprintf(_T("Handle literal value %d\n"), (int)hCurrProc);

    //process ID
    _tprintf(_T("Process ID of current process %d\n"), GetCurrentProcessId());
    //returns the same data, but this call can be used with any process 
    _tprintf(_T("Ditto %d\n"), GetProcessId(hCurrProc));


    return 0;
}

An IO_COUNTERS structure is used to store I/O information for a process or a job object. The win32 GetProcessIoCounters() function enables us to get a pointer to an IO_COUNTERS structure that contains the I/O accounting information for the process.

#include "stdafx.h"
#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    IO_COUNTERS structIOCount;
    HANDLE hCurrProcess = GetCurrentProcess();
    //second argument needs to be the address of an IO_COUNTERS
    //structure
    if (GetProcessIoCounters(hCurrProcess, &structIOCount)){
        _tprintf(_T("Got I/O Information for Process %d\n"), GetProcessId(hCurrProcess));
        _tprintf(_T("The number of read operations performed: %d\n"), structIOCount.ReadOperationCount);
        _tprintf(_T("The number of write operations performed: %d\n"), structIOCount.WriteOperationCount);
        _tprintf(_T("The number of bytes read: %d\n"), structIOCount.ReadTransferCount);
        _tprintf(_T("THe number of bytes written: %d\n"), structIOCount.WriteTransferCount);
    }


    return 0;
}

The CreateProcess() function returns a handle to a new process object that will manage the lifetime of the indicated running program. The CreateProcess() function takes ten arguments

The first parameter for CreateProcess() is a pointer to the null-terminated name of the program to run; if this parameter is NULL, the program indicated in the second argument will be executed. This second argument is likewise a null-terminated string; it indicates the program to execute, along with any command-line arguments. The third argument is a pointer to a SECURITY_ATTRIBUTES structure; if this argument is NULL, the default security attributes are used, and the process handle returned by the function is not inheritable. The fourth argument is likewise a pointer to a SECURITY_ATTRIBUTES structure; if NULL, the default attributes are used, and the thread handle is not inheritable. The fifth argument is a Boolean value indicating whether or not the new process should inherit the inheritable handles of the calling process. The sixth argument is a DWORD value that indicates the creation flags, which can be OR-ed together to form combinations. This argument can also be used to control the priority of the new process. The seventh argument is a pointer to an environment block for the new process. If we put NULL here than the process will be passed a copy of the current environment. The eight argument is a string that specifies the initial default drive and directory of the process. The ninth argument is a pointer to a STARTUPINFO structure, and the tenth argument is a pointer to a PROCESS_INFORMATION structure that will be filled in with details about the newly created process.

#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    STARTUPINFO structSI;
    PROCESS_INFORMATION structPI;
    BOOL bRValue;
    ZeroMemory(&structSI, sizeof(structSI));
    //important!
    structSI.cb = sizeof(STARTUPINFO);

    bRValue = CreateProcess(
        NULL, //lpApplicationName
        "notepad.exe", //lpCommandLine
        NULL, //lpProcessAttributes
        NULL, //lpThreadAttributes
        TRUE, //bInheritHandles
        0, //dwCreationFlags
        NULL, //lpEnvironment
        NULL, //lpCurrentDirectory
        &structSI, //lpStartupInfo
        &structPI //lpProcessInformation
        );

    if (bRValue){
        printf("Process created.\n");
    }

    return 0;
}

The PROCESS_INFORMATION structure has four members. The hProcess member is the handle to the newly created process. The calling program should use the CloseHandle() function to release its reference to the newly created process kernel object. The hThread member is the handle to the primary thread of the new process. The dwProcessId member stores the system-wide unique identifier to the new process. The dwThreadId member stores the system-wide unique identifier to the primary thread of the new process.

#include "stdafx.h"
#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    STARTUPINFO structSI;
    PROCESS_INFORMATION structPI;

    ZeroMemory(&structSI, sizeof(structSI));
    structSI.cb = sizeof(structSI);

    //createprocess returns Boolean value
    if (CreateProcess(NULL, "notepad.exe", NULL, NULL, FALSE,
        0, NULL, NULL, &structSI, &structPI)){
        //display the information stored in structPI
        printf("Notepad.exe created as process %d and thread %d\n",
            structPI.dwProcessId, structPI.dwThreadId);
    }

    //let's close the newly created process and thread handles
    CloseHandle(structPI.hProcess);
    CloseHandle(structPI.hThread);

}

Each process has a handle and a system-wide unique identifier. From the calling program, we can access these values via the PROCESS_INFORMATION data structure. From within the process itself, an application can determine its own pseudo-handle via the GetCurrentProcess() call. Note that a pseudo-handle is only valid in the context of the calling process, as it is not a real handle.

The TerminateProcess() function can be used to terminate another process. Generally speaking, this function should not be called unless in an emergency.

Any process can get the exit status of a process it has a handle to by using the GetExitCodeProcess() function. The GetExitCodeProcess() function returns a Boolean value and accepts two parameters, the first being the handle to the process and the second being a pointer to a DWORD value to store the exit code.

#include "stdafx.h"
#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    STARTUPINFO structSInfo;
    PROCESS_INFORMATION structPInfo;
    DWORD dwExitCode;
    BOOLEAN boolRValue;

    ZeroMemory(&structSInfo, sizeof(structSInfo));

    //start notepad
    boolRValue = CreateProcess(
        NULL, //application name
        "notepad.exe", //command line
        NULL, //Inherit security attributes
        NULL, //process attributes
        TRUE, //inherit handles
        0, //no creation flags
        NULL, //environment
        NULL, //current directory
        &structSInfo,
        &structPInfo
        );

    if (boolRValue){
        printf("Notepad.exe created as process %d, thread %d.\n", structPInfo.dwProcessId, structPInfo.dwThreadId);
    }
    else {
        printf("Error creating new process.");
        exit(EXIT_FAILURE);
    }

    //wait for a few seconds
    Sleep(3000);

    //terminate notepad.exe
    TerminateProcess(structPInfo.hProcess, 0);

    //get exit code
    boolRValue = GetExitCodeProcess(structPInfo.hProcess, &dwExitCode);

    if (boolRValue){
        printf("notepad.exe's exit code is %d\n", dwExitCode);
    }

    CloseHandle(structPInfo.hProcess);
    CloseHandle(structPInfo.hThread);

    return 0;

}

One last item to cover is waiting for a process to exit – this is the simplest, and perhaps most limiting, way to synchronize processes. To wait for a process to exit, we simply call  WaitForSingleObject(). The WaitForSingleObject() function takes two arguments, the first is the handle to the process, and the second specifies how long we want the process to wait for the other process to end.

#include "stdafx.h"
#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    STARTUPINFO structSI;
    PROCESS_INFORMATION structPI;
    DWORD dwWaitRValue, dwExitCode;

    ZeroMemory(&structSI, sizeof(structSI));
    structSI.cb = sizeof(structSI);

    CreateProcess(
        NULL,
        "powershell.exe",
        NULL,
        NULL,
        FALSE,
        CREATE_NEW_CONSOLE, //creation flag
        NULL,
        NULL,
        &structSI,
        &structPI
        );

    //print some information
    printf("Powershell.exe was created as process ID %d.\n", structPI.dwProcessId);

    //wait for process to exit
    while (true){
        dwWaitRValue = WaitForSingleObject(structPI.hProcess, 1000);
        if (dwWaitRValue == WAIT_OBJECT_0){
            printf("\nProcess ID %d exited.\n", structPI.dwProcessId);
            break;
        }
        if (dwWaitRValue == WAIT_TIMEOUT){
            printf("Powershell.exe (Process %d) has not exited yet.\n", structPI.dwProcessId);
        }
    }

    //get exit code
    if (GetExitCodeProcess(structPI.hProcess, &dwExitCode)){
        printf("Powershell.exe exited with code %d.\n", dwExitCode);
    }

    CloseHandle(structPI.hProcess);
    CloseHandle(structPI.hThread);

    return 0;

}

This covers most of what we need to know about the basics of creating and managing processes in Windows.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s