WinDbg Introduction
We can use WinDbg to inspect or modify code execution at the assembly level
on both 32-bit and 64-bit versions of Windows.
While there are other debuggers, such as the popular Immunity Debugger,
most lack 64-bit support.
We’ll begin by discussing how to attach to a running process.
Let’s open Notepad,
and run WinDbg from the taskbar.
In WinDbg, we can attach to the Notepad process through the File menu
or by pressing the F6 key.
We’ll locate notepad.exe,
select it, and click OK to attach.
Once WinDbg attaches to the process, it pauses the application execution flow
so we can interact with the process through the debugger.
Although we can customize the WinDbg window layout, we’ll use a fairly basic
setup consisting of only two windows: the Disassembly window in the upper pane,
and the Command window in the lower pane
With WinDbg running and attached to a process, our goal is to inspect the
execution context at a specific location,
step through individual instructions, and dump contents of registers and memory.
We’ll use breakpoints to stop program execution at a specific location.
WinDbg supports several different breakpoint types but we’ll use a software
breakpoint set at a specific address or code location.
We can set a breakpoint with the bp command followed by a memory address
or the name of a function.
In this example, we’re setting a breakpoint on the WriteFile function,
which is exported by the kernel32 dynamic link library.
This function is called whenever a write operation to a file
is performed by an application.
After defining the breakpoint we’ll continue execution with the “g” command
To trigger our breakpoint, we’ll enter some text into Notepad...
...and save the file.
When any thread reaches the function, the debugger will stop the execution flow
and we can view and modify registers and memory.
With the execution halted, let’s step through a single assembly instruction
at a time with the “p” command.
If we want to view the next instructions,
we can unassemble...
...a specific address location, typically RIP.
We can use the “L” flag to specify the number of instructions to display.
In this example, we’ll unassemble the next five instructions.
We can view all registers with the “r” command.
We can also inspect individual registers by specifying the name of the register
For a more detailed view, if a register contains a valid address,
we can inspect the content of that memory area with the “dd”,
“dc”,
and “dq” commands,
which will dump memory content formatted as 32-bit values,
32-bit values with ASCII representation,
and as 64-bit values, respectively.
These commands will also dump the contents of memory at any address
In addition to inspecting the contents of a memory location,
we can also modify memory content.
For example, we could modify a memory location to force an execution path
that could aid or speed up our analysis.
Let’s modify a DWORD using the “ed” command,
followed by the memory address we wish to edit,
and the new value:
This basic tutorial forms the foundation for the reverse engineering
we’ll perform to ultimately bypass AMSI.