Getting Started with WinDbg
January 5, 2021 - Posted by Gianni Gnesa
The Windows Debugger, also known as Win-Dee-Bee-Gee is one, if not THE, most popular tool used by reverse engineers and exploit developers to understand how an application works. It has hundreds of commands that allow you to step through the code, analyze crashes, gather information about loaded modules and images, inspect memory, work with symbols, get process or thread related information, and much more.
With so many commands, it can be difficult to analyze an application in WinDbg. That’s why I decided to go through some of the most useful commands and show you how to use them. First, let’s open our target application inside WinDbg. In this case, our target application is WinRar 6.0.0 (x64).

Every time you open an application inside WinDbg or attach WinDbg to a running process, the process is immediately paused. This allows you to gather some information about your target and set breakpoints.

So, let’s start by getting some information about our target with the !peb command, a command that dumps the content of the Process Environment Block (PEB).
0:000> !peb
PEB at 0000001fe0ba4000
InheritedAddressSpace: No
ReadImageFileExecOptions: No
BeingDebugged: Yes
ImageBaseAddress: 00007ff6e5760000
NtGlobalFlag: 70
NtGlobalFlag2: 0
Ldr 00007fff1120f3a0
Ldr.Initialized: Yes
Ldr.InInitializationOrderModuleList: 000001f021862870 . 000001f021863100
Ldr.InLoadOrderModuleList: 000001f021862a20 . 000001f02186dda0
Ldr.InMemoryOrderModuleList: 000001f021862a30 . 000001f02186ddb0
Base TimeStamp Module
7ff6e5760000 5fc684ae Dec 01 19:00:14 2020 C:\Program Files\WinRAR\WinRAR.exe
7fff110b0000 20af46db May 18 19:37:31 1987 C:\Windows\SYSTEM32\ntdll.dll
7fff0f080000 6edcef78 Dec 09 12:09:44 2028 C:\Windows\System32\KERNEL32.DLL
7fff0d950000 91fb2582 Aug 11 18:57:38 2047 C:\Windows\System32\KERNELBASE.dll
7fff10c80000 2aafc763 Sep 11 00:08:35 1992 C:\Windows\System32\USER32.dll
7fff0dc70000 1900dcc9 Apr 18 06:08:41 1983 C:\Windows\System32\win32u.dll
7fff0f520000 3d1ed208 Jun 30 11:40:24 2002 C:\Windows\System32\GDI32.dll
7fff0d4b0000 c18abd9e Nov 23 03:53:18 2072 C:\Windows\System32\gdi32full.dll
7fff0e4e0000 1d1dafdc Jun 24 20:13:48 1985 C:\Windows\System32\msvcp_win.dll
7fff0dc90000 27fc3e10 Apr 05 11:06:24 1991 C:\Windows\System32\ucrtbase.dll
7fff0eb90000 f7d4a12d Oct 05 00:14:05 2101 C:\Windows\System32\COMDLG32.dll
7fff0e580000 20688290 Mar 26 02:21:20 1987 C:\Windows\System32\msvcrt.dll
7fff0eca0000 ef941eb1 May 15 15:10:09 2097 C:\Windows\System32\combase.dll
7fff10f30000 d066af57 Oct 17 13:34:47 2080 C:\Windows\System32\RPCRT4.dll
7fff0d6b0000 0984de7c Jan 23 10:50:52 1975 C:\Windows\System32\bcryptPrimitives.dll
7fff10e10000 76e52162 Mar 18 04:02:58 2033 C:\Windows\System32\shcore.dll
7fff0e620000 2303fb66 Aug 13 10:18:46 1988 C:\Windows\System32\SHLWAPI.dll
7fff0f760000 8971038d Jan 26 12:37:49 2043 C:\Windows\System32\SHELL32.dll
7fff0d900000 a7a2bb4e Feb 14 15:50:54 2059 C:\Windows\System32\cfgmgr32.dll
7fff0dd90000 71998de0 May 24 23:32:16 2030 C:\Windows\System32\windows.storage.dll
7fff0f560000 44ac0c41 Jul 05 21:00:17 2006 C:\Windows\System32\advapi32.dll
7fff0f4c0000 f030acf4 Sep 11 09:10:44 2097 C:\Windows\System32\sechost.dll
7fff0d440000 c779bd8b Jan 19 08:26:03 2076 C:\Windows\System32\kernel.appcore.dll
7fff0d460000 883ace6c Jun 05 06:28:28 2042 C:\Windows\System32\powrprof.dll
7fff0d420000 ce335499 Aug 17 06:01:29 2079 C:\Windows\System32\profapi.dll
7fff0f610000 1e6c2994 Mar 05 12:10:12 1986 C:\Windows\System32\ole32.dll
7fff0efb0000 9aa147c8 Mar 17 05:02:16 2052 C:\Windows\System32\OLEAUT32.dll
7ffefa3b0000 1129a048 Feb 15 16:31:20 1979 C:\Windows\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.16299.2166_none_0f8e8cac5a673ae0\COMCTL32.dll
7fff0ba30000 d4c34281 Feb 11 11:12:17 2083 C:\Windows\SYSTEM32\UxTheme.dll
7ffef7080000 ebfce134 Jun 18 08:54:44 2095 C:\Windows\WinSxS\amd64_microsoft.windows.gdiplus_6595b64144ccf1df_1.1.16299.2166_none_40797f91b4f9b4ae\gdiplus.dll
7fff07d10000 8f22dbf7 Feb 05 07:53:43 2046 C:\Windows\SYSTEM32\MSIMG32.dll
SubSystemData: 0000000000000000
ProcessHeap: 000001f021860000
ProcessParameters: 000001f021861fa0
CurrentDirectory: 'C:\Users\user\Desktop\'
WindowTitle: 'C:\Program Files\WinRAR\WinRAR.exe'
ImageFile: 'C:\Program Files\WinRAR\WinRAR.exe'
CommandLine: '"C:\Program Files\WinRAR\WinRAR.exe"'
DllPath: '< Name not readable >'
Environment: 000001f021861100
=::=::\
ALLUSERSPROFILE=C:\ProgramData
APPDATA=C:\Users\user\AppData\Roaming
CommonProgramFiles=C:\Program Files\Common Files
CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
CommonProgramW6432=C:\Program Files\Common Files
COMPUTERNAME=DESKTOP-A6H6EG0
ComSpec=C:\Windows\system32\cmd.exe
FPS_BROWSER_APP_PROFILE_STRING=Internet Explorer
FPS_BROWSER_USER_PROFILE_STRING=Default
HOMEDRIVE=C:
HOMEPATH=\Users\user
LOCALAPPDATA=C:\Users\user\AppData\Local
LOGONSERVER=\\DESKTOP-A6H6EG0
NUMBER_OF_PROCESSORS=1
OneDrive=C:\Users\user\OneDrive
OneDriveConsumer=C:\Users\user\OneDrive
OS=Windows_NT
Path=C:\Program Files (x86)\Windows Kits\10\Debuggers\x64;C:\Python27\;C:\Python27\Scripts;C:\Program Files (x86)\Python37-32\Scripts\;C:\Program Files (x86)\Python37-32\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Users\user\AppData\Local\Microsoft\WindowsApps;
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW
PROCESSOR_ARCHITECTURE=AMD64
PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 78 Stepping 3, GenuineIntel
PROCESSOR_LEVEL=6
PROCESSOR_REVISION=4e03
ProgramData=C:\ProgramData
ProgramFiles=C:\Program Files
ProgramFiles(x86)=C:\Program Files (x86)
ProgramW6432=C:\Program Files
PSModulePath=C:\Program Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules
PUBLIC=C:\Users\Public
SESSIONNAME=Console
SRCSRV_SHOW_TF_PROMPT=1
SRCSRV_TIMEOUT_SECONDS=300
SystemDrive=C:
SystemRoot=C:\Windows
TEMP=C:\Users\user\AppData\Local\Temp
TMP=C:\Users\user\AppData\Local\Temp
USERDOMAIN=DESKTOP-A6H6EG0
USERDOMAIN_ROAMINGPROFILE=DESKTOP-A6H6EG0
USERNAME=user
USERPROFILE=C:\Users\user
WINDBG_DIR=C:\Program Files (x86)\Windows Kits\10\Debuggers\x64
windir=C:\Windows
_NT_SYMBOL_PATH=srv*c:\symbols*http://msdl.microsoft.com/download/symbols
If we go through the !peb command’s output, we can see a lot of information about the target, including the location in memory of some key elements (e.g. heap), the list of modules and their base address, as well as the environment variables.
To get additional information about the loaded modules, one can take advantage of the lm command. The lm command is used to list modules and if used in conjunction with the v parameter, it provides a wealth of information. For example, let’s say we want to get more information about the ntdll module. In that case, we would use the following command.
0:000> lmv m ntdll
Browse full module list
start end module name
00007fff`110b0000 00007fff`11290000 ntdll (pdb symbols) c:\symbols\ntdll.pdb\D1465676F2C4BFFF8390E9F1EA4CC4AA1\ntdll.pdb
Loaded symbol image file: C:\Windows\SYSTEM32\ntdll.dll
Image path: ntdll.dll
Image name: ntdll.dll
Browse all global symbols functions data
Image was built with /Brepro flag.
Timestamp: 20AF46DB (This is a reproducible build file hash, not a timestamp)
CheckSum: 001DF94D
ImageSize: 001E0000
File version: 10.0.16299.1806
Product version: 10.0.16299.1806
File flags: 0 (Mask 3F)
File OS: 40004 NT Win32
File type: 2.0 Dll
File date: 00000000.00000000
Translations: 0409.04b0
Information from resource tables:
CompanyName: Microsoft Corporation
ProductName: Microsoft® Windows® Operating System
InternalName: ntdll.dll
OriginalFilename: ntdll.dll
ProductVersion: 10.0.16299.1806
FileVersion: 10.0.16299.1806 (WinBuild.160101.0800)
FileDescription: NT Layer DLL
LegalCopyright: © Microsoft Corporation. All rights reserved.
Okay. We now know how to get the list of loaded modules and retrieve some information about a specific module. What’s next? Well, modules typically provide functionality through exported functions also known as symbols. To see the symbols of a module, for example the kernel32 module, we can use the x command as follows:
0:000> x kernel32!*
00007fff`0f0a4dae KERNEL32!ThpCreateRawSnap$filt$0 (void)
00007fff`0f0a4ae0 KERNEL32!GlobalUnlock$filt$0 (void)
00007fff`0f090c14 KERNEL32!SdbpCleanupLocalDatabaseSupport (void)
00007fff`0f0a02b0 KERNEL32!CreateActCtxA (void)
00007fff`0f0d5368 KERNEL32!RtlStringCopyWideCharArrayWorker (void)
00007fff`0f0a4c69 KERNEL32!GlobalSize$fin$1 (void)
00007fff`0f093450 KERNEL32!QueryInformationJobObject (void)
00007fff`0f0c6c8c KERNEL32!Internal_InvokeSwitchCallbacksOnINIT (void)
00007fff`0f094250 KERNEL32!RegisterWaitForSingleObject (void)
00007fff`0f091dc8 KERNEL32!BasepFindActCtxSection_CheckAndConvertParameters (void)
00007fff`0f0a4928 KERNEL32!BaseDllReadWriteIniFileOnDisk$fin$0 (void)
00007fff`0f094d20 KERNEL32!GetLongPathNameW (void)
…
00007fff`0f0f54a8 KERNEL32!_imp_GetProcessPriorityBoost = <no type information>
00007fff`0f0b6884 KERNEL32!RtlStringCchPrintfW (<no parameter info>)
00007fff`0f0f4b00 KERNEL32!_imp_MoveFileWithProgressW = <no type information>
00007fff`0f0f3270 KERNEL32!ByteMatchSectionInstallers = <no type information>
00007fff`0f0e77a0 KERNEL32!QuirkIsEnabledForProcessWorker (<no parameter info>)
00007fff`0f09e3b0 KERNEL32!GetConsoleWindow (<no parameter info>)
00007fff`0f0f48a8 KERNEL32!_imp_ReadFile = <no type information>
00007fff`0f0f3400 KERNEL32!ByteMatchAppendedDataInstallers = <no type information>
00007fff`0f0f63c0 KERNEL32!_imp_wcsncpy = <no type information>
00007fff`0f0d6220 KERNEL32!BackupReadGhostedFileExtents (<no parameter info>)
00007fff`0f0f4318 KERNEL32!_imp_AppXPostSuccessExtension = <no type information>
00007fff`0f0b7160 KERNEL32!GetProcessWorkingSetSizeExStub (<no parameter info>)
00007fff`0f0f4340 KERNEL32!_imp_GetUnicodeStringToEightBitStringRoutine = <no type information>
Not surprisingly, the list of symbols for the kernel32 module is quite long. To see only symbols that contain a specific keyword, we can use the following syntax:
x modulename!*keyword*
Here is an example, to see only symbols in the kernel32 module that contain the word “File”.
0:000> x kernel32!*File*
00007fff`0f0a4928 KERNEL32!BaseDllReadWriteIniFileOnDisk$fin$0 (void)
00007fff`0f0eb1b8 KERNEL32!AslpFileGetImageNtHeader$filt$0 (void)
00007fff`0f08e310 KERNEL32!BaseDllReadWriteIniFileOnDisk (void)
00007fff`0f0a494c KERNEL32!AslpFileMappingGetFileKind$filt$0 (void)
00007fff`0f0a0ce0 KERNEL32!GetPrivateProfileSectionA (void)
00007fff`0f091be0 KERNEL32!GetPrivateProfileSectionW (void)
00007fff`0f08f5cc KERNEL32!AslFileMappingCreate (void)
00007fff`0f090b4c KERNEL32!AslFileMappingDelete (void)
00007fff`0f0a17d0 KERNEL32!WritePrivateProfileSectionW (void)
00007fff`0f0eac31 KERNEL32!AslpFileGetExeWrapper$filt$0 (void)
00007fff`0f0ebadc KERNEL32!AslpFileGetVersionBlock$filt$0 (void)
00007fff`0f0e5f2a KERNEL32!WerpUnregisterFile$fin$0 (void)
00007fff`0f0a05f0 KERNEL32!GetPrivateProfileIntA (void)
...
00007fff`0f09f450 KERNEL32!WerRegisterFileStub (<no parameter info>)
00007fff`0f0a2f20 KERNEL32!SetFilePointer (<no parameter info>)
00007fff`0f0b75a0 KERNEL32!OpenFileByIdStub (<no parameter info>)
00007fff`0f0a2be0 KERNEL32!FindFirstFileNameW (<no parameter info>)
00007fff`0f0f5600 KERNEL32!_imp_K32GetModuleFileNameExW = <no type information>
00007fff`0f0f48e0 KERNEL32!_imp_SetFileAttributesW = <no type information>
00007fff`0f0b6fd0 KERNEL32!GetCompressedFileSizeAStub (<no parameter info>)
00007fff`0f0fb508 KERNEL32!ENCRYPTFILEEX_NAME = <no type information>
00007fff`0f0f5540 KERNEL32!_imp_K32GetModuleFileNameExA = <no type information>
00007fff`0f08ecb0 KERNEL32!BaseDllIniFileNameLength (<no parameter info>)
00007fff`0f0e7c50 KERNEL32!AslFileNotFound (<no parameter info>)
00007fff`0f0f4b00 KERNEL32!_imp_MoveFileWithProgressW = <no type information>
00007fff`0f0f48a8 KERNEL32!_imp_ReadFile = <no type information>
00007fff`0f0d6220 KERNEL32!BackupReadGhostedFileExtents (<no parameter info>)
Despite being a bit shorter the list still counts dozens of functions. Nonetheless, if we scroll through the list, it is now possible to spot interesting functions like the KERNEL32!ReadFile or the KERNEL32!WriteFile, and their implementations (e.g. KERNEL32!_imp_ReadFile, KERNEL32!_imp_WriteFile, etc.).
Now, let’s assume we want to see the files WinRar opens. The function that is typically used for that is the CreateFile(). Using the command syntax seen a moment ago, we can look for every instance of the “CreateFile” string.
0:000> x kernel32!CreateFile*
00007fff`0f0a4f70 KERNEL32!CreateFileTransactedW$fin$0 (void)
00007fff`0f0a1fd0 KERNEL32!CreateFileTransactedW (void)
00007fff`0f09bf40 KERNEL32!CreateFileMappingA (<no parameter info>)
00007fff`0f0b6b30 KERNEL32!CreateFileMappingNumaWStub (<no parameter info>)
00007fff`0f0dabb0 KERNEL32!CreateFileMappingNumaA (<no parameter info>)
00007fff`0f09c750 KERNEL32!CreateFileMappingWStub (<no parameter info>)
00007fff`0f0da3b0 KERNEL32!CreateFileTransactedA (<no parameter info>)
00007fff`0f0a2af0 KERNEL32!CreateFile2 (<no parameter info>)
00007fff`0f0a2b10 KERNEL32!CreateFileW (<no parameter info>)
00007fff`0f0a2b00 KERNEL32!CreateFileA (<no parameter info>)
In this case, there are ten symbols that contain the “CreateFile” string, but only one is likely to be used by WinRar: KERNEL32!CreateFileW.
HANDLE __fastcall CreateFileW(LPCWSTR lpFileName, DWORD
HANDLE RAX:8 <RETURN>
LPCWSTR RCX:8 lpFileName
DWORD EDX:4 dwDesiredAccess
DWORD R8D:4 dwShareMode
LPSECURITY_ATT R9:8 lpSecurityAttributes
DWORD Stack[0x28]:4 dwCreationDisposition
DWORD Stack[0x30]:4 dwFlagsAndAttributes
HANDLE Stack[0x38]:8 hTemplateFile
To set a breakpoint at the kernel32!CreateFileW function, we can use the bp command as follows:
0:000> bp Kernel32!CreateFileW
To verify that our breakpoint has been set correctly, we can then use the bl command to list all the breakpoints.
0:000> bl
0 e Disable Clear 00007fff`0f0a2b10 0001 (0001) 0:**** KERNEL32!CreateFileW
Great. Our breakpoint is set. Let’s resume the execution of WinRar. If everything goes well, we should hit the breakpoint.
0:000> g
ModLoad: 00007fff`11050000 00007fff`1107d000 C:\Windows\System32\IMM32.DLL
ModLoad: 00007ffe`fc1d0000 00007ffe`fc1e9000 C:\Windows\SYSTEM32\CLDAPI.dll
ModLoad: 00007fff`00c90000 00007fff`00c9a000 C:\Windows\SYSTEM32\FLTLIB.DLL
ModLoad: 00007ffe`fdaf0000 00007ffe`fdb72000 C:\Windows\SYSTEM32\AEPIC.dll
ModLoad: 00007fff`0c570000 00007fff`0c5a1000 C:\Windows\SYSTEM32\ntmarta.dll
ModLoad: 00007fff`0cf00000 00007fff`0cf25000 C:\Windows\SYSTEM32\bcrypt.dll
ModLoad: 00007fff`0cdf0000 00007fff`0ce07000 C:\Windows\SYSTEM32\cryptsp.dll
ModLoad: 00007fff`0e680000 00007fff`0e71e000 C:\Windows\System32\clbcatq.dll
ModLoad: 00007fff`09a30000 00007fff`09be1000 C:\Windows\system32\propsys.dll
Breakpoint 0 hit
*** ERROR: Module load completed but symbols could not be loaded for WinRAR.exe
KERNEL32!CreateFileW:
00007fff`0f0a2b10 ff25e21e0500 jmp qword ptr [KERNEL32!_imp_CreateFileW (00007fff`0f0f49f8)] ds:00007fff`0f0f49f8={KERNELBASE!CreateFileW (00007fff`0d991940)}
Perfect. The breakpoint 0 has been hit. At this point, to better understand how we got to the KERNEL32!CreateFileW function, a reverse engineer would typically inspect the stack using the k command.
0:000> k
# Child-SP RetAddr Call Site
00 0000001f`e09a64c8 00007ff6`e57cf220 KERNEL32!CreateFileW
01 0000001f`e09a64d0 00007ff6`e57efbcb WinRAR+0x6f220
02 0000001f`e09a7570 00007ff6`e57ef8fb WinRAR+0x8fbcb
03 0000001f`e09ab930 00007ff6`e58496d5 WinRAR+0x8f8fb
04 0000001f`e09ac970 00007ff6`e58618e3 WinRAR+0xe96d5
05 0000001f`e09afaa0 00007fff`0f0937e4 WinRAR+0x1018e3
06 0000001f`e09afae0 00007fff`1111cb61 KERNEL32!BaseThreadInitThunk+0x14
07 0000001f`e09afb10 00000000`00000000 ntdll!RtlUserThreadStart+0x21
In this case, the KERNEL32!CreateFileW was called by the WinRAR+0x6f220 function. Cool. So what about the content of the registers. How can we take a look at the registers? That’s simple. We only need to type r in the command window and WinDbg will dump the content of all the main registers.
0:000> r
rax=0000000000000000 rbx=0000001fe09a75d0 rcx=0000001fe09a88e0
rdx=0000000080000000 rsi=0000000000000800 rdi=0000001fe09ab950
rip=00007fff0f0a2b10 rsp=0000001fe09a64c8 rbp=0000001fe09a88e0
r8=0000000000000003 r9=0000000000000000 r10=0000000000000000
r11=0000001fe09a88e0 r12=0000000080000000 r13=0000000000000000
r14=0000000000000003 r15=0000000008000000
iopl=0 nv up ei pl zr na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
KERNEL32!CreateFileW:
00007fff`0f0a2b10 ff25e21e0500 jmp qword ptr [KERNEL32!_imp_CreateFileW (00007fff`0f0f49f8)] ds:00007fff`0f0f49f8={KERNELBASE!CreateFileW (00007fff`0d991940)}
And, if we want to take a look at the code, we can simply use the u command to unassemble the code at the location specified.
0:000> u rip
KERNEL32!CreateFileW:
00007fff`0f0a2b10 ff25e21e0500 jmp qword ptr [KERNEL32!_imp_CreateFileW (00007fff`0f0f49f8)]
00007fff`0f0a2b16 cc int 3
00007fff`0f0a2b17 cc int 3
00007fff`0f0a2b18 cc int 3
00007fff`0f0a2b19 cc int 3
00007fff`0f0a2b1a cc int 3
00007fff`0f0a2b1b cc int 3
00007fff`0f0a2b1c cc int 3
So, let’s go back to our initial question and find out the first file opened by WinRar. We know that KERNEL32!CreateFileW opens a file and most likely the name of the file to open is passed as an argument.
However, there are a handful of ways to pass arguments to a function and it is very important to know which way (or calling convention) KERNEL32!CreateFileW is using. The easiest way to know the calling convention used by a function is to use your favorite disassembler (e.g. Ghidra, IDA Pro, etc.) and look at the prototype of the function. In this case, the KERNEL32!CreateFileW function uses the fastcall calling convention, which means that the pointer to the file name will be in RCX.

To dump the content of the RCX register, we have several options. The dd command or the du command. Since we expect RCX to point to a Unicode string, I will use the du command.
0:000> du rcx
0000001f`e09a88e0 "C:\Program Files\WinRAR\winrar.l"
0000001f`e09a8920 "ng"
And there you have it! C:\Program Files\WinRAR\winrar.lng. This language pack file called winrar.lng is the first file accessed by WinRar. Now, try to resume the execution and open a RAR archive. What happens? Is the breakpoint hit again? Well, if you find out, let me know in the comment section down below. ;-)