Windows API Programming with C++ Introduction

This section shows code snippets that use the Windows API to do the following:

  • Allocate and manipulate memory using VirtualAllocVirtualFree, and related calls
  • Create, suspend, and terminate processes with CreateProcessTerminateProcess, etc.
  • Interact with the file system using CreateFileReadFile, and WriteFile
  • Work with handles and native data types like HANDLEDWORDLPVOIDLPCSTR, and more
  • Display native dialog boxes like MessageBox using User32.dll
  • Understand basic Windows memory models and error handling with GetLastError

By learning to use the API directly, you’ll gain low-level control and unlock capabilities that are simply not possible with higher-level abstractions.

Example Basics

Example 1: MessageBox with MessageBoxA

#include <windows.h> // Required for all WinAPI calls

int main() {
    // Display a simple message box with a message, title, and OK button
    MessageBoxA(
        NULL,                     // No parent window
        "Hello from WinAPI!",     // Message text
        "MessageBox Example",     // Window title
        MB_OK | MB_ICONINFORMATION // Button and icon type
    );

    return 0;
}

Concepts:

  • GUI interaction
  • Calling User32.dll via MessageBoxA
  • WinAPI flags for dialog styles

Example 2: Creating and Writing to a File

#include <windows.h>
#include <iostream>

int main() {
    // Create or open a file for writing
    HANDLE hFile = CreateFileA(
        "example.txt",             // File name
        GENERIC_WRITE,             // Desired access
        0,                         // Share mode
        NULL,                      // Security attributes
        CREATE_ALWAYS,             // Creation disposition
        FILE_ATTRIBUTE_NORMAL,     // Flags and attributes
        NULL                       // Template file
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to create file. Error: " << GetLastError() << std::endl;
        return 1;
    }

    const char* data = "Hello, WinAPI file I/O!";
    DWORD bytesWritten;

    // Write data to the file
    BOOL success = WriteFile(
        hFile,
        data,
        strlen(data),
        &bytesWritten,
        NULL
    );

    if (!success) {
        std::cerr << "Write failed. Error: " << GetLastError() << std::endl;
    } else {
        std::cout << "Successfully wrote " << bytesWritten << " bytes." << std::endl;
    }

    CloseHandle(hFile); // Always close handles

    return 0;
}

Concepts:

  • File creation
  • File writing
  • HANDLEDWORD, and WinAPI error handling

Example 3: Allocating Memory with VirtualAlloc

#include <windows.h>
#include <iostream>

