← Back to all projects

LEARN WIN32 API DEEP DIVE

Learn Win32 API: From Zero to Windows System Programmer

Goal: Deeply understand Windows system programming through the Win32 API—from basic handle operations to building real system tools that interact directly with the Windows kernel.


Why Win32 API Matters

Every Windows application—from Notepad to Visual Studio—ultimately calls Win32 APIs. Frameworks like .NET, Qt, and Electron are just abstractions over these same APIs. When you understand Win32, you understand Windows itself.

After completing these projects, you will:

  • Understand the difference between handles and pointers (and why Windows chose handles)
  • Know how Windows manages memory, processes, threads, and files
  • Be able to build real system utilities that work at the OS level
  • Debug any Windows application by understanding what’s happening underneath
  • Write high-performance I/O code using overlapped operations
  • Understand DLL loading, process injection, and security boundaries

Core Concept Analysis

The Windows Object Model

User Mode                          Kernel Mode
-----------                        -----------
HANDLE ──────────────────────────► Kernel Object
  │                                    │
  │  (Handle Table)                    │
  │  Process-specific                  │
  │  index into kernel                 │
  │  object table                      │
  │                                    ▼
  │                              ┌─────────────┐
  └──────────────────────────────│ Object Body │
                                 │ - Type      │
                                 │ - Security  │
                                 │ - RefCount  │
                                 └─────────────┘

Fundamental Concepts

  1. Handles vs. Pointers
    • Pointers: Direct memory addresses—if you have it, you can access it
    • Handles: Opaque indices into a per-process table maintained by the kernel
    • Why handles?: Security (kernel validates access), abstraction (kernel can move objects), process isolation (handles are per-process)
  2. The Win32 API Pattern
    // 1. Create/Open an object → get a HANDLE
    HANDLE hFile = CreateFile(...);
    
    // 2. Check for errors
    if (hFile == INVALID_HANDLE_VALUE) {
        DWORD error = GetLastError();  // Error code
        // Handle error
    }
    
    // 3. Use the handle with other APIs
    ReadFile(hFile, buffer, size, &bytesRead, NULL);
    
    // 4. Clean up
    CloseHandle(hFile);
    
  3. Error Handling
    • GetLastError(): Returns last error for current thread (per-thread error code)
    • HRESULT: COM-style error codes (32-bit: severity, facility, code)
    • NTSTATUS: Native API error codes (used by kernel)
    • FormatMessage(): Convert error codes to human-readable text
  4. Unicode in Windows
    • Windows uses UTF-16 internally (2 bytes per character, 4 for supplementary)
    • W suffix: Unicode version (CreateFileW)—use these
    • A suffix: ANSI version (CreateFileA)—legacy, avoid
    • TCHAR: Macro that resolves to wchar_t with UNICODE defined
    • Recommendation: Always use W APIs directly, forget TCHAR exists
  5. Core Object Types | Object Type | Creation API | Description | |————-|————–|————-| | File | CreateFile | Files, directories, devices, pipes | | Process | CreateProcess | Running program instance | | Thread | CreateThread | Execution context within process | | Event | CreateEvent | Synchronization primitive | | Mutex | CreateMutex | Mutual exclusion | | Semaphore | CreateSemaphore | Resource counting | | File Mapping | CreateFileMapping | Shared memory | | Registry Key | RegOpenKeyEx | Registry access | | Heap | HeapCreate | Memory allocation arena |

  6. Memory Architecture
    ┌───────────────────────────────┐ 0xFFFFFFFF (4GB on 32-bit)
    │      Kernel Space             │ (Not directly accessible)
    ├───────────────────────────────┤ 0x80000000 (typically)
    │      Stack (grows down)       │
    ├───────────────────────────────┤
    │           ↓                   │
    │      (Free space)             │
    │           ↑                   │
    ├───────────────────────────────┤
    │      Heap(s) (grows up)       │
    ├───────────────────────────────┤
    │      DLLs                     │
    ├───────────────────────────────┤
    │      Executable Image         │
    ├───────────────────────────────┤
    │      Reserved/NULL            │
    └───────────────────────────────┘ 0x00000000
    

Project List

Projects are ordered from fundamental understanding to advanced implementations.


Project 1: Win32 Error Message Lookup Tool

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust (windows-rs)
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 1: Beginner
  • Knowledge Area: Error Handling / Win32 Fundamentals
  • Software or Tool: Windows Error Lookup
  • Main Book: “Windows via C/C++” by Jeffrey Richter

What you’ll build: A command-line tool that takes Win32 error codes (like 5, 2, 1314) or HRESULT values and displays the human-readable message, category, and common causes.

Why it teaches Win32: This is the first thing every Windows developer needs—understanding error codes. You’ll learn FormatMessage, the difference between Win32 errors and HRESULT, and how Windows reports problems.

Core challenges you’ll face:

  • Understanding error code formats → maps to Win32 vs HRESULT vs NTSTATUS
  • Using FormatMessage with different flags → maps to message table lookups
  • Handling system vs custom message sources → maps to DLL message resources
  • Unicode output to console → maps to wide character handling

Key Concepts:

  • GetLastError/SetLastError: “Windows via C/C++” Chapter 1 - Jeffrey Richter
  • HRESULT structure: MSDN “Structure of COM Error Codes”
  • FormatMessage API: “Windows System Programming” Chapter 2 - Johnson Hart
  • Console Unicode: “Windows Internals Part 1” Appendix - Russinovich

Difficulty: Beginner Time estimate: Weekend Prerequisites: Basic C, ability to compile on Windows (Visual Studio or MinGW)

Real world outcome:

C:\> errcode 5
Win32 Error Code: 5 (0x00000005)
Message: Access is denied.
Category: Security/Permissions
Common causes:
  - Trying to access a file without proper permissions
  - Running as non-admin when admin rights needed
  - File is locked by another process

C:\> errcode 0x80070005
HRESULT: 0x80070005
Severity: Error
Facility: WIN32 (7)
Code: 5
Message: Access is denied.

C:\> errcode 0xC0000005
NTSTATUS: 0xC0000005
Message: Access violation
This is a memory access violation (read/write to invalid address)

Implementation Hints:

FormatMessage is the key API:

FormatMessage(
    FORMAT_MESSAGE_FROM_SYSTEM |    // Look up in system message table
    FORMAT_MESSAGE_ALLOCATE_BUFFER, // Let Windows allocate the buffer
    NULL,                           // Source (NULL for system)
    errorCode,                      // The error code to look up
    0,                              // Language (0 = default)
    (LPWSTR)&messageBuffer,         // Output buffer
    0,                              // Min size
    NULL                            // Arguments for format strings
);

For HRESULT decoding, remember the structure:

  • Bits 31: Severity (0=success, 1=error)
  • Bits 30-29: Reserved
  • Bits 28-16: Facility code (identifies subsystem)
  • Bits 15-0: Error code

Questions to answer yourself:

  • What happens when you call GetLastError() twice in a row?
  • Why does SetLastError(0) exist?
  • How do you get error messages from a specific DLL?

Learning milestones:

  1. You decode Win32 errors correctly → You understand thread-local error storage
  2. You parse HRESULT components → You understand COM error architecture
  3. You format messages with arguments → You understand Windows message tables
  4. Console shows Unicode correctly → You understand Windows text encoding

Project 2: File Copy Utility with Progress

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 1: Beginner
  • Knowledge Area: File I/O / Handle Management
  • Software or Tool: Custom File Copy Tool
  • Main Book: “Windows System Programming” by Johnson M. Hart

What you’ll build: A command-line file copy utility that shows real-time progress, handles large files (>4GB), and properly reports errors—essentially a simple version of robocopy.

Why it teaches Win32: File I/O is the most fundamental Win32 operation. You’ll learn CreateFile (which is misnamed—it opens everything), handle management, and the ReadFile/WriteFile pattern that applies to all I/O.

Core challenges you’ll face:

  • Opening files with correct flags → maps to access modes, share modes, disposition
  • Reading/writing in chunks → maps to buffer management, large file handling
  • Getting file size for progress → maps to GetFileSizeEx, 64-bit file sizes
  • Handling errors mid-copy → maps to cleanup, resource management

Key Concepts:

  • CreateFile parameters: “Windows System Programming” Chapter 2 - Johnson Hart
  • Buffered I/O strategies: “Windows via C/C++” Chapter 10 - Jeffrey Richter
  • Handle cleanup patterns: “Effective C++” Item 13 (RAII concept) - Scott Meyers
  • Large file support: MSDN “File Pointers” documentation

Difficulty: Beginner Time estimate: Weekend Prerequisites: Project 1 completed, basic file concepts

Real world outcome:

C:\> mycopy source.iso D:\backup\source.iso
Copying: source.iso
Size: 4.7 GB

[████████████████████░░░░░░░░░░] 68% | 3.2 GB / 4.7 GB | 125 MB/s | ETA: 12s

Copy completed successfully!
Source: C:\source.iso (4,700,000,000 bytes)
Dest:   D:\backup\source.iso (4,700,000,000 bytes)
Time:   38 seconds
Speed:  123.7 MB/s

