Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

9 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

WINDIAG β€” Windows System & Application Diagnostic Tool

Platform Language Build

WINDIAG is a lightweight Windows system and application diagnostic utility designed to help developers and systems administrators diagnose process crashes, monitor debug events, analyze API hooking integrity, and statically scan executables for legacy OS compatibility.

Built in pure C89 targeting Windows 2000 and newer, it operates with zero runtime dependencies (no MFC, no ATL, no modern C++ runtime dependencies).


πŸ–₯️ Graphical User Interface (GUI)

The GUI front end (windiag_gui.exe) features a custom, high-fidelity dark-mode user interface designed to mimic a vintage green phosphor CRT monitor.

WINDIAG GUI Screenshot


πŸš€ Key Features

  • Retro Phosphor GUI Front End (windiag_gui.exe): Features custom rendering, interactive process lists, a scrollable event log terminal, and visual scanlines.
  • Command Line Front End (windiag.exe): A robust CLI for headless environments or scripting.
  • Remote Network Debugging Architecture (wd_network.c):
    • Supports dual operational modes: Agent Mode (runs on remote target machines/VMs) and Controller Mode (runs on host).
    • Streams live process lists, real-time debug events, CPU register states, stack values, and disassembled instruction lines over TCP (WDNE wire protocol).
    • Allows remote process attaching, launching, setting/clearing software breakpoints, single-stepping, and crash analysis over network connections.
  • Static Compatibility Scanner (wd_compat.c):
    • Inspects any PE binary (.exe/.dll) without executing it to determine compatibility.
    • Reads PE headers directly from disk (memory-mapped, read-only).
    • Checks linked subsystem versions against Windows 2000 (NT 5.0).
    • Walks the import table to flag functions that do not exist on Windows 2000/XP (e.g., GetTickCount64, InitializeSRWLock, SHGetKnownFolderPath).
    • Identifies modern manifest blocks (Vista+ requestedExecutionLevel/supportedOS) and Universal CRT dependencies.
  • API Hook & Integrity Scanner (wd_apichk.c):
    • Inspects function entry points of loaded modules in target processes.
    • Detects API hijacking/hooking (e.g., standard JMP or PUSH+RET trampolines).
    • Detects code patching (e.g., debug-inserted INT3 breakpoints or suspicious NOP sleds).
  • Thread-Safe Event Logger (wd_events.c): Uses a high-performance, lock-free ring buffer to forward real-time debug events from background monitor threads straight to the GUI thread.
  • Win32 Debug Loop (wd_debug.c): Attaches to running PIDs, attaches by process name, or launches new executables to capture standard debug events and exception crashes.
  • Crash History Persistence (wd_history.c):
    • Remembers crash addresses, exception codes, and call stacks across sessions.
    • On subsequent debugs of the same executable, it logs previous crashes and suggests breakpoint locations.
  • Active Software Breakpoint Manager (wd_debug.c):
    • Allows setting up to 32 active software breakpoints (INT3 / 0xCC instruction patching) at target memory addresses.
    • Handles instruction byte restoration, single-stepping over breakpoints, and cleanup on exit.

πŸ“‚ Project Architecture

include/
  wd_common.h    - Shared data types, constants, palette details, and limits
  wd_backend.h   - Exception table mapping, module tracking, and PE probing
  wd_events.h    - Thread-safe event ring buffer interface
  wd_debug.h     - Debug loop definitions (Attach, Launch, Monitor)
  wd_network.h   - Remote socket debugging protocol & agent/controller types
  wd_compat.h    - Static compatibility scanner definitions
  wd_apichk.h    - API integrity checker definitions
  wd_history.h   - Process event history definitions

src/
  wd_backend.c   - Exception decoding and module/symbol resolution
  wd_events.c    - Event queue ring-buffer implementation
  wd_debug.c     - Active Win32 debugging and event monitor loop
  wd_network.c   - Remote Agent / Controller TCP socket debugging engine
  wd_compat.c    - Static PE header parsing & import verification
  wd_apichk.c    - Target process API memory scanning for hooks
  wd_disasm.c    - X86/x64 instruction disassembler helper
  wd_history.c   - Historical database of process event details
  wd_cli_main.c  - Console application entry point (windiag.exe)
  wd_gui_main.c  - Vintage-styled GUI entry point (windiag_gui.exe)