int main() {
    // Allocate 1 KB of memory with read/write permissions
    LPVOID mem = VirtualAlloc(
        NULL,                      // Let the OS choose the address
        1024,                      // Size in bytes
        MEM_COMMIT | MEM_RESERVE, // Allocation type
        PAGE_READWRITE             // Protection
    );

    if (mem == NULL) {
        std::cerr << "Memory allocation failed. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // Write data into the allocated memory
    strcpy_s((char*)mem, 1024, "This is dynamic memory via WinAPI.");

    // Output the content
    std::cout << "Memory content: " << (char*)mem << std::endl;

    // Free the memory
    VirtualFree(mem, 0, MEM_RELEASE);

    return 0;
}

Concepts:

  • Manual memory allocation
  • Understanding MEM_COMMITMEM_RESERVE, and PAGE_READWRITE
  • Memory cleanup

Example 4: Getting System Information with GetSystemInfo

#include <windows.h>
#include <iostream>

int main() {
    SYSTEM_INFO sysInfo;

    // Populate the SYSTEM_INFO structure
    GetSystemInfo(&sysInfo);

    std::cout << "Processor Architecture: " << sysInfo.wProcessorArchitecture << std::endl;
    std::cout << "Page Size: " << sysInfo.dwPageSize << " bytes" << std::endl;
    std::cout << "Number of Processors: " << sysInfo.dwNumberOfProcessors << std::endl;

    return 0;
}

Concepts:

  • Retrieving system-level information
  • SYSTEM_INFO structure
  • Hardware metadata from the OS

Example 5: Sleep and Beep (Timing and Sound)

#include <windows.h>

int main() {
    // Pause execution for 2 seconds
    Sleep(2000); // Milliseconds

    // Play a beep sound (frequency 750 Hz, duration 300 ms)
    Beep(750, 300);

    return 0;
}

Concepts:

  • Timing control using Sleep
  • Basic interaction with system audio via Beep

Example Intermediate

Example 6: Creating a New Process with CreateProcessA

#include <windows.h>
#include <iostream>

int main() {
    STARTUPINFOA si = { sizeof(si) };  // Initialize STARTUPINFO
    PROCESS_INFORMATION pi;           // Will receive process info

    // Path to executable (must exist)
    LPCSTR appName = "C:\\Windows\\System32\\notepad.exe";

    // Create a new process (Notepad in this case)
    BOOL success = CreateProcessA(
        appName,       // Application name
        NULL,          // Command line
        NULL,          // Process security attributes
        NULL,          // Thread security attributes
        FALSE,         // Inherit handles
        0,             // Creation flags
        NULL,          // Environment
        NULL,          // Current directory
        &si,           // Startup info
        &pi            // Process info (out)
    );

    if (!success) {
        std::cerr << "CreateProcess failed. Error: " << GetLastError() << std::endl;
        return 1;
    }

    std::cout << "Process created! PID: " << pi.dwProcessId << std::endl;

    // Wait until the process exits
    WaitForSingleObject(pi.hProcess, INFINITE);

    // Close handles
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    return 0;
}

Concepts:

  • Process creation
  • STARTUPINFO and PROCESS_INFORMATION
  • Process handles and cleanup

Example 7: Getting the Current Process and Thread IDs

#include <windows.h>
#include <iostream>

int main() {
    DWORD pid = GetCurrentProcessId();
    DWORD tid = GetCurrentThreadId();

    std::cout << "Current Process ID: " << pid << std::endl;
    std::cout << "Current Thread ID: " << tid << std::endl;

    return 0;
}

Concepts:

  • Process and thread identity
  • Use in diagnostics, logging, or monitoring tools

Example 8: Reading a File with WinAPI

#include <windows.h>
#include <iostream>

int main() {
    HANDLE hFile = CreateFileA(
        "example.txt",           // File to open
        GENERIC_READ,            // Desired access
        FILE_SHARE_READ,         // Share mode
        NULL,                    // Security attributes
        OPEN_EXISTING,           // Open only if exists
        FILE_ATTRIBUTE_NORMAL,   // Attributes
        NULL                     // Template
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        std::cerr << "Could not open file. Error: " << GetLastError() << std::endl;
        return 1;
    }

    char buffer[128] = {0};
    DWORD bytesRead;

    BOOL success = ReadFile(
        hFile,
        buffer,
        sizeof(buffer) - 1,
        &bytesRead,
        NULL
    );

    if (!success) {
        std::cerr << "Read failed. Error: " << GetLastError() << std::endl;
    } else {
        std::cout << "Read " << bytesRead << " bytes: " << buffer << std::endl;
    }

    CloseHandle(hFile);

    return 0;
}

Concepts:

  • File reading with low-level control
  • Buffer management and safe output

Example 9: Using OpenProcess to Access Another Process

#include <windows.h>
#include <iostream>

int main() {
    DWORD pid;
    std::cout << "Enter the PID of the process to open: ";
    std::cin >> pid;

    // Open another process with read-only permissions
    HANDLE hProcess = OpenProcess(
        PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
        FALSE,
        pid
    );

    if (hProcess == NULL) {
        std::cerr << "Failed to open process. Error: " << GetLastError() << std::endl;
        return 1;
    }

    std::cout << "Process opened successfully (handle: " << hProcess << ")." << std::endl;

    CloseHandle(hProcess);
    return 0;
}

Concepts:

  • Accessing external processes
  • Required permissions
  • Handle management

Example 10: Creating a Thread with CreateThread

#include <windows.h>
#include <iostream>

// Thread function must match signature
DWORD WINAPI MyThreadFunction(LPVOID lpParam) {
    std::cout << "Thread running... ID: " << GetCurrentThreadId() << std::endl;
    Sleep(1000);
    return 0;
}

int main() {
    HANDLE hThread;
    DWORD threadId;

    // Create a new thread
    hThread = CreateThread(
        NULL,              // Default security
        0,                 // Default stack size
        MyThreadFunction,  // Thread function
        NULL,              // Parameter to thread
        0,                 // Default creation flags
        &threadId          // Receives thread identifier
    );

    if (hThread == NULL) {
        std::cerr << "Thread creation failed. Error: " << GetLastError() << std::endl;
        return 1;
    }

    std::cout << "Thread created. ID: " << threadId << std::endl;

    // Wait for thread to finish
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);

    return 0;
}

Concepts:

  • Thread creation
  • Function signature for WinAPI threads
  • Use of CreateThread and synchronization via WaitForSingleObject

Example Advanced

Example 11: Reading Memory from Another Process

#include <windows.h>
#include <iostream>