C:\> mycopy readonly.txt D:\test.txt
Error copying file:
  Source: C:\readonly.txt
  Error: 32 (ERROR_SHARING_VIOLATION)
  Message: The process cannot access the file because it is being used
           by another process.

Implementation Hints:

CreateFile is the gateway to all I/O in Windows. Despite its name, it doesn’t just create files—it opens files, directories, devices, pipes, and more.

HANDLE hFile = CreateFileW(
    L"path\\to\\file",      // File path (Unicode)
    GENERIC_READ,            // Desired access (GENERIC_READ, GENERIC_WRITE, etc.)
    FILE_SHARE_READ,         // Share mode (who else can access while we have it open)
    NULL,                    // Security attributes
    OPEN_EXISTING,           // Creation disposition (CREATE_NEW, OPEN_EXISTING, etc.)
    FILE_ATTRIBUTE_NORMAL,   // Flags and attributes
    NULL                     // Template file
);

Key questions to answer:

  • What’s the difference between GENERIC_READ and FILE_GENERIC_READ?
  • Why would you use FILE_SHARE_READ vs 0 for share mode?
  • What does FILE_FLAG_SEQUENTIAL_SCAN do and when should you use it?
  • How do you handle files larger than 4GB (hint: LARGE_INTEGER)?

Buffer size matters for performance:

  • Too small (4KB): Many system calls, slow
  • Too large (1GB): Memory waste, potential allocation failure
  • Sweet spot: 64KB-1MB typically

Learning milestones:

  1. Basic copy works → You understand CreateFile/ReadFile/WriteFile/CloseHandle
  2. Progress shows correctly → You understand file size queries and position tracking
  3. Large files (>4GB) work → You understand 64-bit file operations
  4. Errors handled gracefully → You understand proper cleanup and error reporting

Project 3: Directory Tree Walker

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 1: Beginner
  • Knowledge Area: File System / Directory Enumeration
  • Software or Tool: Directory Analyzer Tool
  • Main Book: “Windows System Programming” by Johnson M. Hart

What you’ll build: A tool that recursively walks a directory tree, displaying the structure and calculating sizes—like tree /F combined with disk usage analysis.

Why it teaches Win32: Directory enumeration uses a different pattern than file I/O—the FindFirstFile/FindNextFile/FindClose trilogy. You’ll also encounter WIN32_FIND_DATA, file attributes, and the peculiarities of Windows paths.

Core challenges you’ll face:

  • Understanding FindFirstFile pattern → maps to iterator-style enumeration
  • Handling “.” and “..” entries → maps to directory traversal edge cases
  • Building paths correctly → maps to path manipulation, MAX_PATH
  • Dealing with symbolic links and junctions → maps to reparse points

Key Concepts:

  • FindFirstFile/FindNextFile: “Windows System Programming” Chapter 3 - Johnson Hart
  • WIN32_FIND_DATA structure: MSDN documentation
  • Path handling: “Windows via C/C++” Chapter 15 - Jeffrey Richter
  • File attributes and reparse points: “Windows Internals Part 2” Chapter 12 - Russinovich

Difficulty: Beginner Time estimate: Weekend Prerequisites: Project 2 completed

Real world outcome:

C:\> dirtree C:\Windows\System32\drivers

C:\Windows\System32\drivers
├── etc
│   ├── hosts                           1.2 KB
│   ├── networks                        0.4 KB
│   └── services                       17.5 KB
│   └── [3 files, 19.1 KB]
├── UMDF
│   ├── en-US
│   │   └── WpdMtpDr.dll.mui            3.0 KB
│   └── WpdMtpDr.dll                   97.0 KB
│   └── [2 files, 100.0 KB]
├── acpi.sys                          234.5 KB
├── ntfs.sys                            2.1 MB
└── ...

Summary:
  Directories:  156
  Files:        1,847
  Total Size:   234.7 MB
  Largest:      ntfs.sys (2.1 MB)

Implementation Hints:

The enumeration pattern:

WIN32_FIND_DATAW findData;
HANDLE hFind = FindFirstFileW(L"C:\\path\\*", &findData);

if (hFind == INVALID_HANDLE_VALUE) {
    // Handle error or empty directory
}

do {
    // Skip "." and ".."
    if (wcscmp(findData.cFileName, L".") == 0 ||
        wcscmp(findData.cFileName, L"..") == 0) {
        continue;
    }

    if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        // It's a directory - recurse
    } else {
        // It's a file - process it
    }
} while (FindNextFileW(hFind, &findData));

FindClose(hFind);

Questions to think about:

  • Why does the search pattern end with “” not “.*”?
  • What’s the difference between FILE_ATTRIBUTE_DIRECTORY and FILE_ATTRIBUTE_REPARSE_POINT?
  • How do you avoid infinite loops with symbolic links?
  • What’s MAX_PATH and how do you handle longer paths (hint: “\\?\” prefix)?

Learning milestones:

  1. Basic tree display works → You understand FindFirst/FindNext pattern
  2. Sizes accumulate correctly → You understand recursive algorithms with Windows APIs
  3. Handles junction points → You understand reparse points
  4. Works with long paths → You understand path limitations and workarounds

Project 4: Registry Explorer CLI

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 1: Beginner
  • Knowledge Area: Registry / Configuration Storage
  • Software or Tool: Registry CLI Tool
  • Main Book: “Windows Internals Part 1” by Russinovich, Solomon, and Ionescu

What you’ll build: A command-line registry editor that can query, add, modify, and delete registry values—a scriptable alternative to regedit.

Why it teaches Win32: The registry is Windows’ hierarchical configuration database. Registry APIs (RegOpenKeyEx, RegQueryValueEx, etc.) follow patterns similar to file I/O but with their own quirks around data types and access rights.

Core challenges you’ll face:

  • Understanding registry structure → maps to hives, keys, values, types
  • Handling different value types → maps to REG_SZ, REG_DWORD, REG_BINARY, etc.
  • Registry access rights → maps to security and elevation
  • 32-bit vs 64-bit registry views → maps to WoW64 redirection

Key Concepts:

  • Registry architecture: “Windows Internals Part 1” Chapter 4 - Russinovich
  • Registry APIs: “Windows System Programming” Chapter 4 - Johnson Hart
  • WoW64 Registry Reflection: MSDN “Registry Keys Affected by WOW64”
  • Registry security: “Windows Security Internals” Chapter 7 - Forshaw

Difficulty: Beginner Time estimate: Weekend Prerequisites: Basic understanding of handles from earlier projects

Real world outcome:

C:\> regcli query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion

ProductName     REG_SZ      Windows 10 Pro
EditionID       REG_SZ      Professional
CurrentBuild    REG_SZ      19045
UBR             REG_DWORD   3693

C:\> regcli query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion /v ProductName
Windows 10 Pro

C:\> regcli add HKCU\SOFTWARE\MyApp /v Setting1 /t REG_DWORD /d 42
Successfully created value 'Setting1'

C:\> regcli export HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion > output.reg

C:\> regcli query HKLM\SOFTWARE\Microsoft /s | head -20
Microsoft
├── .NETFramework
│   ├── v2.0.50727
│   ├── v4.0.30319
│   └── ...
├── Windows
│   ├── CurrentVersion
│   │   ├── Run
│   │   ├── Uninstall
│   │   └── ...

Implementation Hints:

Registry APIs follow the handle pattern:

HKEY hKey;
LONG result = RegOpenKeyExW(
    HKEY_LOCAL_MACHINE,           // Predefined root key
    L"SOFTWARE\\Microsoft",       // Subkey path
    0,                            // Options
    KEY_READ,                     // Desired access
    &hKey                         // Output handle
);

if (result != ERROR_SUCCESS) {
    // Handle error - note: registry uses LONG, not GetLastError
}

// Enumerate values
DWORD index = 0;
WCHAR valueName[256];
DWORD valueNameLen = 256;
DWORD type;
BYTE data[1024];
DWORD dataSize = 1024;

while (RegEnumValueW(hKey, index, valueName, &valueNameLen,
                     NULL, &type, data, &dataSize) == ERROR_SUCCESS) {
    // Process value based on type
    index++;
    valueNameLen = 256;  // Reset for next iteration
    dataSize = 1024;
}

RegCloseKey(hKey);

Important questions:

  • Why do predefined keys (HKEY_LOCAL_MACHINE) not need to be closed?
  • What happens if you open a 32-bit registry view from a 64-bit app?
  • How do you read a REG_MULTI_SZ (multi-string) value?
  • Why would RegQueryValueEx return ERROR_MORE_DATA?

Learning milestones:

  1. Can read registry values → You understand RegOpenKeyEx/RegQueryValueEx
  2. Handles all data types → You understand REG_* type system
  3. Recursive enumeration works → You understand RegEnumKeyEx/RegEnumValue
  4. Modify operations succeed → You understand registry write permissions

Project 5: Process Lister (Task Manager Clone)

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Process Management / System Information
  • Software or Tool: Process List Tool
  • Main Book: “Windows via C/C++” by Jeffrey Richter

What you’ll build: A command-line tool that lists all running processes with their PID, memory usage, thread count, and executable path—like a scriptable Task Manager.