πŸ› οΈ Building the Project

Using MSVC (Windows / WinDDK)

Use the included build.bat script, which configures compiler flags to target legacy Windows subsystems (5.0 for Windows 2000).

build.bat

Note

The build script points to the Windows Driver Kit (WinDDK) 7.600 build environment by default. If building on modern machines, adjust the pathing variables in build.bat to your local MSVC installation or WinSDK location.

Using MinGW-w64 (GCC / Make)

A Makefile is provided for GCC/MinGW-w64 cross-compilers:

make

πŸ“– Usage Guide

Command Line Interface (CLI)

windiag.exe [options]
Command Option Description
-p <pid> Attach debugger to a running process by PID.
-n <name> Find a running process by executable name and attach.
-e <path> Launch a new program and monitor its execution.
-check <path> Perform a static compatibility scan (does not execute the file).
-evtlog Dump the recent Application/System event log error messages.
-l List all currently running active processes.
-v Enable verbose debug logging output.
-deepsnap Enable rolling pre-crash execution snapshots (ring buffer tracking thread/DLL events).
-autoanalyze Auto-run !analyze -v on crash, output report, and terminate target.
-srcpath <path> Specify search directory for source code resolution.

Graphical User Interface (GUI)

Run windiag_gui.exe to launch the vintage monitor layout:

  • Left Panel: Lists active processes running in the system. Use search to filter.
  • Right Panel: Interactive event terminal displaying real-time attaches, DLL loadings, exceptions, and API integrity scan logs.
  • Top Actions Bar:
    • Attach: Attach to the selected process.
    • Launch EXE: Select a file to launch under the debugger.
    • Check File (F3): Perform a static compatibility check on a file.
    • API Hook Scan: Scan the active process for any API hooks/patches.

πŸ•ΉοΈ Interactive Debugger Commands (CLI & GUI)

When actively debugging or monitoring a process (after attaching or launching), you can control the debugger using the following commands in the CLI input or GUI event terminal:

Command Action Example
b <address> Add software breakpoint at the hexadecimal address b 00401234
d <address> Delete/remove breakpoint at the hexadecimal address d 00401234
u <address> [count] Unassemble (disassemble) instructions at address u 00401234 5
g Resume execution ("Go") g
s Step a single instruction s
q Quit monitoring and detach q
!analyze Basic crash analysis (exception + registers + stack) !analyze
!analyze -v Full analysis (+ all-threads + module list) !analyze -v
!analyze --deep Full analysis + pre-crash event log snapshot !analyze --deep

πŸ“Š Example Diagnostic Output Log

Below is an actual output snippet from running windiag.exe -deepsnap -srcpath "." -go -autoanalyze -v -e "wd_test_multithread.exe badcall":

================══════════════ CRASH ANALYSIS ==============================
  Mode: !analyze --deep  (verbose + execution snapshot)
----------------------------------------------------------------------------
--- Exception ---
  Code    : 0xC0000005  ACCESS_VIOLATION
  Address : 0x7741DFF9  ntdll.dll!strcat+0x89
  Flags   : 0x00000000  (continuable)

--- Faulting Instruction ---
  0x7741DFF9  89 17                     mov dword ptr [edi], edx
  EIP page  : state=COMMIT type=IMAGE protect=EXECUTE_READ base=0x7741D000 size=0xAF000

--- Source Code ---
  Source: d:\download\files\src\wd_test_multithread.c : line 187
     184:     if (strcmp(g_bugType, "badcall") == 0) {
     185:         printf("[Thread E] Triggering bad call inside shell32.dll...\n");
     186:         trigger_bad_dll_call();
  >  187:     } else {
     188:         Sleep(INFINITE);
     189:     }
     190:     return 0;

