The Windows API enables direct interaction with core components of the Windows operating system, making it a popular tool among various users, such as red teamers, threat actors, blue teamers, software developers, and solution providers. In this post, we solve TryHackMe Introduction to Windows API as well.
Its seamless integration with the Windows system allows for a wide range of applications. The Win32 API, in particular, is commonly utilized for tasks such as offensive tool and malware development, EDR (Endpoint Detection & Response) engineering, and general software development. For a comprehensive overview of its use cases, refer to the Windows API Index.
Certified Security Blue Team Level 1 Study Notes
Information Security 101 | Study Notes
SubSystem & Hardware Interaction with Windows APIs
Programs frequently require access to Windows subsystems or hardware but are limited to ensure system stability. To address this, Microsoft introduced the Win32 API, a library that acts as a bridge between user-mode applications and the kernel.
Windows organizes hardware access into two primary modes: user mode and kernel mode. These modes define the level of access an application or driver has to hardware, the kernel, and memory. API or system calls serve as intermediaries between these modes, transferring information to the system for processing in kernel mode.
When examining how programming languages interact with the Win32 API, the process becomes more complex. The application must first pass through the language runtime before interacting with the API.
For additional details on the runtime, refer to Runtime Detection Evasion.
Windows API Components
The Win32 API, often referred to as the Windows API, relies on several integral components that establish its structure and organization.
To understand it better, let’s take a top-down approach. At the top layer is the API itself, while at the bottom layer are the parameters that define specific API calls.
Layer | Explanation |
API | A top-level/general term or theory used to describe any call found in the win32 API structure. |
Header files or imports | Defines libraries to be imported at run-time, defined by header files or library imports. Uses pointers to obtain the function address. |
Core DLLs | A group of four DLLs that define call structures. (KERNEL32, USER32, and ADVAPI32). These DLLs define kernel and user services that are not contained in a single subsystem. |
Supplemental DLLs | Other DLLs defined as part of the Windows API. Controls separate subsystems of the Windows OS. ~36 other defined DLLs. (NTDLL, COM, FVEAPI, etc.) |
Call Structures | Defines the API call itself and parameters of the call. |
API Calls | The API call used within a program, with function addresses obtained from pointers. |
In/Out Parameters | The parameter values that are defined by the call structures. |
Windows OS Libraries
Each API call within the Win32 library is stored in memory and requires a pointer to its memory address. However, obtaining these pointers is complicated by ASLR (Address Space Layout Randomization), which randomizes memory locations to enhance security. Different programming languages or packages employ distinct methods to bypass ASLR.
In this discussion, we will focus on two of the most common approaches: P/Invoke and the Windows header file.
Windows Header File
Microsoft introduced the Windows header file, commonly referred to as the Windows loader, as a direct solution to challenges posed by ASLR. At a high level, the loader operates at runtime by identifying the API calls being made and creating a thunk table to resolve function addresses or pointers.
Fortunately, it’s not necessary to delve deeply into the inner workings of this process to use API calls effectively.
By simply including the windows.h file at the beginning of an unmanaged program, developers can invoke any Win32 function with ease.
P/Invoke
Microsoft defines P/Invoke (Platform Invoke) as “a technology that allows you to access structs, callbacks, and functions in unmanaged libraries from your managed code.”
P/Invoke simplifies the process of calling unmanaged functions, such as those in the Win32 API, from managed code. It begins by importing the necessary DLL that contains the desired unmanaged function or Win32 API call. This process provides a straightforward way to bridge the gap between managed and unmanaged code.
Here is an example of how a DLL can be imported with specific options:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);
This declaration demonstrates importing the user32.dll
library to access the MessageBox
function. P/Invoke handles the details of invoking this unmanaged function from the managed environment.
API Calls in the Win32 Library
API calls form the second major component of the Win32 library, providing extensibility and flexibility to address a wide variety of use cases. Most of these API calls are thoroughly documented in resources like the Windows API documentation and pinvoke.net.
In this task, we will explore the basics of API call naming conventions and the use of in/out parameters.
Naming Schemes and Representational Characters
The functionality of an API call can be extended by altering its naming scheme and appending specific representational characters.
Below is a table summarizing the naming conventions supported by Microsoft for these extensions:
Character | Description |
---|---|
A | Indicates ANSI (American National Standards Institute) encoding for string parameters. |
W | Indicates Unicode encoding for string parameters. |
Ex | Represents an “extended” version of a standard function with additional features. |
32 | Refers to 32-bit specific versions of a function. |
This naming system allows developers to select functions that best match their needs, such as those supporting Unicode or enhanced functionality.
Creating API Calls in C and C++
Microsoft provides low-level programming languages like C and C++ with a set of pre-configured libraries to simplify access to API calls. The windows.h header file, discussed earlier, is key to defining the structure of these calls and obtaining function pointers.
To include the windows.h file in your program, add the following line at the top of your code:
#include <windows.h>
Objective: Creating a Pop-Up Window
Let’s create a simple pop-up window with the title “Test” using the CreateWindowExA
function.
First, let’s revisit the in/out parameters of this API call to understand how it works:
HWND CreateWindowExA(
DWORD dwExStyle, // Extended window style
LPCSTR lpClassName, // Window class name
LPCSTR lpWindowName, // Window title (the title bar text)
DWORD dwStyle, // Style of the window
int x, // X-coordinate of the window
int y, // Y-coordinate of the window
int nWidth, // Width of the window
int nHeight, // Height of the window
HWND hWndParent, // Handle to the parent window
HMENU hMenu, // Handle to a menu
HINSTANCE hInstance, // Handle to the instance of the module
LPVOID lpParam // Pointer to additional parameters
);
This function creates a window with various customizable properties. To meet our objective, the title bar will display “Test”.
Example Implementation
Here’s a basic implementation of the CreateWindowExA
function in a C program:
#include <windows.h>
int main() {
// Registering the window class
const char* className = "MyWindowClass";
WNDCLASS wc = { 0 };
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = className;
RegisterClass(&wc);
// Creating the window
HWND hwnd = CreateWindowExA(
0, // Extended style
className, // Window class name
"Test", // Window title
WS_OVERLAPPEDWINDOW, // Window style
CW_USEDEFAULT, // X position
CW_USEDEFAULT, // Y position
500, // Width
300, // Height
NULL, // Parent window
NULL, // Menu
GetModuleHandle(NULL), // Application instance
NULL // Additional parameters
);
// Showing the window
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// Message loop
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
This code registers a window class, creates a window titled “Hello THM!”, and displays it on the screen. It also includes a basic message loop to keep the window running until it is closed by the user.
.NET and PowerShell API Implementations
P/Invoke allows managed code, such as C#, to call unmanaged functions from DLLs by importing the DLLs and mapping pointers to their API calls. Below is a simple example demonstrating how P/Invoke can be used to invoke a Windows API function.
Example Code
using System;
using System.Runtime.InteropServices;
class Program
{
// Import the MessageBox function from user32.dll
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
static void Main(string[] args)
{
// Call the MessageBox function
MessageBox(IntPtr.Zero, "Hello, THM!", "P/Invoke Example", 0);
}
}
Explanation of Components
DllImport
Attribute- The
DllImport
attribute is used to specify the DLL that contains the unmanaged function. - Parameters:
"user32.dll"
: Specifies the DLL from which the function is imported.CharSet
: Defines how string parameters are marshaled (e.g., ANSI or Unicode).SetLastError
: Indicates that the function sets the system’s last-error code, which can be retrieved usingMarshal.GetLastWin32Error()
.
- The
- Function Declaration
- The
MessageBox
function is declared with the same signature as in the unmanaged library. - Parameters:
IntPtr hWnd
: Handle to the owner window (useIntPtr.Zero
for no owner).string text
: The message to display.string caption
: The title of the message box.uint type
: Specifies the buttons and icons for the message box (e.g.,0
for a simple “OK” button).
- The
- Calling the Function
- The
MessageBox
function is invoked like any other method in C#. - In this example, it displays a message box with the text “Hello, THM!” and a title “P/Invoke Example.”
- The
Commonly Abused Win32 API Calls
Certain API calls within the Win32 library are often exploited for malicious purposes. Organizations like SANS and MalAPI.io have worked to document and categorize these calls to help identify and mitigate such threats.
Below is a table summarizing some of the most frequently abused API calls, categorized by their prevalence in real-world samples:
API Call | Description |
---|---|
VirtualAllocEx | Allocates memory in a remote process, commonly used for injecting malicious code. |
WriteProcessMemory | Writes data into the memory of a target process, often a step in process injection. |
CreateRemoteThread | Creates a thread in another process, frequently used to execute injected code. |
OpenProcess | Opens a handle to a target process, used for various malicious operations. |
LoadLibrary | Loads a DLL into the process’s address space, often abused for loading malicious libraries. |
GetProcAddress | Retrieves the address of a function in a DLL, used to dynamically resolve API calls. |
NtQuerySystemInformation | Gathers detailed system information, often leveraged for reconnaissance. |
SetWindowsHookEx | Installs a hook procedure to monitor or manipulate events, abused for keylogging. |
RegSetValueEx | Modifies the Windows registry, frequently used for persistence mechanisms. |
CreateFile | Creates or opens files, often exploited to access or manipulate sensitive data. |
Observations
- Memory Manipulation
Functions likeVirtualAllocEx
,WriteProcessMemory
, andCreateRemoteThread
are integral to process injection techniques, making them prime targets for abuse. - Dynamic API Resolution
LoadLibrary
andGetProcAddress
are commonly exploited to dynamically load malicious code or resolve function addresses without detection. - System and Process Control
Calls such asOpenProcess
andNtQuerySystemInformation
enable attackers to interact with and gather information about processes and the system. - Persistence and Monitoring
Functions likeSetWindowsHookEx
andRegSetValueEx
are used to maintain persistence or monitor user activity.
Malware Case Study
Keylogger
To analyze the keylogger, it’s crucial to identify the API calls and hooks it employs. Since the keylogger is written in C#, it uses P/Invoke to bridge managed code with the unmanaged Windows API.
Below is a snippet of the P/Invoke definitions from the malware’s source code:
using System;
using System.Runtime.InteropServices;
class KeyloggerAPI
{
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
// Delegate for the hook procedure
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
}
Key API Calls in the Snippet
SetWindowsHookEx
- Installs a hook procedure to monitor system events, such as keyboard or mouse input.
- Parameters:
idHook
: Specifies the type of hook (e.g., WH_KEYBOARD for keyboard hooks).lpfn
: Pointer to the hook procedure.hMod
: Handle to the DLL containing the hook procedure (optional for local hooks).dwThreadId
: Specifies the thread ID to monitor (or 0 for all threads).
- Abuse Potential: Often exploited in keyloggers to intercept and log keystrokes.
UnhookWindowsHookEx
- Removes a previously installed hook.
- Parameter:
hhk
: Handle to the hook to be removed.
CallNextHookEx
- Passes the hook information to the next hook procedure in the chain.
- Parameters:
hhk
: Handle to the current hook (can be null for global hooks).nCode
,wParam
,lParam
: Data about the event being processed.
GetModuleHandle
- Retrieves a handle to the specified module in the current process.
- Parameter:
lpModuleName
: Name of the module (passingnull
retrieves the handle of the current process).
Observations
- Hooks: The keylogger uses
SetWindowsHookEx
to establish hooks for monitoring keyboard input. - Process Interception:
CallNextHookEx
ensures proper event handling while intercepting key events. - Resource Management:
UnhookWindowsHookEx
is used to remove hooks, whileGetModuleHandle
helps in setting up the hook procedure.
By analyzing these definitions, we gain insight into the keylogger’s functionality and its reliance on the Windows API for implementing malicious hooks.
Shellcode Launcher
To analyze the shellcode launcher, the process involves identifying the API calls it implements. Like the keylogger analysis, this malware sample also uses P/Invoke to interact with the unmanaged Windows API.
Below is a snippet of the P/Invoke definitions from the shellcode launcher’s source code:
using System;
using System.Runtime.InteropServices;
class ShellcodeLauncherAPI
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
}
Key API Calls in the Snippet
VirtualAlloc
- Allocates memory in the process’s virtual address space.
- Parameters:
lpAddress
: Base address of the region (optional;IntPtr.Zero
for system-determined address).dwSize
: Size of the memory allocation.flAllocationType
: Specifies the type of allocation (e.g.,MEM_COMMIT
orMEM_RESERVE
).flProtect
: Protection for the memory region (e.g.,PAGE_EXECUTE_READWRITE
).
- Abuse Potential: Used to allocate memory for storing shellcode.
CreateThread
- Creates a new thread in the process.
- Parameters:
lpThreadAttributes
: Security attributes for the thread.dwStackSize
: Initial stack size.lpStartAddress
: Pointer to the thread’s starting function (e.g., the shellcode).lpParameter
: Pointer to arguments passed to the thread.dwCreationFlags
: Thread creation options (e.g.,0
for default).lpThreadId
: Outputs the thread identifier.
- Abuse Potential: Often used to execute shellcode in a new thread.
WaitForSingleObject
- Waits for a specified object (e.g., thread) to enter a signaled state or timeout.
- Parameters:
hHandle
: Handle to the object (e.g., thread created withCreateThread
).dwMilliseconds
: Time to wait (e.g.,INFINITE
for no timeout).
- Abuse Potential: Ensures the shellcode thread completes execution before the process ends.
VirtualProtect
- Changes the protection of a region of memory.
- Parameters:
lpAddress
: Base address of the memory region.dwSize
: Size of the memory region.flNewProtect
: New protection options (e.g.,PAGE_EXECUTE_READWRITE
).lpflOldProtect
: Outputs the previous protection settings.
- Abuse Potential: Used to modify memory protections, enabling shellcode execution.
Observations
- Memory Manipulation: Functions like
VirtualAlloc
andVirtualProtect
are used to allocate and prepare memory for executing shellcode. - Thread Management:
CreateThread
launches the shellcode in a new thread, whileWaitForSingleObject
synchronizes its execution. - Process Execution: These API calls collectively enable the injection, preparation, and execution of shellcode in a process.
By analyzing these definitions, it becomes evident that the shellcode launcher leverages API calls focused on memory and thread manipulation, common tactics in malware execution.
Introduction to Windows API TryHackMe | Room Answers
What header file imports and defines the User32 DLL and structure?
winuser.h
What parent header file contains all other required child and core header files?
windows.h
Does a process in the user mode have direct hardware access? (Y/N)
N
Does launching an application as an administrator open the process in kernel mode? (Y/N)
N
What overarching namespace provides P/Invoke to .NET?
system
What memory protection solution obscures the process of importing API calls?
ASLR
Which character appended to an API call represents an ANSI encoding?
A
Which character appended to an API call represents extended functionality?
Ex
What is the memory allocation type of 0x00080000 in the VirtualAlloc API call?
MEM_RESET
Do you need to define a structure to use API calls in C? (Y/N)
N
What method is used to import a required DLL?
DllImport
What type of method is used to reference the API call to obtain a struct?
External
Which API call returns the address of an exported DLL function?
GetProcAddress
Which API call imports a specified DLL into the address space of the calling process?
LoadLibrary
What Win32 API call is used to obtain a pseudo handle of our current process in the keylogger sample?
GetCurrentProcess
What Win32 API call is used to set a hook on our current process in the keylogger sample?
SetWindowsHookEx
What Win32 API call is used to obtain a handle from the pseudo handle in the keylogger sample?
GetModuleHandle
What Win32 API call is used unset the hook on our current process in the keylogger sample?
UnhookWindowsHookEx
What Win32 API call is used to allocate memory for the size of the shellcode in the shellcode launcher sample?
VirtualAlloc
What native method is used to write shellcode to an allocated section of memory in the shellcode launcher sample?
Marshal.copy
What Win32 API call is used to create a new execution thread in the shellcode launcher sample?
CreateThread
What Win32 API call is used to wait for the thread to exit in the shellcode launcher sample?
WaitForSingleObject