Why it teaches Win32: Process enumeration is your first step into the “system” side of Windows programming. You’ll use ToolHelp32 or PSAPI to enumerate processes, then OpenProcess to get handles to query information.

Core challenges you’ll face:

  • Enumerating all processes → maps to CreateToolhelp32Snapshot or EnumProcesses
  • Opening processes for query → maps to access rights, privilege requirements
  • Getting process details → maps to QueryFullProcessImageName, memory counters
  • Handling access denied → maps to elevation, protected processes

Key Concepts:

  • Process enumeration: “Windows via C/C++” Chapter 4 - Jeffrey Richter
  • Process object and handles: “Windows Internals Part 1” Chapter 3 - Russinovich
  • Memory counters: MSDN “PROCESS_MEMORY_COUNTERS” structure
  • Protected processes: “Windows Internals Part 1” Chapter 3 - Russinovich

Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Completed file and registry projects, understanding of handles

Real world outcome:

C:\> proclist
PID     PPID    Threads  Memory     Name                Path
----    ----    -------  ------     ----                ----
0       0       4        0 KB       [System Process]
4       0       156      144 KB     System
88      4       3        1.2 MB     Registry
464     4       8        3.4 MB     smss.exe
572     556     12       5.1 MB     csrss.exe           \Windows\System32\csrss.exe
632     556     12       4.8 MB     wininit.exe         \Windows\System32\wininit.exe
...
14532   12340   15       78.5 MB    chrome.exe          \Program Files\Google\Chrome\...

Processes: 247 | Threads: 3,128 | Total Memory: 8.2 GB

C:\> proclist /pid 14532
Process: chrome.exe (PID: 14532)
  Path:         C:\Program Files\Google\Chrome\Application\chrome.exe
  Parent:       chrome.exe (PID: 12340)
  User:         DESKTOP-ABC\John
  Started:      2024-01-15 09:23:45
  Threads:      15
  Handles:      542
  Working Set:  78.5 MB
  Private:      52.3 MB
  CPU Time:     00:05:32.156

  Modules:
    chrome.exe          1.8 MB  0x00007FF6A0000000
    ntdll.dll           2.0 MB  0x00007FFC80000000
    kernel32.dll        768 KB  0x00007FFC7E000000
    ...

Implementation Hints:

Two main approaches for process enumeration:

Approach 1: ToolHelp32 (easier, more info)

HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W pe32 = { sizeof(pe32) };

if (Process32FirstW(hSnapshot, &pe32)) {
    do {
        // pe32.th32ProcessID - PID
        // pe32.th32ParentProcessID - Parent PID
        // pe32.cntThreads - Thread count
        // pe32.szExeFile - Executable name
    } while (Process32NextW(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);

Approach 2: PSAPI (just PIDs, then open each)

DWORD pids[1024], bytesReturned;
EnumProcesses(pids, sizeof(pids), &bytesReturned);
int processCount = bytesReturned / sizeof(DWORD);

for (int i = 0; i < processCount; i++) {
    HANDLE hProc = OpenProcess(
        PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
        FALSE,
        pids[i]
    );
    // Query process info...
    CloseHandle(hProc);
}

Questions to explore:

  • Why can’t you open System (PID 4) with OpenProcess?
  • What’s the difference between QueryFullProcessImageName and GetModuleFileNameEx?
  • Why do you need SeDebugPrivilege for some processes?
  • What are protected processes light (PPL)?

Learning milestones:

  1. Basic process list works → You understand enumeration APIs
  2. Memory and thread counts show → You understand process info queries
  3. Full paths work → You understand QueryFullProcessImageName
  4. Handle “access denied” gracefully → You understand process security

Project 6: Memory Map Viewer

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Memory Management / Virtual Memory
  • Software or Tool: Memory Map Analyzer
  • Main Book: “Windows Internals Part 1” by Russinovich, Solomon, and Ionescu

What you’ll build: A tool that displays the virtual memory layout of any process—showing code, heap, stack, DLLs, and mapped files with their protection and state.

Why it teaches Win32: This is where you truly understand Windows memory management. You’ll see how VirtualAlloc creates regions, how DLLs get mapped, and how the stack grows. This knowledge is essential for debugging, security research, and optimization.

Core challenges you’ll face:

  • Querying memory regions → maps to VirtualQueryEx, MEMORY_BASIC_INFORMATION
  • Understanding memory states → maps to committed, reserved, free
  • Interpreting protection flags → maps to PAGE_EXECUTE_READ, PAGE_READWRITE, etc.
  • Identifying region types → maps to image, mapped, private

Key Concepts:

  • Virtual memory architecture: “Windows Internals Part 1” Chapter 5 - Russinovich
  • VirtualAlloc/VirtualQuery: “Windows via C/C++” Chapter 13 - Jeffrey Richter
  • Memory protection: “Windows System Programming” Chapter 5 - Johnson Hart
  • Address space layout: “Windows Internals Part 1” Chapter 5 - Russinovich

Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Process project completed, basic understanding of virtual memory

Real world outcome:

C:\> memmap 14532

Process: chrome.exe (PID: 14532)
Virtual Address Space Map:

Address Range                    Size      State     Protect    Type        Info
-------------------------------  --------  --------  ---------  ----------  ----
0x00000000`00000000              64 KB     Free      ---        ---
0x00000000`00010000              64 KB     Reserved  ---        Private
0x00000000`00020000              4 KB      Commit    RW-        Private     [Stack Guard]
0x00000000`00021000              1020 KB   Commit    RW-        Private     [Stack]
...
0x00007FF6`A0000000              4 KB      Commit    R--        Image       chrome.exe [Headers]
0x00007FF6`A0001000              1.2 MB    Commit    R-X        Image       chrome.exe [.text]
0x00007FF6`A0130000              400 KB    Commit    R--        Image       chrome.exe [.rdata]
0x00007FF6`A0195000              48 KB     Commit    RW-        Image       chrome.exe [.data]
...
0x00007FFC`80000000              2 MB      Commit    R-X        Image       ntdll.dll [.text]
...
0x00007FFC`F0000000 - End        ---       Free      ---        ---

Summary:
  Committed:     856 MB  (private: 520 MB, image: 336 MB)
  Reserved:      2.1 GB
  Free:          127 TB (64-bit address space)

  Heaps:         12 (total: 45 MB)
  Stacks:        15 (total: 15 MB)
  Loaded DLLs:   87

Implementation Hints:

The core loop for memory enumeration:

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

MEMORY_BASIC_INFORMATION mbi;
LPVOID address = NULL;

while (VirtualQueryEx(hProcess, address, &mbi, sizeof(mbi))) {
    // mbi.BaseAddress    - Region start
    // mbi.RegionSize     - Size of this region
    // mbi.State          - MEM_COMMIT, MEM_RESERVE, MEM_FREE
    // mbi.Protect        - PAGE_READONLY, PAGE_READWRITE, etc.
    // mbi.Type           - MEM_IMAGE, MEM_MAPPED, MEM_PRIVATE
    // mbi.AllocationBase - Start of the allocation

    // Move to next region
    address = (LPBYTE)mbi.BaseAddress + mbi.RegionSize;
}

Protection flags to understand:

  • PAGE_EXECUTE_READ (R-X) - Code
  • PAGE_READWRITE (RW-) - Data, heap, stack
  • PAGE_READONLY (R–) - Read-only data, imports
  • PAGE_NOACCESS (—) - Guard pages, reserved
  • PAGE_GUARD - Stack guard (triggers exception on access)

Questions to explore:

  • Why is there a large “hole” in the address space on 64-bit Windows?
  • What’s the difference between MEM_RESERVE and MEM_COMMIT?
  • How can you identify heap vs stack vs regular allocation?
  • What does MEM_IMAGE tell you vs MEM_MAPPED?

Learning milestones:

  1. Basic region list works → You understand VirtualQueryEx
  2. Protection flags decoded → You understand page protection
  3. DLLs identified by path → You understand MEM_IMAGE and module mapping
  4. Can map heap regions → You understand HeapWalk (advanced)

Project 7: Simple Command Shell

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Process Creation / Pipes / I/O
  • Software or Tool: Custom Shell
  • Main Book: “Windows System Programming” by Johnson M. Hart

What you’ll build: A command shell that can launch programs, capture their output, handle pipes between commands, and support basic I/O redirection—a minimal cmd.exe.

Why it teaches Win32: CreateProcess is the heart of Windows program execution. Combined with pipes and I/O redirection, you’ll understand how shells work, how stdout/stderr flow, and how processes communicate.

Core challenges you’ll face:

  • Parsing command lines → maps to tokenization, quoting rules
  • Creating child processes → maps to CreateProcess, STARTUPINFO
  • Piping between processes → maps to CreatePipe, handle inheritance
  • Redirecting I/O → maps to STARTUPINFO handles, HANDLE_FLAG_INHERIT

Key Concepts:

  • CreateProcess: “Windows via C/C++” Chapter 4 - Jeffrey Richter
  • Pipe I/O: “Windows System Programming” Chapter 6 - Johnson Hart
  • Handle inheritance: “Windows via C/C++” Chapter 3 - Jeffrey Richter
  • Console I/O: “Windows System Programming” Chapter 2 - Johnson Hart

Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: File I/O project, process project completed

Real world outcome:

mysh> dir C:\Windows | findstr System
 Directory of C:\Windows

01/15/2024  09:00 AM    <DIR>          System32
01/15/2024  09:00 AM    <DIR>          SystemResources
01/15/2024  09:00 AM    <DIR>          SysWOW64

mysh> echo Hello > output.txt
mysh> type output.txt
Hello

mysh> notepad.exe &
[Started: notepad.exe, PID: 15632]

mysh> tasklist | findstr chrome | sort
chrome.exe                   12340 Console                    1    156,532 K
chrome.exe                   14532 Console                    1     78,456 K
chrome.exe                   14680 Console                    1     45,232 K

mysh> set PATH
PATH=C:\Windows\system32;C:\Windows;...

mysh> cd C:\Users
C:\Users> pwd
C:\Users

Implementation Hints:

CreateProcess core usage:

STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi;

// For I/O redirection:
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = hInputPipe;   // or GetStdHandle(STD_INPUT_HANDLE)
si.hStdOutput = hOutputPipe;
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);

BOOL success = CreateProcessW(
    NULL,              // Application name (NULL = parse from command line)
    commandLine,       // Command line (mutable!)
    NULL,              // Process security attributes
    NULL,              // Thread security attributes
    TRUE,              // Inherit handles
    0,                 // Creation flags
    NULL,              // Environment (NULL = inherit)
    NULL,              // Current directory (NULL = inherit)
    &si,               // Startup info
    &pi                // Process information (output)
);

// IMPORTANT: Close handles you don't need
CloseHandle(pi.hThread);
// Keep pi.hProcess if you need to wait for it

For pipes between commands (cmd1 | cmd2):

HANDLE hReadPipe, hWritePipe;
SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, TRUE };  // Inherit handles

CreatePipe(&hReadPipe, &hWritePipe, &sa, 0);

// cmd1's stdout → hWritePipe
// cmd2's stdin → hReadPipe

// IMPORTANT: Close the ends you don't use in each process!

Questions to think about:

  • Why must the command line buffer be writable?
  • What happens if you don’t close pipe handles correctly?
  • How do you handle commands that don’t exist?
  • What’s the difference between CREATE_NEW_CONSOLE and 0?

Learning milestones:

  1. Simple commands run → You understand CreateProcess basics
  2. Output capture works → You understand pipe creation and STARTF_USESTDHANDLES
  3. **Pipelines work (cmd cmd)** → You understand multi-process pipe chains
  4. Background execution works (&) → You understand handle management and detachment

Project 8: File System Monitor

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 3. The “Service & Support” Model (B2B Utility)
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Overlapped I/O / File System Events
  • Software or Tool: File Watcher Tool
  • Main Book: “Windows via C/C++” by Jeffrey Richter

What you’ll build: A real-time file system monitor that watches directories for changes (creates, deletes, renames, modifications) and logs them—like a simplified version of Process Monitor’s file operations.

Why it teaches Win32: This introduces asynchronous/overlapped I/O, which is essential for high-performance Windows programming. ReadDirectoryChangesW is a great introduction to the Windows async model.

Core challenges you’ll face:

  • Setting up overlapped I/O → maps to OVERLAPPED structure, event objects
  • Understanding change notifications → maps to FILE_NOTIFY_INFORMATION parsing
  • Handling buffer overflows → maps to when changes happen faster than you process
  • Recursive directory watching → maps to subdirectory monitoring flags

Key Concepts:

  • Overlapped I/O: “Windows via C/C++” Chapter 10 - Jeffrey Richter
  • ReadDirectoryChangesW: MSDN documentation
  • Event objects: “Windows via C/C++” Chapter 8 - Jeffrey Richter
  • IOCP for scaling: “Windows via C/C++” Chapter 10 - Jeffrey Richter

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: All prior projects, understanding of async concepts

Real world outcome:

C:\> fswatch C:\Users\John\Documents --recursive

Watching: C:\Users\John\Documents (recursive)
Press Ctrl+C to stop...

[2024-01-15 10:23:45.123] CREATED    C:\Users\John\Documents\report.docx
[2024-01-15 10:23:45.456] MODIFIED   C:\Users\John\Documents\report.docx
[2024-01-15 10:23:46.789] MODIFIED   C:\Users\John\Documents\report.docx
[2024-01-15 10:24:12.345] RENAMED    C:\Users\John\Documents\report.docx
                          -> C:\Users\John\Documents\report_final.docx
[2024-01-15 10:25:33.678] DELETED    C:\Users\John\Documents\~$report.docx
[2024-01-15 10:26:01.234] CREATED    C:\Users\John\Documents\New Folder
                          (directory)

Statistics (last 60 seconds):
  Created:  12
  Modified: 45
  Deleted:  8
  Renamed:  3

Implementation Hints:

Basic overlapped I/O setup:

HANDLE hDir = CreateFileW(
    directoryPath,
    FILE_LIST_DIRECTORY,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,  // Key flags!
    NULL
);

OVERLAPPED overlapped = {0};
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

BYTE buffer[4096];
DWORD bytesReturned;

ReadDirectoryChangesW(
    hDir,
    buffer,
    sizeof(buffer),
    TRUE,  // Watch subtree
    FILE_NOTIFY_CHANGE_FILE_NAME |
    FILE_NOTIFY_CHANGE_DIR_NAME |
    FILE_NOTIFY_CHANGE_LAST_WRITE |
    FILE_NOTIFY_CHANGE_SIZE,
    &bytesReturned,
    &overlapped,
    NULL   // Or completion routine
);

// Wait for changes
WaitForSingleObject(overlapped.hEvent, INFINITE);

// Parse FILE_NOTIFY_INFORMATION structures in buffer
FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*)buffer;
while (TRUE) {
    // fni->Action - what happened
    // fni->FileName - the file (NOT null-terminated!)
    // fni->FileNameLength - length in bytes

    if (fni->NextEntryOffset == 0) break;
    fni = (FILE_NOTIFY_INFORMATION*)((BYTE*)fni + fni->NextEntryOffset);
}

Key questions:

  • Why do you need FILE_FLAG_BACKUP_SEMANTICS for directories?
  • What happens if changes occur faster than you can process them?
  • How do you handle FILE_ACTION_RENAMED_OLD_NAME and FILE_ACTION_RENAMED_NEW_NAME?
  • What’s the difference between using an event vs a completion routine?

Learning milestones:

  1. Basic notifications work → You understand ReadDirectoryChangesW
  2. Overlapped I/O pattern mastered → You understand async I/O model
  3. Rename tracking works → You understand pairing old/new name events
  4. No missed events under load → You understand buffer management and re-issuing

Project 9: DLL Dependency Walker

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool” (Solo-Preneur Potential)
  • Difficulty: Level 3: Advanced
  • Knowledge Area: PE Format / DLL Loading
  • Software or Tool: Dependency Analysis Tool
  • Main Book: “Windows Internals Part 1” by Russinovich, Solomon, and Ionescu

What you’ll build: A tool that analyzes an executable and shows all its DLL dependencies (including recursive), exports, and imports—like the classic Dependency Walker tool.

Why it teaches Win32: This forces you to understand the PE (Portable Executable) format and how Windows loads DLLs. You’ll parse headers, import tables, and understand the loader’s perspective.

Core challenges you’ll face:

  • Parsing PE headers → maps to DOS header, NT headers, section table
  • Reading import directory → maps to Import Address Table, Import Name Table
  • Resolving DLL paths → maps to search order, SxS, API sets
  • Handling 32-bit vs 64-bit → maps to PE32 vs PE32+ formats

Key Concepts:

  • PE format: “Windows Internals Part 1” Chapter 3 - Russinovich
  • Import table structure: “Practical Malware Analysis” Chapter 1 - Sikorski & Honig
  • DLL search order: MSDN “Dynamic-Link Library Search Order”
  • API Sets: “Windows Internals Part 1” Chapter 3 - Russinovich

Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Memory map project, understanding of binary formats

Real world outcome:

C:\> depends notepad.exe

Analyzing: C:\Windows\System32\notepad.exe
Type: PE32+ (64-bit)
Subsystem: Windows GUI

Import Dependencies:
├── KERNEL32.dll (C:\Windows\System32\KERNEL32.DLL)
│   ├── GetLastError
│   ├── CreateFileW
│   ├── ReadFile
│   └── ... (45 more functions)
├── USER32.dll (C:\Windows\System32\USER32.DLL)
│   ├── CreateWindowExW
│   ├── SendMessageW
│   └── ... (78 more functions)
├── GDI32.dll (C:\Windows\System32\GDI32.DLL)
│   └── ... (12 functions)
├── COMCTL32.dll (C:\Windows\WinSxS\...\COMCTL32.DLL)
│   └── ... (5 functions)
└── api-ms-win-core-*.dll → KERNELBASE.dll (API Set)

Dependency Tree (recursive):
notepad.exe
├── KERNEL32.dll
│   └── KERNELBASE.dll
│       └── ntdll.dll
├── USER32.dll
│   ├── win32u.dll
│   ├── GDI32.dll
│   └── ...
└── ...

Missing Dependencies: None
Potential Issues: None

Exports (none - not a DLL)

Implementation Hints:

PE parsing requires understanding the header chain:

DOS Header (at offset 0)
    e_lfanew → PE Signature ("PE\0\0")
                    ↓
              File Header (COFF)
                    ↓
              Optional Header (PE32/PE32+)
                    ↓
              Data Directories[16]
                    ↓
              Section Headers[]

For imports, look at Data Directory index 1 (IMAGE_DIRECTORY_ENTRY_IMPORT):

1. Map the file into memory (CreateFileMapping + MapViewOfFile)
2. Find DOS header, verify "MZ"
3. Follow e_lfanew to PE signature, verify "PE\0\0"
4. Parse IMAGE_FILE_HEADER and IMAGE_OPTIONAL_HEADER
5. Get Data Directories from optional header
6. Import Directory points to IMAGE_IMPORT_DESCRIPTOR array
7. Each descriptor has:
   - Name (RVA to DLL name)
   - OriginalFirstThunk (Import Name Table)
   - FirstThunk (Import Address Table)
8. Walk the INT to get function names

Key concepts:

  • RVA (Relative Virtual Address) vs File Offset - you need to convert!
  • API Sets (api-ms-win-*) are virtual DLLs that redirect to real DLLs
  • The loader’s search order is complex: app directory, SxS, System32, PATH…

Learning milestones:

  1. Can parse PE headers → You understand the PE format
  2. Can list imports → You understand import tables
  3. Can resolve DLL paths → You understand the loader search order
  4. Can handle API sets → You understand modern Windows redirection

Project 10: Thread Pool and Work Queue

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Threading / Synchronization
  • Software or Tool: Custom Thread Pool Library
  • Main Book: “Windows via C/C++” by Jeffrey Richter

What you’ll build: A reusable thread pool library that efficiently processes work items, with configurable pool size, work stealing, and graceful shutdown.

Why it teaches Win32: Threading is where Win32 gets interesting. You’ll learn CreateThread, synchronization primitives (events, mutexes, critical sections, condition variables), and how to coordinate concurrent work safely.

Core challenges you’ll face:

  • Thread lifecycle management → maps to CreateThread, ExitThread, WaitForMultipleObjects
  • Work queue synchronization → maps to CRITICAL_SECTION, condition variables
  • Efficient waiting → maps to WaitForSingleObject vs spinning
  • Graceful shutdown → maps to signaling threads, cleanup order

Key Concepts:

  • Thread APIs: “Windows via C/C++” Chapter 6 - Jeffrey Richter
  • Synchronization: “Windows via C/C++” Chapter 8 - Jeffrey Richter
  • Condition variables: “Windows via C/C++” Chapter 8 - Jeffrey Richter
  • Thread pool design: “C++ Concurrency in Action” Chapter 9 - Anthony Williams

Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Process/thread basics, understanding of concurrency

Real world outcome:

// Usage example:
ThreadPool* pool = ThreadPoolCreate(4);  // 4 worker threads

// Submit work
for (int i = 0; i < 1000; i++) {
    ThreadPoolSubmit(pool, ProcessItem, &items[i]);
}

// Wait for all work to complete
ThreadPoolWait(pool);

// Shutdown
ThreadPoolDestroy(pool);
C:\> threadpool_test
Created thread pool with 4 workers
Submitting 10000 work items...
All items submitted in 12ms

Progress: [████████████████████] 100% (10000/10000)

Completed in 1.23 seconds
Throughput: 8,130 items/second
Average latency: 0.49ms per item
Thread utilization: 97.2%

Per-thread stats:
  Thread 0: 2,512 items processed
  Thread 1: 2,498 items processed
  Thread 2: 2,503 items processed
  Thread 3: 2,487 items processed

Implementation Hints:

Basic structure:

typedef struct {
    CRITICAL_SECTION lock;
    CONDITION_VARIABLE workAvailable;
    CONDITION_VARIABLE workComplete;

    WorkItem* queue;        // Work queue (ring buffer or linked list)
    int queueSize;
    int queueHead, queueTail;

    HANDLE* threads;        // Worker thread handles
    int threadCount;

    volatile BOOL shutdown; // Shutdown signal
    volatile int pendingWork;
} ThreadPool;

Worker thread loop:

DWORD WINAPI WorkerThread(LPVOID param) {
    ThreadPool* pool = (ThreadPool*)param;

    while (TRUE) {
        EnterCriticalSection(&pool->lock);

        // Wait for work or shutdown
        while (pool->queueSize == 0 && !pool->shutdown) {
            SleepConditionVariableCS(&pool->workAvailable,
                                     &pool->lock,
                                     INFINITE);
        }

        if (pool->shutdown && pool->queueSize == 0) {
            LeaveCriticalSection(&pool->lock);
            break;
        }

        // Dequeue work item
        WorkItem item = DequeueWork(pool);
        LeaveCriticalSection(&pool->lock);

        // Execute work (outside lock!)
        item.func(item.param);

        // Signal completion
        InterlockedDecrement(&pool->pendingWork);
        WakeConditionVariable(&pool->workComplete);
    }
    return 0;
}

Key questions:

  • Why use CRITICAL_SECTION instead of Mutex?
  • What’s the difference between WakeConditionVariable and WakeAllConditionVariable?
  • How do you handle the case where work items can spawn more work?
  • Why check for shutdown twice in the worker loop?

Learning milestones:

  1. Basic submission/execution works → You understand thread creation and work queues
  2. No deadlocks or races → You understand synchronization
  3. Wait for completion works → You understand condition variables
  4. Graceful shutdown works → You understand coordinated cleanup

Project 11: Service Application

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 3. The “Service & Support” Model (B2B Utility)
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Windows Services / SCM
  • Software or Tool: Windows Service
  • Main Book: “Windows System Programming” by Johnson M. Hart

What you’ll build: A proper Windows service that installs, starts, stops, and responds to the Service Control Manager—the foundation for any background daemon on Windows.

Why it teaches Win32: Services are how Windows runs background processes. The Service Control Manager (SCM) protocol is unique to Windows and teaches you about system architecture, security contexts, and proper daemon design.

Core challenges you’ll face:

  • Service entry point pattern → maps to ServiceMain, service dispatch table
  • SCM communication → maps to RegisterServiceCtrlHandler, SetServiceStatus
  • Handling control requests → maps to SERVICE_CONTROL_STOP, pause, etc.
  • Running as SYSTEM → maps to security contexts, session 0 isolation

Key Concepts:

  • Service architecture: “Windows System Programming” Chapter 13 - Johnson Hart
  • SCM protocol: “Windows via C/C++” Chapter 4 - Jeffrey Richter
  • Service security: “Windows Internals Part 1” Chapter 7 - Russinovich
  • Session 0 isolation: MSDN “Application Compatibility: Session 0 Isolation”

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Process and thread understanding, basic Windows administration

Real world outcome:

C:\> myservice install
Service 'MyService' installed successfully.

C:\> sc query MyService
SERVICE_NAME: MyService
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 1  STOPPED

C:\> net start MyService
The MyService service is starting.
The MyService service was started successfully.

C:\> sc query MyService
SERVICE_NAME: MyService
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 4  RUNNING
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)

C:\> myservice status
Service is running (PID: 5678)
Uptime: 00:05:23
Requests processed: 1,234

C:\> net stop MyService
The MyService service is stopping.
The MyService service was stopped successfully.

C:\> myservice uninstall
Service 'MyService' uninstalled successfully.

Implementation Hints:

Service structure:

// Main entry point
int main(int argc, char* argv[]) {
    if (argc > 1) {
        if (strcmp(argv[1], "install") == 0) return InstallService();
        if (strcmp(argv[1], "uninstall") == 0) return UninstallService();
    }

    // When run by SCM, start service dispatcher
    SERVICE_TABLE_ENTRY serviceTable[] = {
        { L"MyService", ServiceMain },
        { NULL, NULL }
    };

    StartServiceCtrlDispatcher(serviceTable);
    return 0;
}

// Called by SCM to start the service
void WINAPI ServiceMain(DWORD argc, LPWSTR* argv) {
    // Register control handler
    g_statusHandle = RegisterServiceCtrlHandlerEx(
        L"MyService",
        ServiceCtrlHandler,
        NULL
    );

    // Report starting
    ReportServiceStatus(SERVICE_START_PENDING, 0, 3000);

    // Do initialization...

    // Report running
    ReportServiceStatus(SERVICE_RUNNING, 0, 0);

    // Main service loop
    while (!g_shutdown) {
        // Do work...
        Sleep(1000);
    }

    // Report stopped
    ReportServiceStatus(SERVICE_STOPPED, 0, 0);
}

// Handle control requests
DWORD WINAPI ServiceCtrlHandler(
    DWORD control,
    DWORD eventType,
    LPVOID eventData,
    LPVOID context
) {
    switch (control) {
        case SERVICE_CONTROL_STOP:
            ReportServiceStatus(SERVICE_STOP_PENDING, 0, 3000);
            g_shutdown = TRUE;
            return NO_ERROR;

        case SERVICE_CONTROL_INTERROGATE:
            return NO_ERROR;
    }
    return ERROR_CALL_NOT_IMPLEMENTED;
}

Key questions:

  • Why does ServiceMain run on a different thread than main()?
  • What happens if you don’t respond to SERVICE_CONTROL_STOP in time?
  • How do you debug a service? (hint: DebugBreak or attach debugger)
  • What’s the difference between LocalSystem, LocalService, and NetworkService?

Learning milestones:

  1. Service installs and starts → You understand SCM registration
  2. Responds to stop correctly → You understand the control handler
  3. Runs continuously → You understand the service main loop
  4. Survives logoff → You understand session 0 and service isolation

Project 12: Named Pipe Server

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: IPC / Named Pipes
  • Software or Tool: IPC Server/Client
  • Main Book: “Windows System Programming” by Johnson M. Hart

What you’ll build: A client-server application using named pipes for inter-process communication, supporting multiple simultaneous clients—the foundation for local IPC on Windows.

Why it teaches Win32: Named pipes are Windows’ primary IPC mechanism. This teaches you CreateNamedPipe, overlapped I/O for multiple clients, and the security model for inter-process communication.

Core challenges you’ll face:

  • Creating named pipe server → maps to CreateNamedPipe, PIPE_ACCESS_
  • Handling multiple clients → maps to ConnectNamedPipe, overlapped I/O
  • Message framing → maps to PIPE_TYPE_MESSAGE vs PIPE_TYPE_BYTE
  • Security descriptors → maps to pipe access control

Key Concepts:

  • Named pipe APIs: “Windows System Programming” Chapter 11 - Johnson Hart
  • Overlapped pipe I/O: “Windows via C/C++” Chapter 10 - Jeffrey Richter
  • Pipe security: “Windows Security Internals” Chapter 8 - Forshaw
  • Message mode pipes: MSDN “Named Pipe Type, Read, and Wait Modes”

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Overlapped I/O understanding from file watcher project

Real world outcome:

# Server terminal
C:\> pipeserver --name "\\.\pipe\MyService"
Server listening on \\.\pipe\MyService
[10:23:45] Client connected from PID 1234
[10:23:45] Client 1: Request: "HELLO"
[10:23:45] Client 1: Response: "HELLO BACK"
[10:23:46] Client connected from PID 5678
[10:23:46] Client 2: Request: "STATUS"
[10:23:46] Client 2: Response: "OK: Uptime 00:05:23, Clients: 2"
[10:23:47] Client 1 disconnected
^C Shutting down...

# Client terminal
C:\> pipeclient --name "\\.\pipe\MyService"
Connected to \\.\pipe\MyService
> HELLO
< HELLO BACK
> STATUS
< OK: Uptime 00:05:23, Clients: 2
> quit
Disconnected.

Implementation Hints:

Server pattern with multiple clients:

// Create pipe instance
HANDLE hPipe = CreateNamedPipeW(
    L"\\\\.\\pipe\\MyPipe",
    PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
    PIPE_UNLIMITED_INSTANCES,  // Max instances
    4096,                       // Output buffer
    4096,                       // Input buffer
    0,                          // Default timeout
    NULL                        // Security (NULL = default)
);

// Wait for client connection (overlapped)
OVERLAPPED overlapped = {0};
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

ConnectNamedPipe(hPipe, &overlapped);
WaitForSingleObject(overlapped.hEvent, INFINITE);

// Now client is connected - read/write with ReadFile/WriteFile

For multiple clients, you need either:

  1. A thread per client (simple)
  2. Overlapped I/O with WaitForMultipleObjects (intermediate)
  3. I/O Completion Ports (advanced, scalable)

Questions to explore:

  • What’s the difference between PIPE_TYPE_MESSAGE and PIPE_TYPE_BYTE?
  • How do you get the client’s PID (hint: GetNamedPipeClientProcessId)?
  • What happens if the client dies while you’re reading?
  • How do you secure a pipe so only certain users can connect?

Learning milestones:

  1. Single client works → You understand CreateNamedPipe basics
  2. Message mode works → You understand pipe types
  3. Multiple clients work → You understand overlapped I/O or threading
  4. Security works → You understand pipe security descriptors

Project 13: DLL Injection Tool

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 4: Expert
  • Knowledge Area: Process Manipulation / Code Injection
  • Software or Tool: DLL Injector
  • Main Book: “Windows Internals Part 1” by Russinovich, Solomon, and Ionescu

What you’ll build: A tool that injects a DLL into a running process using the classic CreateRemoteThread technique—the foundation for modding, debugging, and security research.

Why it teaches Win32: This combines everything: process handles, memory management (VirtualAllocEx), cross-process writes (WriteProcessMemory), and remote thread creation. It’s the “boss level” of Win32 process manipulation.

Core challenges you’ll face:

  • Opening target process → maps to OpenProcess, PROCESS_ALL_ACCESS
  • Allocating memory in remote process → maps to VirtualAllocEx
  • Writing to remote process → maps to WriteProcessMemory
  • Creating remote thread → maps to CreateRemoteThread, LoadLibraryW address

Key Concepts:

  • Process memory manipulation: “Windows via C/C++” Chapter 22 - Jeffrey Richter
  • DLL injection techniques: “Practical Malware Analysis” Chapter 12 - Sikorski & Honig
  • Thread creation in remote process: “Windows Internals Part 1” Chapter 3 - Russinovich
  • Anti-injection defenses: “Windows Internals Part 1” Chapter 7 - Russinovich

Difficulty: Expert Time estimate: 2-3 weeks Prerequisites: Memory map and process projects, understanding of DLL loading

Real world outcome:

C:\> injector --pid 1234 --dll mydll.dll
Target: notepad.exe (PID: 1234)
DLL: C:\path\to\mydll.dll

[+] Opening target process...
[+] Allocated 520 bytes in target @ 0x1A0000
[+] Wrote DLL path to target memory
[+] Found LoadLibraryW @ 0x7FFE12345678
[+] Created remote thread (TID: 5678)
[+] Remote thread completed, return value: 0x00007FFE90000000 (DLL base)

Injection successful! DLL loaded at 0x00007FFE90000000

C:\> injector --pid 1234 --list
Modules in notepad.exe (PID: 1234):
  0x00007FF789000000  notepad.exe
  0x00007FFE80000000  ntdll.dll
  0x00007FFE78000000  kernel32.dll
  ...
  0x00007FFE90000000  mydll.dll  <-- Injected!

Implementation Hints:

The classic injection flow:

// 1. Open target process with required access
HANDLE hProcess = OpenProcess(
    PROCESS_CREATE_THREAD |
    PROCESS_QUERY_INFORMATION |
    PROCESS_VM_OPERATION |
    PROCESS_VM_WRITE |
    PROCESS_VM_READ,
    FALSE,
    targetPid
);

// 2. Allocate memory in target for DLL path
size_t pathSize = (wcslen(dllPath) + 1) * sizeof(WCHAR);
LPVOID remoteBuffer = VirtualAllocEx(
    hProcess,
    NULL,
    pathSize,
    MEM_COMMIT | MEM_RESERVE,
    PAGE_READWRITE
);

// 3. Write DLL path to remote memory
WriteProcessMemory(hProcess, remoteBuffer, dllPath, pathSize, NULL);

// 4. Get address of LoadLibraryW (same in all processes due to ASLR per-boot)
HMODULE hKernel32 = GetModuleHandleW(L"kernel32.dll");
LPVOID loadLibraryAddr = GetProcAddress(hKernel32, "LoadLibraryW");

// 5. Create remote thread that calls LoadLibraryW with our path
HANDLE hThread = CreateRemoteThread(
    hProcess,
    NULL,
    0,
    (LPTHREAD_START_ROUTINE)loadLibraryAddr,
    remoteBuffer,
    0,
    NULL
);

// 6. Wait for it to complete
WaitForSingleObject(hThread, INFINITE);

// 7. Get exit code (DLL base address or 0 if failed)
DWORD exitCode;
GetExitCodeThread(hThread, &exitCode);

// 8. Cleanup
CloseHandle(hThread);
VirtualFreeEx(hProcess, remoteBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);

Important questions:

  • Why does LoadLibraryW address work across processes?
  • What’s the difference between 32-bit and 64-bit injection?
  • What protections exist to prevent injection (PPL, CFG, etc.)?
  • How would you inject into a process that hasn’t loaded kernel32 yet?

Learning milestones:

  1. Can inject into normal processes → You understand the basic technique
  2. Handles 32/64 bit correctly → You understand architecture differences
  3. Can detect injection failures → You understand what can go wrong
  4. Understands protections → You know about modern Windows defenses

Project 14: Mini Debugger

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 5: Master
  • Knowledge Area: Debugging / Process Control
  • Software or Tool: Custom Debugger
  • Main Book: “Windows Internals Part 1” by Russinovich, Solomon, and Ionescu

What you’ll build: A command-line debugger that can attach to processes, set breakpoints, step through code, and inspect memory—a stripped-down WinDbg.

Why it teaches Win32: The Windows debug APIs are the ultimate process control mechanism. You’ll learn WaitForDebugEvent, context manipulation, and how breakpoints actually work (int 3, DR registers).

Core challenges you’ll face:

  • Debug event loop → maps to WaitForDebugEvent, ContinueDebugEvent
  • Handling exceptions → maps to EXCEPTION_DEBUG_EVENT, breakpoints
  • Reading/writing context → maps to GetThreadContext, SetThreadContext
  • Setting breakpoints → maps to int3 (0xCC) patching, hardware breakpoints

Key Concepts:

  • Debug API: “Windows via C/C++” Chapter 24 - Jeffrey Richter
  • Exception handling: “Windows Internals Part 1” Chapter 8 - Russinovich
  • Context and registers: “Windows via C/C++” Chapter 24 - Jeffrey Richter
  • Breakpoint implementation: “Practical Malware Analysis” Chapter 8 - Sikorski & Honig

Difficulty: Master Time estimate: 1 month+ Prerequisites: All previous projects, assembly basics, understanding of x64 ABI

Real world outcome:

C:\> minidbg notepad.exe
minidbg - Mini Windows Debugger
Debugging: notepad.exe (PID: 1234)

Process created. Entry point: 0x00007FF789001000
[BREAKPOINT] ntdll!LdrInitializeThunk

dbg> bp kernel32!CreateFileW
Breakpoint 1 set at 0x00007FFE78012340

dbg> g
[BREAKPOINT] Hit breakpoint 1 at kernel32!CreateFileW
RIP: 0x00007FFE78012340
RCX: 0x00000012ABCD0000  -> L"C:\Users\John\test.txt"
RDX: 0x80000000 (GENERIC_READ)
R8:  0x00000001 (FILE_SHARE_READ)

dbg> k
Call Stack:
  kernel32!CreateFileW+0x0
  notepad!OpenFile+0x45
  notepad!WndProc+0x234
  USER32!DispatchMessageW+0x123
  notepad!WinMain+0x78

dbg> r
RAX: 0x0000000000000000  RBX: 0x0000000000000000
RCX: 0x00000012ABCD0000  RDX: 0x0000000080000000
R8:  0x0000000000000001  R9:  0x0000000000000000
RIP: 0x00007FFE78012340  RSP: 0x00000012ABCD8000
RFLAGS: 0x0000000000000246

dbg> db rcx
00000012`ABCD0000  43 00 3A 00 5C 00 55 00-73 00 65 00 72 00 73 00  C.:.\.U.s.e.r.s.
00000012`ABCD0010  5C 00 4A 00 6F 00 68 00-6E 00 5C 00 74 00 65 00  \.J.o.h.n.\.t.e.

dbg> g
Process exited with code 0

Implementation Hints:

Basic debug loop:

// Start debugging
DEBUG_EVENT debugEvent;
DWORD continueStatus = DBG_CONTINUE;

// Either create process with DEBUG_PROCESS flag
CreateProcess(..., DEBUG_PROCESS, ...);
// Or attach to existing
DebugActiveProcess(targetPid);

while (TRUE) {
    WaitForDebugEvent(&debugEvent, INFINITE);

    switch (debugEvent.dwDebugEventCode) {
        case CREATE_PROCESS_DEBUG_EVENT:
            // Process started - save handle
            break;

        case EXCEPTION_DEBUG_EVENT:
            // Exception occurred
            DWORD exceptionCode = debugEvent.u.Exception.ExceptionRecord.ExceptionCode;

            if (exceptionCode == EXCEPTION_BREAKPOINT) {
                // int3 hit - check if it's ours or system
            } else if (exceptionCode == EXCEPTION_SINGLE_STEP) {
                // Single step completed
            } else {
                // Real exception - pass to debuggee
                continueStatus = DBG_EXCEPTION_NOT_HANDLED;
            }
            break;

        case EXIT_PROCESS_DEBUG_EVENT:
            // Process exited
            return;

        case LOAD_DLL_DEBUG_EVENT:
            // DLL loaded - good time to set breakpoints
            break;
    }

    ContinueDebugEvent(
        debugEvent.dwProcessId,
        debugEvent.dwThreadId,
        continueStatus
    );
    continueStatus = DBG_CONTINUE;
}

Setting a software breakpoint:

// 1. Read original byte
BYTE originalByte;
ReadProcessMemory(hProcess, address, &originalByte, 1, NULL);

// 2. Write int3 (0xCC)
BYTE int3 = 0xCC;
WriteProcessMemory(hProcess, address, &int3, 1, NULL);

// 3. When breakpoint hits, restore original byte and single-step
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(hThread, &ctx);

// Restore original byte
WriteProcessMemory(hProcess, address, &originalByte, 1, NULL);

// Back up RIP to re-execute the instruction
ctx.Rip--;

// Enable single-step (TRAP flag)
ctx.EFlags |= 0x100;
SetThreadContext(hThread, &ctx);

// After single-step, re-set the breakpoint

Questions to master:

  • Why does RIP point past the int3 when the breakpoint hits?
  • How do hardware breakpoints (DR0-DR7) differ from software breakpoints?
  • What’s the difference between first-chance and second-chance exceptions?
  • How do you handle multi-threaded debugging?

Learning milestones:

  1. Can attach and see events → You understand the debug loop
  2. Breakpoints work → You understand int3 and context manipulation
  3. Single-stepping works → You understand TRAP flag
  4. Stack traces work → You understand x64 unwinding

Project 15: Custom Heap Allocator

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 4: Expert
  • Knowledge Area: Memory Management / Heap Internals
  • Software or Tool: Custom Memory Allocator
  • Main Book: “Windows Internals Part 1” by Russinovich, Solomon, and Ionescu

What you’ll build: A custom heap allocator using VirtualAlloc that implements malloc/free semantics with your own allocation strategy—understanding how heaps really work.

Why it teaches Win32: This is the deepest understanding of Windows memory management. You’ll learn VirtualAlloc (reserve/commit/decommit), page granularity, and how to build data structures for tracking allocations.

Core challenges you’ll face:

  • Using VirtualAlloc correctly → maps to reserve vs commit, page alignment
  • Tracking allocations → maps to free lists, headers, coalescing
  • Handling fragmentation → maps to allocation strategies, compaction
  • Thread safety → maps to locking strategies for heap access

Key Concepts:

  • VirtualAlloc: “Windows via C/C++” Chapter 13 - Jeffrey Richter
  • Heap algorithms: “Introduction to Algorithms” Chapter 17 - CLRS
  • Windows heap implementation: “Windows Internals Part 1” Chapter 5 - Russinovich
  • Lock-free data structures: “C++ Concurrency in Action” Chapter 7 - Anthony Williams

Difficulty: Expert Time estimate: 2-4 weeks Prerequisites: Memory map project, data structures knowledge

Real world outcome:

C:\> heaptest
Custom Heap Allocator Test

Allocating 10000 random-sized blocks (16 bytes - 1 MB)...
Allocation time: 45ms

Heap statistics:
  Total reserved:   64 MB (16 regions)
  Total committed:  12.3 MB
  Allocated:        8.7 MB in 10000 blocks
  Free:             3.6 MB in 234 free chunks
  Overhead:         1.2 MB (9.7%)
  Fragmentation:    12.3%

Free half the blocks randomly...
Free time: 23ms

After freeing:
  Allocated:        4.2 MB in 5000 blocks
  Free:             8.1 MB in 412 free chunks (coalesced from 5234)

Allocate 5000 more blocks...
Reuse rate: 89% (allocated from free list)

Stress test: 1000000 alloc/free cycles...
Completed in 2.3 seconds
Peak memory: 45 MB
Final memory: 12 MB (no leaks)

Implementation Hints:

Basic structure:

typedef struct BlockHeader {
    size_t size;          // Size of this block (including header)
    int free;             // Is this block free?
    struct BlockHeader* next;  // Next block in heap (for coalescing)
    struct BlockHeader* prev;  // Previous block
} BlockHeader;

typedef struct FreeBlock {
    BlockHeader header;
    struct FreeBlock* nextFree;  // Next in free list
    struct FreeBlock* prevFree;  // Previous in free list
} FreeBlock;

typedef struct Heap {
    CRITICAL_SECTION lock;
    FreeBlock* freeList;      // Head of free list
    void* regionStart;        // Start of virtual memory region
    size_t regionSize;        // Total reserved size
    size_t committed;         // Currently committed bytes
} Heap;

VirtualAlloc usage:

// Reserve a large region (doesn't use physical memory)
void* region = VirtualAlloc(
    NULL,
    64 * 1024 * 1024,  // 64 MB
    MEM_RESERVE,
    PAGE_NOACCESS
);

// Commit pages as needed (uses physical memory/pagefile)
VirtualAlloc(
    address,
    pageSize,          // 4 KB typically
    MEM_COMMIT,
    PAGE_READWRITE
);

// Decommit when no longer needed (returns physical memory)
VirtualFree(
    address,
    pageSize,
    MEM_DECOMMIT
);

Key questions:

  • What’s the minimum allocation granularity? (64 KB for reserve, 4 KB for commit)
  • How do you handle alignment requirements?
  • When should you return memory to the OS (decommit)?
  • How do best-fit, first-fit, and segregated free lists compare?

Learning milestones:

  1. Basic alloc/free works → You understand VirtualAlloc and headers
  2. Free list works → You understand linked list management
  3. Coalescing works → You understand reducing fragmentation
  4. Thread-safe → You understand heap locking

Final Capstone Project: Process Monitor Clone

  • File: LEARN_WIN32_API_DEEP_DIVE.md
  • Main Programming Language: C
  • Alternative Programming Languages: C++, Rust
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 3. The “Service & Support” Model (B2B Utility)
  • Difficulty: Level 5: Master
  • Knowledge Area: ETW / System Monitoring / Kernel Events
  • Software or Tool: System Activity Monitor
  • Main Book: “Windows Internals Part 1” by Russinovich, Solomon, and Ionescu

What you’ll build: A comprehensive system activity monitor that shows real-time file, registry, network, and process events across the entire system—a clone of Sysinternals Process Monitor.

Why it teaches Win32: This is the ultimate Win32 project. It combines everything: ETW (Event Tracing for Windows) for kernel events, multiple consumers, high-throughput event processing, and sophisticated filtering. You’ll understand how Windows exposes its internal operations.

Core challenges you’ll face:

  • Setting up ETW sessions → maps to StartTrace, EnableTraceEx2
  • Consuming events in real-time → maps to OpenTrace, ProcessTrace
  • Decoding event data → maps to TDH APIs, manifest parsing
  • High-volume event handling → maps to buffering, filtering, UI threading
  • Correlating events → maps to process tree, stack traces

Key Concepts:

  • ETW architecture: “Windows Internals Part 1” Chapter 8 - Russinovich
  • ETW APIs: MSDN “Event Tracing” documentation
  • Kernel providers: Microsoft-Windows-Kernel-File, Registry, Process
  • TDH (Trace Data Helper): MSDN “Retrieving Event Data Using TDH”

Difficulty: Master Time estimate: 1-2 months Prerequisites: All previous projects completed

Real world outcome:

C:\> procmon --filter notepad.exe

Process Monitor - Real-time System Activity
Filter: Process Name = notepad.exe
═══════════════════════════════════════════════════════════════════════════

Time          Process          Operation         Path                      Result
────────────────────────────────────────────────────────────────────────────
10:23:45.123  notepad.exe      CreateFile        C:\test.txt               SUCCESS
                               ├─ Desired Access: GENERIC_READ | GENERIC_WRITE
                               ├─ Share Mode:    FILE_SHARE_READ
                               └─ Options:       FILE_NON_DIRECTORY_FILE

10:23:45.125  notepad.exe      ReadFile          C:\test.txt               SUCCESS
                               ├─ Offset:        0
                               └─ Length:        4096

10:23:45.130  notepad.exe      RegQueryValue     HKCU\Software\Microsoft\  SUCCESS
                                                 Notepad\fWrap
                               └─ Data:          1 (DWORD)

10:23:46.456  notepad.exe      WriteFile         C:\test.txt               SUCCESS
                               ├─ Offset:        0
                               └─ Length:        1234

10:23:46.789  notepad.exe      CloseFile         C:\test.txt               SUCCESS

───────────────────────────────────────────────────────────────────────────
Events: 5,234 | Filtered: 127 | Rate: 45/sec | Buffer: 2% full

Implementation Hints:

ETW session setup:

EVENT_TRACE_PROPERTIES* props = AllocateSessionProperties();
props->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
props->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
props->Wnode.ClientContext = 1;  // QPC timestamps
wcscpy(sessionName, L"MyProcMon");

// Start session
StartTraceW(&sessionHandle, sessionName, props);

// Enable kernel providers
CLASSIC_EVENT_ID eventIds[] = {
    // File events
    { { FileIOGuid }, 0 /* all events */ },
    // Registry events
    { { RegistryGuid }, 0 },
    // Process events
    { { ProcessGuid }, 0 },
};

EnableTraceEx2(
    sessionHandle,
    &SystemTraceControlGuid,
    EVENT_CONTROL_CODE_ENABLE_PROVIDER,
    TRACE_LEVEL_INFORMATION,
    0, 0,
    0,
    NULL
);

Event consumption:

EVENT_TRACE_LOGFILEW logFile = {0};
logFile.LoggerName = L"MyProcMon";
logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME |
                           PROCESS_TRACE_MODE_EVENT_RECORD;
logFile.EventRecordCallback = EventCallback;

TRACEHANDLE traceHandle = OpenTraceW(&logFile);

// This blocks and calls EventCallback for each event
ProcessTrace(&traceHandle, 1, NULL, NULL);

Event callback:

VOID WINAPI EventCallback(PEVENT_RECORD eventRecord) {
    // eventRecord->EventHeader contains:
    // - TimeStamp (QPC)
    // - ProcessId
    // - ThreadId
    // - EventDescriptor.Id (event type)

    // Use TDH to decode the event data
    DWORD bufferSize = 0;
    TdhGetEventInformation(eventRecord, 0, NULL, NULL, &bufferSize);

    TRACE_EVENT_INFO* info = malloc(bufferSize);
    TdhGetEventInformation(eventRecord, 0, NULL, info, &bufferSize);

    // Parse properties based on event type...
}

Key questions to master:

  • What’s the difference between kernel-mode and user-mode ETW providers?
  • How do you handle the volume of events without dropping any?
  • How do you decode stack traces from events?
  • What permissions are needed to enable kernel providers?

Learning milestones:

  1. Can see file events → You understand ETW basics
  2. Multiple event types work → You understand provider enabling
  3. Events decode correctly → You understand TDH
  4. Filtering works efficiently → You understand event processing
  5. No dropped events under load → You understand buffering

Project Comparison Table

Project Difficulty Time Depth of Understanding Fun Factor
Error Message Lookup Beginner Weekend ⭐⭐ ⭐⭐
File Copy Utility Beginner Weekend ⭐⭐⭐ ⭐⭐
Directory Tree Walker Beginner Weekend ⭐⭐⭐ ⭐⭐⭐
Registry Explorer CLI Beginner Weekend ⭐⭐⭐ ⭐⭐
Process Lister Intermediate 1-2 weeks ⭐⭐⭐⭐ ⭐⭐⭐
Memory Map Viewer Intermediate 1-2 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Simple Command Shell Intermediate 1-2 weeks ⭐⭐⭐⭐ ⭐⭐⭐⭐
File System Monitor Advanced 1-2 weeks ⭐⭐⭐⭐ ⭐⭐⭐
DLL Dependency Walker Advanced 2-3 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Thread Pool Advanced 2-3 weeks ⭐⭐⭐⭐ ⭐⭐⭐
Service Application Advanced 1-2 weeks ⭐⭐⭐⭐ ⭐⭐⭐
Named Pipe Server Advanced 1-2 weeks ⭐⭐⭐⭐ ⭐⭐⭐
DLL Injection Tool Expert 2-3 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Mini Debugger Master 1 month+ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Custom Heap Allocator Expert 2-4 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Process Monitor Clone Master 1-2 months ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

If you’re new to Win32:

  1. Start with Error Message Lookup — Get comfortable with the build environment and basic Win32 patterns
  2. File Copy Utility — Master the handle pattern with CreateFile/ReadFile/WriteFile
  3. Directory Tree Walker — Learn enumeration patterns
  4. Registry Explorer — Another handle-based subsystem
  5. Process Lister — Your first “system” tool

If you have some systems experience:

  1. Memory Map Viewer — Jump straight into understanding memory
  2. Simple Command Shell — Master process creation and piping
  3. File System Monitor — Learn overlapped I/O
  4. DLL Dependency Walker — Understand PE format and loading

If you want the hardcore path:

  1. DLL Injection Tool — Combine everything you know about processes and memory
  2. Mini Debugger — The ultimate control over program execution
  3. Process Monitor Clone — ETW and kernel-level visibility

Essential Resources

Primary Books

  1. “Windows via C/C++” by Jeffrey Richter — The Bible of Win32 programming
  2. “Windows System Programming” by Johnson M. Hart — Practical, example-driven
  3. “Windows Internals Part 1 & 2” by Russinovich et al. — Deep understanding of how Windows works

Online Resources

  • MSDN Documentation — The authoritative reference
  • Windows SDK Samples — Official code examples
  • ReactOS Source Code — Open-source Windows implementation (great for understanding undocumented behavior)
  • Geoff Chappell’s Site — Deep dives into undocumented Windows

Tools You’ll Need

  • Visual Studio — IDE and compiler
  • Windows SDK — Headers and libraries
  • WinDbg — Debugger (preview version recommended)
  • Sysinternals Suite — Reference implementations of what you’re building
  • API Monitor — See what APIs programs call
  • x64dbg — User-mode debugger for testing

Summary

Project Main Language
Error Message Lookup Tool C
File Copy Utility with Progress C
Directory Tree Walker C
Registry Explorer CLI C
Process Lister (Task Manager Clone) C
Memory Map Viewer C
Simple Command Shell C
File System Monitor C
DLL Dependency Walker C
Thread Pool and Work Queue C
Service Application C
Named Pipe Server C
DLL Injection Tool C
Mini Debugger C
Custom Heap Allocator C
Process Monitor Clone (Capstone) C

These projects will take you from Win32 beginner to expert. By the end, you’ll understand Windows at a level that very few developers achieve—you’ll know what’s really happening when you call an API, and you’ll be able to build sophisticated system tools that work at the OS level.