Introduction to Windows API | TryHackMe Walkthrough

Introduction to Windows API | TryHackMe Walkthrough

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.

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.

LayerExplanationAPIA top-level/general term or theory used to describe any call found in the win32 API structure.Header files or importsDefines libraries to be imported at run-time, defined by header files or library imports. Uses pointers to obtain the function address.Core DLLsA 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 DLLsOther DLLs defined as part of the Windows API. Controls separate subsystems of the Windows OS. ~36 other defined DLLs. (NTDLL, COM, FVEAPI, etc.)Call StructuresDefines the API call itself and parameters of the call.API CallsThe API call used within a program, with function addresses obtained from pointers.In/Out ParametersThe 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:

CharacterDescriptionAIndicates ANSI (American National Standards Institute) encoding for string parameters.WIndicates Unicode encoding for string parameters.ExRepresents an “extended” version of a standard function with additional features.32Refers 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 using Marshal.GetLastWin32Error().

Function Declaration

  • The MessageBox function is declared with the same signature as in the unmanaged library.
  • Parameters:
  • IntPtr hWnd: Handle to the owner window (use IntPtr.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).

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.”

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 CallDescriptionVirtualAllocExAllocates memory in a remote process, commonly used for injecting malicious code.WriteProcessMemoryWrites data into the memory of a target process, often a step in process injection.CreateRemoteThreadCreates a thread in another process, frequently used to execute injected code.OpenProcessOpens a handle to a target process, used for various malicious operations.LoadLibraryLoads a DLL into the process’s address space, often abused for loading malicious libraries.GetProcAddressRetrieves the address of a function in a DLL, used to dynamically resolve API calls.NtQuerySystemInformationGathers detailed system information, often leveraged for reconnaissance.SetWindowsHookExInstalls a hook procedure to monitor or manipulate events, abused for keylogging.RegSetValueExModifies the Windows registry, frequently used for persistence mechanisms.CreateFileCreates or opens files, often exploited to access or manipulate sensitive data.

Observations

  1. Memory Manipulation Functions like VirtualAllocEx, WriteProcessMemory, and CreateRemoteThread are integral to process injection techniques, making them prime targets for abuse.
  2. Dynamic API Resolution LoadLibrary and GetProcAddress are commonly exploited to dynamically load malicious code or resolve function addresses without detection.
  3. System and Process Control Calls such as OpenProcess and NtQuerySystemInformation enable attackers to interact with and gather information about processes and the system.
  4. Persistence and Monitoring Functions like SetWindowsHookEx and RegSetValueEx 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 (passing null 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, while GetModuleHandle 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 or MEM_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 with CreateThread).

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 and VirtualProtect are used to allocate and prepare memory for executing shellcode.
  • Thread Management: CreateThread launches the shellcode in a new thread, while WaitForSingleObject 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

Room answers can be found here.

Check out also


要查看或添加评论,请登录