Introduction to Win32 API
For my first real post, I am going to explain the basics of processes, threads, handles, and the Win32 API. These concepts are fundamental to learning how to manipulate other running processes in order to establish persistence, evade detection, and much more.
i.e. Malware
Processes
What is a process?
Each process is an instance of an executing program that has its own memory space and system resources. These processes are managed by the operating system, which allocates resources and schedules their execution.
Virtual Address Space
One of the most important features of a process is its virtual address space. This is a unique memory space that is allocated to each process by the operating system. The virtual address space allows a process to access and manipulate its own memory without interfering with other processes running on the same system.
The virtual address space is divided into multiple sections, each with its own set of permissions and attributes. These sections include the code section, which contains the executable code of the process, and the data section, which contains the variables and data used by the process.
By separating each process into its own virtual address space, the operating system can ensure that each process runs independently of the others, without interfering with their memory or resources.
What is a thread?
A thread is a unit of execution within a process. Each process can have multiple threads, each of which can execute independently of the others. Threads share the same virtual address space as their parent process, so they can access the same memory and resources.
Threads are used to perform concurrent tasks within a process. For example, a web browser might use one thread to handle user input and another thread to download files in the background. By using multiple threads, the browser can perform multiple tasks simultaneously, without blocking the user interface.
Source: codemag.com
Process Handles
In Windows, a process handle is a unique identifier that the operating system assigns to each process. This handle can be used to manage the process, including allocating resources, monitoring its execution, and terminating it if necessary.
The two types of handles we are most interested in are handles to processes and threads, known as a HANDLE
, and handles to modules known as an HMODULE
.
An imaginary function of a program that interacts with another process would look something like this:
// Declare our process handle
HANDLE hProcess;
/* Get the handle somehow...
Pass the handle as an argument to our function */
GetProcessInfo(hProcess);
// This process can now interact with the process referenced by hProcess
Win32 API
The Win32 API is a collection of functions and data structures that allow developers to interact with the Windows operating system. These functions can be used to manipulate resources such as files, registry keys, and processes.
Some common Win32 API functions used in malware development include CreateProcess
, which can be used to launch a new process, and WriteProcessMemory
, which can be used to write data to the memory of another process.
Understanding the Win32 API is essential for malware developers, as it allows them to interact with the operating system and perform a wide range of malicious activities.
But, before we do all of those fun things, let’s make our first Win32 API call to say hello world
.
The function we will be using for this is called MessageBoxW
which as you might guess, creates a pop-up message box.
Microsoft’s documentation about this API is extremely useful in deciphering their initially cryptic functions and data structures. We can see their snippet about this function below.
int MessageBox(
[in, optional] HWND hWnd, // Handle to owner window
[in, optional] LPCTSTR lpText, // Message box text
[in, optional] LPCTSTR lpCaption, // Title bar text
[in] UINT uType // Behavior of the box (See the documentation)
);
This function returns an int
value which indicates which option was pressed.
So… calling this function to display hello world
would look something like this.
#include <windows.h> // Import Win32 API
int main () {
MessageBoxW(
NULL, // We don't need an owner window for now
L"Hello world", // Message box text
L"Amazing Title", // Title bar text
MB_OK // Just one button that says "OK"
);
return EXIT_SUCCESS;
}
Running the W
version of MessageBox indicates that we are using wide (unicode) characters. This naming convention is the same for many other Win32 API calls. Adding the L
macro in front of our argument strings ensures they are encoded as unicode.
Thanks Microsoft.
When we run our code, we get the expected result.
Creating a Process
The Win32 API call we will be using to create a new process is aptly named CreateProcessW
. As mentioned previously, the W
simply indicates that we will be passing it wide (unicode) characters. From Microsoft’s Documentation, here is the Syntax used for this function:
BOOL CreateProcessW(
[in, optional] LPCWSTR lpApplicationName,
[in, out, optional] LPWSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCWSTR lpCurrentDirectory,
[in] LPSTARTUPINFOW lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
So, we can write a very simple program to create a new process. This new process will of course be calc.exe
because that’s what cool people do.
#include <windows.h>
#include <iostream>
int main()
{
//Declare and initialize all values in these data structures to 0
STARTUPINFOW startupInfo = { 0 };
PROCESS_INFORMATION processInfo = { 0 };
if (!CreateProcessW(
L"C:\\Windows\\System32\\calc.exe", // The imagefilename we are going to launch as a new process
NULL, // No need to to pass command line since we are explicitly calling the program above
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS, // Set to normal process priority
NULL,
NULL,
&startupInfo, // A pointer to the STARTUPINFO structure declared above
&processInfo // A pointer to the PROCESS_INFORMATION structure declared above
)){
// Print error statement if we fail to create process
printf("Failed to create process");
return EXIT_FAILURE;
}
// We can then get information about our new process from the PROCESS_INFORMATION
// PROCESS_INFORMATION variable.
printf("Process ID of new process is %ld",processInfo.dwProcessId);
return EXIT_SUCCESS;
}
And when we launch our program, we see that calc.exe is launched and we get it’s Process ID.
We can see the process tree of this behavior here. Notice the Process ID of the calc.exe process.
That’s it for now. I hope you enjoyed this introduction. I plan on exploring this more very soon and my next post will dig further into some of the exciting things we can do with this API.
Thanks for reading.
Big shoutout to crow for his informative and entertaining videos discussing these topics. Definitely check out his channel.