New path and certification for beginners (25% OFF Silver Annual Plan - for a limited time only) Learn More

Introduction to Dynamic Analysis with WinDbg

This module covers the fundamentals of using the debugger and examines various practical cases where dynamic analysis helps uncover malware TTPs on Windows. It extends to both user-mode and kernel-level debugging, demonstrating how to detect behaviors associated with rootkits or exploits, and how the internal structures of the kernel can reveal the presence of malware.

5.00

Created by Yazidou
Co-Authors: MadhukarRaina

Hard Defensive

Summary

Static analysis has many limitations when it comes to malware analysis and reverse engineering, especially as malware Tactics, Techniques, and Procedures (TTPs) evolve to maximize stealth and hide their activities. In this context, dynamic analysis has become a critical skill in the field of malware analysis. WinDbg stands out as the standard tool for debugging on Windows. This powerful debugger provides all the necessary tools to trace and analyze processes or the Windows operating system kernel at the most granular level, offering exceptional granularity and unique flexibility.

Through this module, we will cover the following topics:

  • Getting started with WinDbg and mastering its basic commands
  • Understanding LINQ methods in WinDbg for advanced data querying
  • Analyzing and extracting code injections using dynamic analysis techniques
  • Leveraging Time Travel Debugging (TTD) technology to dissect process behavior and extract payloads
  • Introduction to kernel-mode debugging, including key concepts and structures like the Virtual Address Descriptor (VAD) Tree
  • Exploring kernel debugging from a defensive perspective to uncover and counter Tactics, Techniques, and Procedures (TTPs) used in malicious or exploited drivers
  • Utilizing JavaScript in WinDbg to design scripts for automating process data extraction and analysis tasks in both user and kernel modes
  • Hands-on experience with debugging techniques to analyze stealth mechanisms and various TTPs, such as Process Hollowing methods

This module will provide a comprehensive understanding of WinDbg's capabilities, from basic usage to advanced debugging scenarios, while emphasizing practical applications for both offensive and defensive analysis. The module is accompanied by concrete use cases, examples, and challenges where students can let their creativity and acquired skills shine through.

To easily follow this module, the following prerequisites are highly recommended:

  • Completion of Intro to Assembly Language
  • Completion of Introduction to Malware Analysis
  • Basic understanding of C/C++ and structures
  • A general understanding of the Win32 API and an idea of how processes & threads work on Windows
  • Basic understanding of the Portable Executable (PE) format and its components

Introduction to WinDbg


Debugger

A debugger is a software that helps developers analyze, test, and troubleshoot programs by allowing them to inspect and control the execution of code. It lets us pause execution, step through code line-by-line, inspect variables, and examine memory to identify bugs or understand program behavior. WinDbg, short for Windows Debugger, is a powerful debugger for Windows, widely used for both user-mode (application) and kernel-mode (operating system) debugging. The debugger attaches to a target, which could be a user-mode process (an application), a kernel (the operating system core), or a crash dump file. For a running process, the debugger uses Windows Debugging API such as DebugActiveProcess to hook into the process. For kernel debugging, it communicates with the OS kernel via specific protocols (e.g., network, serial cable, or local mode).

The diagram below shows the debugging modes. We have a Host Machine (running WinDbg) which attaches to a process’s memory and controlling execution. It can also perform local kernel debugging and remote kernel debugging on a target host.

Diagram of a debugging setup: Host machine runs WinDbg, connects to a remote machine for kernel debugging. Local kernel reads memory; debugger downloads .pdb files from a symbol server. User process is debugged with breakpoints. Connection options: NET, COM, Serial port, USB. Remote kernel allows full control.

WinDbg uses Microsoft’s debugging engine (DbgEng.dll) and supports both user-mode and kernel-mode debugging. It provides a command-line interface (with commands like bp for breakpoints, g for go, k for stack trace) and a GUI for ease of use. It relies heavily on symbols (debug information in .pdb files) to map binary code to source code, making it easier to understand variables and functions.

WinDbg

WinDbg is the official debugger for the Windows operating system. It is developed and maintained by Microsoft. Beyond its traditional role in assisting developers of applications or drivers during their development, it has also become a useful tool for malware analysts, vulnerability researchers, exploit developers, and reverse engineers.

Although WinDbg has become the debugging standard on Windows, its older version featured a UI that evolved significantly over the debugger's versions but increasingly failed to provide user-friendly ergonomics over time, especially on recent Windows versions. The interface became somewhat outdated and cumbersome:

WinDbg interface showing disassembly, memory, and processes. Disassembly window highlights a syscall instruction. Memory window displays hexadecimal data. Processes window lists threads for PowerShell.

It is in this context that Microsoft completely overhauled the user interface by releasing WinDbgX, which is now the version referred to when discussing WinDbg:

WinDbg interface with command, disassembly, and memory windows. Command window shows assembly instructions for CreateDialogParamW. Disassembly window highlights current instruction. Memory window displays hexadecimal data. Debugging controls are visible at the top.


Installing WinDbg

WinDbg can be installed either from the Microsoft Store or directly from the Microsoft website.

WinDbg app page showing logo, screenshots, and description. Features include modern visuals, faster windows, and Time Travel Debugging. Rated 4.6 stars. Compatible with Windows 10 Pro and Home.

The graphical interface of WinDbg offers a comprehensive range of features organized intuitively to facilitate the debugging process. Let's explore some of the essential views offered by this GUI.

WinDbg interface with no active debugging session. Toolbar includes controls for stepping and debugging. Command window states "Debuggee not connected." Locals and Threads panels are empty.

Selecting a Process to Debug

Let’s start from the beginning by choosing a process to debug. There are actually two ways to do this:

  • Launch a new process in debugger
  • Attach debugger to a running process

We'll see how this can be done.

  1. We can create a process that will be automatically debugged from WinDbg. To do this, we can go to File, select "Start Debugging", and choose between "Launch Executable" or "Launch Executable (Advanced)" for more parameters, including arguments, target architecture, and enabling Time Travel Debugging (more on this later).

Start debugging menu with options: Launch executable, Launch executable (advanced), Attach to process, Open dump file, Open trace file, Connect to remote debugger. Advanced launch supports Time Travel Debugging. Fields for executable path, arguments, start directory, and target architecture. Options to debug child processes and record with Time Travel Debugging.

  1. We can also attach the debugger to a running process by selecting "Attach to Process", which will pause the process and transfer control of its execution to the debugger.

Start debugging menu with options: Launch executable, Attach to process, Open dump file. Process list shows running applications like chrome.exe and RuntimeBroker.exe with details like PID, platform, and command line.

If we choose to launch a process from the debugger, the debugger will position itself at the very beginning of the process initialization flow (i.e., at the level of LdrInitializeThunk) and take control:

Animated GIF of WinDbg interface showing the process of launching and attaching to a process for debugging. Steps include selecting an executable, configuring options, and starting the debugging session.

When attaching the debugger to a running process, it will interrupt the process by invoking the DbgUiRemoteBreakin() function:

Animated GIF of WinDbg interface demonstrating how to debug a specific process by entering its PID. Steps include accessing the attach process menu, inputting the PID, and initiating the debugging session.

For any of the functions we will discuss later, more information about them can be obtained using the .hh command followed by the function name. In the following example, we will retrieve the documentation for the !address command:

Animated GIF of WinDbg interface showing the use of the help command. The user types the command to access available debugging commands and their descriptions.

We can use the .cls command to clear the console's contents.

Animated GIF of WinDbg interface demonstrating the use of the cls command to clear the console window.

We can stop the execution flow of a process being debugged by either pressing "Break" or using the Alt+Del combination. The execution flow can then be resumed with the g command:

Animated GIF of WinDbg interface showing how to pause and resume a debugging session using toolbar controls.


WinDbg Views

WinDbg is composed of different panes called "views", and each one of them serves a specific purpose in debugging a program. We will start off by explaining the views that we will be using.

  • Command View: This view is where we interact with the debugger through various commands and expressions that we will explore throughout this module.

    WinDbg command window displaying module load information and a break instruction exception. Lists loaded DLLs and shows a breakpoint at ntdll!RtlpDoDebuggerBreak+0x30.

  • Registers View: It displays the state of the CPU registers for the current thread (we will later see how to switch threads). We can enable or disable the display of various register groups (User, SIMD, Floating Point, CET) by right-clicking.

    WinDbg registers window displaying CPU register values, including RAX, RBX, and others. EFLAGS and error values are shown. A menu indicates options for User, SIMD, FloatingPoint, and CET are selected.

  • Disassembly View: It provides a view of the instructions executed by the execution flow of the program being debugged. @$scopeip is a variable that gives the address of the instruction pointer (RIP), or the address of the current instruction. However, we can also specify a different address.

    WinDbg disassembly window showing assembly instructions for ntdll!LdrpDoDebuggerBreak. Instructions include sub, and, mov, call, test, and jmp. The current instruction is highlighted.

  • Threads View: It provides a list of the threads currently running in the debugged process. It also shows the current address where the execution flow of each thread is located.

    Threads window showing a list of threads with TID, Index, and Thread details. Includes PE_Injector!mainCRTStartup and multiple ntdll!TppWorkerThread entries.

  • Call Stack View: It provides the call stack state of the current thread. It includes details such as Child-SP, which is the value of the stack pointer (RSP), as well as the address of the function to which the frame's RSP points.

    Stack window displaying call stack information with Frame Index, Call Site, Child-SP, and Return Address. Includes entries like ntdll!LdrpDoDebuggerBreak+0x30 and ntdll!LdrpInitializeProcess+0x1eda.

  • Memory View: It provides a view of the data contained in memory at a specific point during execution. We can specify an address, just like in the Disassembly View.

    Memory window displaying hexadecimal data and corresponding ASCII characters. Address range starts at 00007FFC9620690. Data includes various byte values and their ASCII interpretations.


Debugging Symbols

Microsoft provides a public symbol server that hosts symbols for debugging purposes. These symbols are essential for debugging as they map memory addresses to meaningful names. If everything goes well, when launching a process or attaching to one, WinDbg automatically loads the symbol files from Microsoft's public server without requiring any manual intervention. We can use !sympath to check the Microsoft public server link:

0:013> !sympath

Symbol search path is: srv*;SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols
Expanded Symbol search path is: cache*;SRV*https://msdl.microsoft.com/download/symbols;srv*c:\symbols*http://msdl.microsoft.com/download/symbols

We can search for specific function symbols using the x command, which accepts wildcards. Here, we're looking for all functions within ntdll.dll:

0:013> x ntdll!*

00007ff8`70a9aebc ntdll!RtlpHpVaMgrRangeCreate (void)
00007ff8`70ace3a0 ntdll!RtlFindLastBackwardRunClear (void)
<SNIP>

It is important to note that almost all structure symbols are prefixed with an underscore _. Therefore, we can search for all structures contained in ntdll.dll using the following command:

0:013> dt ntdll!_*

ntdll!_ARM64_FPCR_REG
ntdll!_ARM64_FPSR_REG
ntdll!_AMD64_MXCSR_REG
<SNIP>

We can also search for the closest symbol to a specific address using the ln command. For example, to find the symbol associated with the function containing the address 00007ff8'7014ef54:

0:013> ln 00007ff8`7014ef54

<SNIP>
(00007ff8`7014ef50)   USER32!CreateDialogParamW+0x4   |  (00007ff8`7014efe4)   USER32!xxxLBShowHideScrollBars

The ld command allows us to load the symbols for a specific module. Here, we are reloading the symbols for all modules:

0:013> ld *

Symbols already loaded for Microsoft_PowerShell_ConsoleHost_resources
Symbols already loaded for System_Management_Automation_resources
Symbols already loaded for mscorlib_resources
<SNIP>

The same action can be performed using the .reload command:

0:013> .reload

Reloading current modules
................................................................

In case of issues with symbols, we can reset the link to Microsoft's public symbol server using the .symfix command while specifying the directory where the symbols should be cached. By default, WinDbg uses C:\Symbols as the cache directory:

0:013> .symfix C:\Symbols

The files retrieved from Microsoft's public symbol server are PDB files, a type of file that contains various debugging information about a module. In the C:\Symbols\ directory, we will find all the PDB files downloaded from the public server. Each one of them is associated with a specific module:

C:\> dir C:\Symbols

17/02/202X  18:42    <DIR>          .
24/02/202X  13:01    <DIR>          advapi32.pdb
17/02/202X  18:38    <DIR>          apphelp.pdb
04/01/202X  18:15    <DIR>          apphost.pdb
09/02/202X  20:07    <DIR>          AsUpIO64.pdb
17/02/202X  18:36    <DIR>          bcrypt.pdb
17/02/202X  18:38    <DIR>          bcryptprimitives.pdb
13/11/202X  15:39    <DIR>          BTAGService.pdb
24/02/202X  13:01    <DIR>          combase.pdb
<SNIP>

In this module, when we'll spawn the target (VM) in the interactive sections, we'll see two machines.

  • ACADEMY-WINDBG-ANALYSIS: This is the Main target (VM), where we'll perform debugging on applications/processes running on the local machine. Additionally, we'll debug the local Windows kernel on the same machine as WinDbg is running. We'll also perform the remote kernel debugging of the second machine from this VM.
  • ACADEMY-WINDBG-REMOTE: This is the The remote system which needs to be booted with special debug settings. We'll use it for debugging the remote kernel of this remote system from our local machine (ACADEMY-WINDBG-ANALYSIS).

Section for answering questions to earn cubes. Lists targets for WinDbg (debugger) and Remote VM (debuggee) with IP addresses. Option to download VPN connection file.

Sign Up / Log In to Unlock the Module

Please Sign Up or Log In to unlock the module and access the rest of the sections.