int main() {
    DWORD pid;
    std::cout << "Enter target PID: ";
    std::cin >> pid;

    HANDLE hProcess = OpenProcess(
        PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
        FALSE,
        pid
    );

    if (!hProcess) {
        std::cerr << "Failed to open process. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // Example address (needs to be valid in target process)
    LPCVOID address = (LPCVOID)0x7FFDF000;
    char buffer[32] = { 0 };
    SIZE_T bytesRead;

    if (ReadProcessMemory(hProcess, address, buffer, sizeof(buffer), &bytesRead)) {
        std::cout << "Data read (" << bytesRead << " bytes): " << buffer << std::endl;
    } else {
        std::cerr << "ReadProcessMemory failed. Error: " << GetLastError() << std::endl;
    }

    CloseHandle(hProcess);
    return 0;
}

Concepts:

  • Cross-process memory access
  • ReadProcessMemory and pointer management
  • Requires knowledge of valid memory regions

Example 12: Allocating Memory in Another Process

#include <windows.h>
#include <iostream>

int main() {
    DWORD pid;
    std::cout << "Enter PID of target process: ";
    std::cin >> pid;

    HANDLE hProcess = OpenProcess(
        PROCESS_VM_OPERATION | PROCESS_VM_WRITE,
        FALSE,
        pid
    );

    if (!hProcess) {
        std::cerr << "OpenProcess failed. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // Allocate memory in remote process
    LPVOID remoteMemory = VirtualAllocEx(
        hProcess,
        NULL,
        1024,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE
    );

    if (!remoteMemory) {
        std::cerr << "VirtualAllocEx failed. Error: " << GetLastError() << std::endl;
        CloseHandle(hProcess);
        return 1;
    }

    std::cout << "Memory allocated at remote address: " << remoteMemory << std::endl;

    CloseHandle(hProcess);
    return 0;
}

Concepts:

  • Remote memory allocation (VirtualAllocEx)
  • Cross-process permissions
  • Critical step for advanced injection techniques

Example 13: Writing to Another Process's Memory

#include <windows.h>
#include <iostream>

int main() {
    DWORD pid;
    std::cout << "Enter target PID: ";
    std::cin >> pid;

    HANDLE hProcess = OpenProcess(
        PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
        FALSE,
        pid
    );

    if (!hProcess) {
        std::cerr << "OpenProcess failed. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // Allocate memory
    LPVOID remoteAddr = VirtualAllocEx(hProcess, NULL, 256, MEM_COMMIT, PAGE_READWRITE);
    const char* message = "Hello from C++ to your memory!";

    SIZE_T bytesWritten;
    if (WriteProcessMemory(hProcess, remoteAddr, message, strlen(message) + 1, &bytesWritten)) {
        std::cout << "Successfully wrote " << bytesWritten << " bytes to remote process." << std::endl;
    } else {
        std::cerr << "WriteProcessMemory failed. Error: " << GetLastError() << std::endl;
    }

    CloseHandle(hProcess);
    return 0;
}

Concepts:

  • Remote memory writing
  • WriteProcessMemory
  • Must ensure correct permissions and valid memory region

Example 14: Changing Memory Protection with VirtualProtect

#include <windows.h>
#include <iostream>

int main() {
    // Allocate memory
    LPVOID mem = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
    if (!mem) {
        std::cerr << "VirtualAlloc failed. Error: " << GetLastError() << std::endl;
        return 1;
    }

    // Write something
    strcpy_s((char*)mem, 1024, "Testing memory protection.");

    // Change to read-only
    DWORD oldProtect;
    if (VirtualProtect(mem, 1024, PAGE_READONLY, &oldProtect)) {
        std::cout << "Memory protection changed to read-only." << std::endl;
    } else {
        std::cerr << "VirtualProtect failed. Error: " << GetLastError() << std::endl;
    }

    // Free memory
    VirtualFree(mem, 0, MEM_RELEASE);

    return 0;
}

Concepts:

  • Memory region protection
  • PAGE_READWRITE to PAGE_READONLY
  • Useful for enforcing execution policies or memory safety

Example 15: Getting the Address of an Exported Function in a DLL

#include <windows.h>
#include <iostream>

int main() {
    HMODULE hKernel32 = LoadLibraryA("kernel32.dll");
    if (!hKernel32) {
        std::cerr << "Failed to load kernel32.dll" << std::endl;
        return 1;
    }

    // Get address of 'Beep' function from kernel32.dll
    FARPROC funcAddr = GetProcAddress(hKernel32, "Beep");

    if (funcAddr) {
        std::cout << "'Beep' function address: " << funcAddr << std::endl;
    } else {
        std::cerr << "GetProcAddress failed. Error: " << GetLastError() << std::endl;
    }

    // Always free loaded libraries (if dynamically loaded)
    FreeLibrary(hKernel32);

    return 0;
}

Concepts:

  • Dynamic API resolution
  • Use of LoadLibrary and GetProcAddress
  • Essential for techniques like DLL injection or manual mapping