--- Registers ---
  EAX=7EFEFCFE  EBX=00000000  ECX=0041F31C  EDX=73617263
  ESI=00401510  EDI=DEAD0000  ESP=00D6FF54  EBP=00D6FF6C
  EIP=7741DFF9  EFL=00010246
  CS=0023  DS=002B  ES=002B  FS=0053  GS=002B  SS=002B

--- Access Violation Details ---
  Operation : WRITE at 0xDEAD0000
  Diagnosis : kernel-space address from user mode (wild pointer / stack smash)

--- Call Stack (faulting thread) ---
  #00  0x7741DFF9  ntdll.dll!strcat+0x89 [EXT]
  #01  0x00401575  D:\Download\files\wd_test_multithread.exe!thread_badcall_proc+0x65
  #02  0x766E7BA9  C:\WINDOWS\SysWOW64\KERNEL32.DLL!BaseThreadInitThunk+0x19 [EXT]
  #03  0x7740C0CB  ntdll.dll!RtlInitializeExceptionChain+0x6b [EXT]

--- Root Cause Heuristic ---
  Kernel-range address accessed in user mode β€” severe stack corruption or wild pointer.

Explanation of Log:

  1. Module Name Resolution: Even though the process crashed immediately after starting up inside ntdll.dll (where the target process's module tables were not yet initialized), the debugger parsed the PE export directory directly from memory to resolve the base address 0x773A0000 to ntdll.dll instead of (unknown).
  2. Fallback Stack Walking: Since EIP (0x7741DFF9) points into ntdll.dll (no local symbols/source), the analyzer automatically walked back to the first user-space call frame (#01 at thread_badcall_proc+0x65) and displayed the exact source code surrounding the crash trigger.

πŸ”§ Recent Diagnostic & Debugger Enhancements

1. Robust First-Chance Exception Propagation

  • Problem: The debugger previously swallowed all first-chance exceptions by continuing them with DBG_CONTINUE. This broke applications using Structured Exception Handling (SEH) internally (such as Windows system DLLs) and caused infinite resume loops or target process hangs.
  • Fix: Modified the debug loop in wd_debug.c to only pause on hardware exceptions if they are second-chance (actual crashes) or if -firstchance logging is explicitly enabled. Unhandled/non-paused exceptions are now continued with DBG_EXCEPTION_NOT_HANDLED, letting target SEH handlers run natively.

2. PE Export-Based Module Name Resolution

  • Problem: During early process startup (e.g., when ntdll.dll loads), the target's Process Environment Block loader list (PEB.Ldr) is not yet initialized. Debug events like LOAD_DLL_DEBUG_EVENT have lpImageName = NULL, and PSAPI APIs like GetModuleFileNameEx return failure, listing early modules as (unknown).
  • Fix: Implemented a parser that directly reads the DOS and NT headers of loaded DLLs from target process memory, follows the Export Directory RVA (IMAGE_DIRECTORY_ENTRY_EXPORT), and reads the module's own name from its export table. This guarantees that ntdll.dll and other system modules are fully resolved on all Windows platforms (NT 5.0 through Windows 11).

3. Fallback Stack-Walking for System DLL Crashes

  • Problem: When a crash occurs inside a system DLL function (e.g. ntdll.dll!strcpy due to a bad argument), the program counter (EIP) is in system space where no local source symbols exist, preventing the source context code viewer from displaying the cause.
  • Fix: Upgraded the !analyze engine in wd_analyze.c. If the faulting frame (#00) cannot be resolved to source files, the analyzer walks the EBP call stack backwards to find the first caller frame inside the user executable. It then displays the exact source code lines and points to the call site that triggered the system crash (e.g., strcpy((char*)0xDEAD0000, "crash")).

4. Consolidated Test Harness

The multithreaded test suite (wd_test_multithread.exe) has been enhanced with dedicated crash modes:

  • div: Recursively dives and divides by zero to verify call stack tracking and line pointer precision.
  • badcall: Calls ntdll!strcpy with a bad pointer destination to verify that system module crashes are resolved, mapped back to the user call site source line, and captured via deep execution snapshots without deadlocking.

5. Attach PEB Loader Lock Deadlock Resolution

  • Problem: Attaching to complex target processes (such as Supermium or multi-threaded Win32 applications) previously deadlocked during process attach. Querying GetModuleFileNameExA or inspecting target PEB memory before entering WaitForDebugEvent caused the ntdll loader lock to hang.
  • Fix: Replaced early PEB process name lookups in wd_debug.c with Toolhelp32 process snapshotting (CreateToolhelp32Snapshot). Deferred API integrity scans to CREATE_PROCESS_DEBUG_EVENT when the target process debug loop is safely initialized.

6. Expanded Event Log Capacity & Scrollback Buffer

  • Problem: High-density processes emitting dozens of DLL load and thread events on startup would overwrite early attach logs in the GUI event terminal.
  • Fix: Increased EVT_RING capacity to 8,192 events and expanded GUI log scrollback memory (MAX_VISIBLE) to 4,096 lines in wd_common.h. Guaranteed full log history preservation across long debugging sessions.

7. Modern PE Disassembly & Deep Trap Analysis (Supermium / Chromium v144 Trace)

  • Enhancement: Validated WinDiag's deep exception analyzer on modern extended PE binaries (chrome.dll v144).
  • Results: Successfully traced hardware UD2 (0F 0B ILLEGAL_INSTRUCTION) assertion traps down to exact source files (base::File::DoInitialize in base/files/file_win.cc invoked by GetBucketsForStorageKey), and secondary handle write access violations in sql::Statement (sql/statement.cc).

8. Remote Network Debugging & Multi-System Agent Protocol (wd_network.c)

  • Architecture: Added a lightweight, custom TCP socket protocol (WDNE) supporting remote debugging across VMs and network-connected machines.
  • Features:
    • Agent Mode: Runs headless on target VMs/legacy OS instances to monitor process execution, capture crashes, and execute remote debugger commands.
    • Controller Mode: Connects from developer host machine to control remote debugging sessions in real time.
    • Data Streaming: Streams active process lists, event logs, CPU register frames, stack contents, and disassembled instructions over low-latency socket connections.

Builds are clean under standard compiler configurations (MSVC /Od /Oy- /Zi and MinGW make).


πŸ” Practical Debugging Cookbook: Resolving Common Crashes

WINDIAG helps you translate raw assembly and register states into concrete source-code bug fixes. Here is how to handle the most common crash patterns:

Scenario 1: Wild Pointer / NULL Pointer Dereference (0xC0000005)

  • What you see in the log:
    EXCEPTION  0xC0000005  ACCESS_VIOLATION
    Diagnosis : NULL pointer dereference (or Near-NULL dereference)
    Faulting Instruction:
      0x00401234  8B 01                     mov eax, dword ptr [ecx]
    Registers:
      ECX=00000000 (or near-zero like 00000004)
    
  • The Assembly Meaning: The CPU was trying to read (mov) 4 bytes from the address stored in ECX and place it into EAX. Because ECX is 0, this is a read access violation.
  • How to Fix the Code: Locate the source line (either resolved by -srcpath or from the stack walk). Find the variable corresponding to ecx. It was not initialized before use, or a function returned a NULL pointer that you forgot to validate. Add a guard check:
    if (my_pointer == NULL) {
        /* handle error */
        return;
    }

Scenario 2: Memory Corruptions / Bad API Calls

  • What you see in the log:
    EXCEPTION  0xC0000005  ACCESS_VIOLATION
    Diagnosis : kernel-range address from user mode (wild pointer)
    Faulting Instruction:
      0x7741DFF9  89 17                     mov dword ptr [edi], edx
    Registers:
      EDI=DEAD0000
    
  • The Assembly Meaning: The CPU was attempting to write (mov) the contents of EDX into the memory address stored in EDI. Since EDI is 0xDEAD0000 (which is outside valid user-mode boundaries), the OS terminated it.
  • How to Fix the Code: This is a classic "bad call" where a bad parameter was passed to a string copy or buffer manipulation function (like strcpy). Find the user code caller frame in the stack trace. Check the pointer argument you are passing to that system function and ensure it points to a valid, allocated memory block.

Scenario 3: Stack Buffer Overrun / Stack Smash

  • What you see in the log:
    Diagnosis : Kernel-range address accessed in user mode
    WARNING: EIP is within the stack region β€” strong indicator of stack buffer overflow!
    Call Stack:
      ... EBP misaligned β€” stack may be corrupt, stopping walk
    
  • The Assembly Meaning: A local array/buffer allocated on the stack was written to beyond its bounds, overwriting the saved frame pointer (EBP) and function return address. When the function tried to return, execution jumped to a corrupt stack location.
  • How to Fix the Code: Find the function shown just before the stack walk failed. Inspect its local stack buffers (e.g. char buffer[128]). Ensure you are using bounded functions. Replace dangerous functions with safe equivalents:
    /* AVOID: */
    strcpy(buf, input);
    /* USE: */
    strncpy(buf, input, sizeof(buf) - 1);
    buf[sizeof(buf) - 1] = '\0';

Scenario 4: Integer Division by Zero (0xC0000094)

  • What you see in the log:
    EXCEPTION  0xC0000094  INTEGER_DIVIDE_BY_ZERO
    Faulting Instruction:
      0x00401250  F7 F9                     idiv ecx
    Registers:
      ECX=00000000
    
  • The Assembly Meaning: The idiv instruction divides the accumulator registers by the divisor register (ECX). Division by zero is a hardware exception.
  • How to Fix the Code: Find the division (/) or modulo (%) operation at the highlighted source line. Add a conditional check to ensure the divisor is non-zero:
    if (divisor == 0) {
        /* handle division by zero */
        return 0;
    }

βš–οΈ How WINDIAG Compares to Legacy and Modern Tools

WINDIAG offers a unique value proposition, especially for developers working in retro-computing, legacy software maintenance, or security research. Below is a comparison of WINDIAG against official Microsoft and third-party tools:

Feature / Capability WINDIAG WinDbg Dr. Watson Dependency Walker AppVerifier
Windows 2000+ Compatibility Yes (Native C89) No (Modern requires .NET/Win10+) Yes (Legacy only) Yes (Legacy only) No (Requires modern OS)
Interactive CLI & GUI Yes (Retro Phosphor CRT UI) Yes (Modern ribbon UI) No (Non-interactive) No (Static-only UI) No (Configuration UI only)
Zero-Source Diagnostics Yes (Deep analysis, disassembly, and stack walk) Yes Yes (Limited) No No (Kernel logs only)
API Hook & Integrity Scanner Yes (Scans target memory for patches/hooks) No No No No
Static Compatibility Checks Yes (Scans imports and manifest Vista+ symbols) No No Yes No
Rolling execution snapshot Yes (Lock-free ring buffer execution history) No No No No
Resource Footprint Ultra-Light (< 200 KB, zero runtime dependencies) Heavy Light Light Heavy

Key Advantages:

  1. Zero-Source Diagnostics: Even without source code or local symbol packages (PDB), WINDIAG decodes raw memory accesses, disassembles the faulting instruction, lists registers, and resolves exported DLL symbols (e.g. ntdll.dll!strcat+0x89) so you can identify "bad calls" and their calling origins instantly.
  2. Combination of Static & Dynamic Analysis: Unlike traditional tools that are either debuggers (WinDbg) or static scanners (Dependency Walker), WINDIAG lets you statically check an executable's compatibility beforehand and debug it dynamically on the fly.
  3. Advanced Anti-Tampering & Integrity Checking: WINDIAG scans memory pages to detect if APIs have been hooked or patched (often by antivirus software, debuggers, or hooks), a feature not natively present in other debuggers.
  4. Pre-Crash Flight Recorder (-deepsnap): Tracks thread creation, library loadings, and first-chance exceptions in a high-speed lock-free circular buffer, allowing you to view the events leading right up to a crash.

About

No description, website, or topics provided.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages