WinDbg用户模式调试基础教程

本文内容是基于用户模式下的调试,后面的文章中,会介绍内核模式下的调试。

Windows调试工具(Debugging Tools for Windows)Debugging Tools for Windows由调试器、工具以及软件包中调试器的相关文档组成。这个工具包可以作为Windows SDK或者WDK的一部分安装。

工具包中包括四个调试器:Cdb.exe、Ntsd.exe、Kd.exe和WinDbg.exe。

本文只详细介绍WinDbg,其它工具只做简单介绍。

Cdb.exe和Ntsd.exeCdb和Ntsd是用户模式的、基于控制台的调试器。它们能够被附加到进程上,就像别的用户模式调试器一样。这两者都有控制台用户界面一键入一条命令,得到一个回应,如此这般地重复。这两者之间唯一的区别在于,如果从控制台窗口启动,Cdb会直接用原来那个窗口,而Ntsd会打开一个新的窗口。其他方面它们都一样。

Kd.exeKd是具有控制台用户界面的内核调试器,能够被附加到本地内核或者另一台机器上。

WinDbg.exeWinDbg是唯一一个具有图形用户界面的调试器。根据从菜单所做的选择,或者启动时指定的命令行参数,它能够进行用户模式和内核模式调试。

本文是基于用户模式的调试。

WinDbg简介像平常在Visual Studio中调试时,大部分的功能是借助菜单或按钮实现,WinDbg建立在命令之上。

用户输入一个命令,调试器用文本描述命令执行的结果给出响应。

在GUI模式下,一些结果用专门的窗口进行显示,比如局部变量、栈、线程等。

正是因为这种调试模式,所以WinDbg学习的门槛就要相对高一些,得记这些命令。

WinDbg支持三类命令:1、内部命令

这些命令内建于调试器之中,操作被调试的目标

2、元命令

这些命令以(.)开头,操作调试进程自身,不直接操作被调试的目标。

3、扩展命令

这些命令以(!)开头,为调试器提供了很多功能。所有扩展命令均在外部DLL中实现。 默认情况下,调试器加载一组预定义的扩展DLL,但是通过使用.load命令可以从Debugger目录或其他目录中加载更多DLL。

说明:WinDbg是大小写不敏感的,所以在输入命令时,大小写都可以。

WinDbg基础调试(用户模式)有两种方法可以初始化用户模式的调试

1、先打开WinDbg,再通过在“ 文件 ”菜单上,选择“ 启动可执行文件”。程序运行后,会自动附加到WinDbg

2、先运行程序,再打开WinDbg,然后通过“文件”菜单上,选择“附加到进程”,通过选择进程列表里的进程进行附加。

这里我们以notepad(Windows记事本)为例进行演示

1、打开WinDbg2、启动notepad在“ 文件 ”菜单上,选择“ 启动可执行文件”。 在“启动可执行文件”对话框中,

输入C:\Windows\System32\notepad.exe。

(notepad.exe 文件通常位于 C:\Windows\System32.)

对于 “文件名”,请输入 notepad.exe。 选择“打开” 。进程创建后,会自动中断,WinDbg会自动添加一个断点。

图片说明:命令窗口是我们主要关注的窗口,它应该始终保持打开状态。这个窗口会显示各种命令的响应结果。

如果不小心关闭了,可以通过工具栏按钮再次打开

图片3、输入 .symfix命令,自动将符号路径设置为指向 Microsoft 符号存储关于调试符号,可以参考https://www.cnblogs.com/zhaotianff/p/16931797.html

4、输入 x notepad!* 命令查看notepad模块的符号代码语言:javascript复制1 0:000> x notepad!* 代码语言:javascript复制2 00007ff6`40267460 notepad!:: (void) 代码语言:javascript复制3 00007ff6`4026eb70 notepad!wistd::__function::__func<,long __cdecl(unsigned short *,unsigned __int64,unsigned __int64 *)>::operator() (void) 代码语言:javascript复制4 00007ff6`402662d0 notepad!:: (void) 代码语言:javascript复制5 00007ff6`40267bac notepad!ShowOpenSaveDialog (void) 代码语言:javascript复制6 00007ff6`4027d75c notepad!StringLengthWorkerW (void) 代码语言:javascript复制7 00007ff6`4027f5a0 notepad!`WaitForCompletion,Windows::Foundation::IAsyncOperation >'::`2'::FTMEventDelegate::Invoke (void) 代码语言:javascript复制8 00007ff6`40261380 notepad!wil::details::`dynamic initializer for 'g_header_init_InitializeStagingSRUMFeatureReporting'' (void) 代码语言:javascript复制9 00007ff6`40267570 notepad!wistd::__function::__func<,long __cdecl(unsigned short *,unsigned __int64,unsigned __int64 *)>::destroy (void)这里的!后面的*通配符,代表显示全部符号。

如果我们想查找 notepad的Main函数,可以输入 x notepad!*main*

代码语言:javascript复制1 0:012> x notepad!*main*代码语言:javascript复制2 00007ff6`47a10118 notepad!__mainCRTStartup (void)代码语言:javascript复制3 00007ff6`47a11213 notepad!__mainCRTStartup$filt$0 (void)代码语言:javascript复制4 00007ff6`47a13000 notepad!_imp___getmainargs = 代码语言:javascript复制5 00007ff6`479fa140 notepad!WinMain (WinMain)代码语言:javascript复制6 00007ff6`47a10100 notepad!WinMainCRTStartup (WinMainCRTStartup)5、使用 ~ 命令,显示被调试进程内部的所有线程信息代码语言:javascript复制1 0:000> ~代码语言:javascript复制2 . 0 Id: 5ba4.950 Suspend: 1 Teb: 00000034`cb6a0000 Unfrozen代码语言:javascript复制3 1 Id: 5ba4.234c Suspend: 1 Teb: 00000034`cb6a2000 Unfrozen代码语言:javascript复制4 2 Id: 5ba4.6834 Suspend: 1 Teb: 00000034`cb6a4000 Unfrozen代码语言:javascript复制5 3 Id: 5ba4.52dc Suspend: 1 Teb: 00000034`cb6a6000 Unfrozen线程基本信息如下所示(这里以列表里的第一项进行说明)

0

Id: 5ba4.950

Suspend: 1

Teb: 00000034`cb6a0000

Unfrozen

调试器线程索引

进程ID.线程ID

挂起计数(通常是1)

线程环境块(TEB)

是否冻结(从调试器的角度看,通常未冻结)

说明:WinDbg默认以十六进制显示,可以使用 ? 命令将数值转换为十进制,

如前面进程Id为5ba4,输入 ? 5ba4,可以查看十进制 进程Id号

代码语言:javascript复制1 0:002> ? 5ba4代码语言:javascript复制2 Evaluate expression: 23460 = 00000000`00005ba4可以用0n前缀代表十进制数字,使用这个命令可以反查十六进制数字

代码语言:javascript复制1 0:002> ? 0n23460代码语言:javascript复制2 Evaluate expression: 23460 = 00000000`00005ba46、输入 lm 命令,查看已经加载的模块代码语言:javascript复制1 0:000> lm 代码语言:javascript复制2 start end module name 代码语言:javascript复制3 00007ff6`40260000 00007ff6`40292000 notepad (pdb symbols) C:\ProgramData\dbg\sym\notepad.pdb\48F76637AE64DAE8764C8F9F4B27AEA51\notepad.pdb 代码语言:javascript复制4 00007ffb`e0710000 00007ffb`e0995000 COMCTL32 (deferred) 代码语言:javascript复制5 00007ffb`f29d0000 00007ffb`f29f1000 win32u (deferred) 代码语言:javascript复制6 00007ffb`f2b40000 00007ffb`f2bc0000 bcryptPrimitives (deferred) 代码语言:javascript复制7 00007ffb`f2bc0000 00007ffb`f2cba000 ucrtbase (deferred) 代码语言:javascript复制8 00007ffb`f3440000 00007ffb`f34de000 msvcp_win (deferred) 代码语言:javascript复制9 00007ffb`f3550000 00007ffb`f37f3000 KERNELBASE (deferred) 代码语言:javascript复制10 00007ffb`f3950000 00007ffb`f3ae4000 gdi32full (deferred) 代码语言:javascript复制11 00007ffb`f3af0000 00007ffb`f3b99000 shcore (deferred) 代码语言:javascript复制12 00007ffb`f3ba0000 00007ffb`f3d33000 USER32 (deferred) 代码语言:javascript复制13 00007ffb`f3d40000 00007ffb`f3de3000 advapi32 (deferred) 代码语言:javascript复制14 00007ffb`f3f90000 00007ffb`f4042000 KERNEL32 (deferred) 代码语言:javascript复制15 00007ffb`f49a0000 00007ffb`f4a3e000 msvcrt (deferred) 代码语言:javascript复制16 00007ffb`f4a50000 00007ffb`f4ae7000 sechost (deferred) 代码语言:javascript复制17 00007ffb`f4d70000 00007ffb`f4e90000 RPCRT4 (deferred) 代码语言:javascript复制18 00007ffb`f4f00000 00007ffb`f4f26000 GDI32 (deferred) 代码语言:javascript复制19 00007ffb`f55f0000 00007ffb`f5926000 combase (deferred) 代码语言:javascript复制20 00007ffb`f5a40000 00007ffb`f5c30000 ntdll (pdb symbols) C:\ProgramData\dbg\sym\ntdll.pdb\CFD10E5F223FEE5F26227CB82510FEDC1\ntdll.pdb模块列表显示了当前被调试进程(notepad.exe)的所有模块(DLL和EXE)。你能够从中看到已加载的模块的起始和终止的虚拟地址。

在模块名称后面可以看到这个模块的符号的状态(在括号里)。可能的值有这些:

deferred(推迟)在当前调试会话中还没用到,因此现在还没有被加载。这些符号会在载入时被载入。

pdb symbols(pdb符号)正确的公开符号已经被载入(来自微软公开的符号服务器),后面会显示PDF文件路径

export symbol(输出符号)这个DLL只有输出符号可用,这一般意味着该模块没有符号,或者没有找到相应的符号

no symbol(没有符号)试图去找本模块的符号,但是什么都没发现,连输出符号都没有(这种模块没有输出符号,比如可执行文件和驱动程序文件)。

可以通过.reload /f modulename.dll强制加载模块的符号

如这里我执行 .reload /f gdi32.dll

代码语言:javascript复制1 0:000> .reload /f gdi32.dll 代码语言:javascript复制2 0:000> lm 代码语言:javascript复制3 start end module name 代码语言:javascript复制4 00007ff6`40260000 00007ff6`40292000 notepad (pdb symbols) c:\symbols\notepad.pdb\48F76637AE64DAE8764C8F9F4B27AEA51\notepad.pdb 代码语言:javascript复制5 00007ffb`e0710000 00007ffb`e0995000 COMCTL32 (deferred) 代码语言:javascript复制6 00007ffb`f29d0000 00007ffb`f29f1000 win32u (deferred) 代码语言:javascript复制7 00007ffb`f2b40000 00007ffb`f2bc0000 bcryptPrimitives (deferred) 代码语言:javascript复制8 00007ffb`f2bc0000 00007ffb`f2cba000 ucrtbase (deferred) 代码语言:javascript复制9 00007ffb`f3440000 00007ffb`f34de000 msvcp_win (deferred) 代码语言:javascript复制10 00007ffb`f3550000 00007ffb`f37f3000 KERNELBASE (deferred) 代码语言:javascript复制11 00007ffb`f3950000 00007ffb`f3ae4000 gdi32full (deferred) 代码语言:javascript复制12 00007ffb`f3af0000 00007ffb`f3b99000 shcore (deferred) 代码语言:javascript复制13 00007ffb`f3ba0000 00007ffb`f3d33000 USER32 (deferred) 代码语言:javascript复制14 00007ffb`f3d40000 00007ffb`f3de3000 advapi32 (deferred) 代码语言:javascript复制15 00007ffb`f3f90000 00007ffb`f4042000 KERNEL32 (deferred) 代码语言:javascript复制16 00007ffb`f49a0000 00007ffb`f4a3e000 msvcrt (deferred) 代码语言:javascript复制17 00007ffb`f4a50000 00007ffb`f4ae7000 sechost (deferred) 代码语言:javascript复制18 00007ffb`f4d70000 00007ffb`f4e90000 RPCRT4 (deferred) 代码语言:javascript复制19 00007ffb`f4f00000 00007ffb`f4f26000 GDI32 (pdb symbols) c:\symbols\gdi32.pdb\209AD405837D061EF9D34CBDC009D7711\gdi32.pdb代码语言:javascript复制20 00007ffb`f55f0000 00007ffb`f5926000 combase (deferred) 21 00007ffb`f5a40000 00007ffb`f5c30000 ntdll (pdb symbols) c:\symbols\ntdll.pdb\CFD10E5F223FEE5F26227CB82510FEDC1\ntdll.pdb7 当前线程指示在前面输入 ~ 查看线程时,有一个线程数据前面带有一个点,这个线程就是当前线程。

代码语言:javascript复制1 0:005> ~代码语言:javascript复制2 0 Id: 8824.5a80 Suspend: 1 Teb: 00000096`de0af000 Unfrozen代码语言:javascript复制3 1 Id: 8824.48bc Suspend: 1 Teb: 00000096`de0bd000 Unfrozen代码语言:javascript复制4 2 Id: 8824.521c Suspend: 1 Teb: 00000096`de0bf000 Unfrozen代码语言:javascript复制5 3 Id: 8824.460 Suspend: 1 Teb: 00000096`de0c3000 Unfrozen代码语言:javascript复制6 4 Id: 8824.5a30 Suspend: 1 Teb: 00000096`de107000 Unfrozen代码语言:javascript复制7 . 5 Id: 8824.9a8 Suspend: 1 Teb: 00000096`de109000 Unfrozen如果没有指定哪个线程的话,任何线程命令都会作用到这个线程上。

8 输入命令 k ,显示当前线程的调用堆栈代码语言:javascript复制1 0:005> k代码语言:javascript复制2 # Child-SP RetAddr Call Site代码语言:javascript复制3 00 00000096`ddfafc78 00007ffb`f5b0d4db ntdll!DbgBreakPoint代码语言:javascript复制4 01 00000096`ddfafc80 00007ffb`f3fa7bd4 ntdll!DbgUiRemoteBreakin+0x4b代码语言:javascript复制5 02 00000096`ddfafcb0 00007ffb`f5aace71 KERNEL32!BaseThreadInitThunk+0x14代码语言:javascript复制6 03 00000096`ddfafce0 00000000`00000000 ntdll!RtlUserThreadStart+0x21可以看到这个线程的调用列表(当然,只有用户模式的)。上面输出中的栈顶部是函数DbgBreakPoint,它位于模块ntdll.dll中。

通用的带有符号的地址格式是: modulename !functionname + offset。如果正好位于函数的开头,那么offset是可选的,也可能为零。

另外要注意模块名称里不带扩展名。在上面的输出中, DbgBreakPoint被DbgUiRemoteBreakin调用,而后者又被BaseThreadInitThunk调用,依此类推。

另外说明一下:该线程是被WinDbg注入的,而非进程的实际线程,以便强行进入目标进程。

9 输入命令 ~ns 切换线程 , n是线程的索引值。如切换到线程0就是 ~0s

再执行 k,显示调用堆栈,输出如下:

代码语言:javascript复制1 0:005> ~0s 代码语言:javascript复制2 win32u!NtUserGetMessage+0x14: 代码语言:javascript复制3 00007ffb`f29d1164 c3 ret 代码语言:javascript复制4 0:000> k 代码语言:javascript复制5 # Child-SP RetAddr Call Site 代码语言:javascript复制6 00 00000096`ddeafd28 00007ffb`f3bc477d win32u!NtUserGetMessage+0x14 代码语言:javascript复制7 01 00000096`ddeafd30 00007ff6`4026a3d3 USER32!GetMessageW+0x2d 代码语言:javascript复制8 02 00000096`ddeafd90 00007ff6`402802b7 Notepad!WinMain+0x293 代码语言:javascript复制9 03 00000096`ddeafe60 00007ffb`f3fa7bd4 Notepad!__mainCRTStartup+0x19f代码语言:javascript复制10 04 00000096`ddeaff20 00007ffb`f5aace71 KERNEL32!BaseThreadInitThunk+0x14代码语言:javascript复制11 05 00000096`ddeaff50 00000000`00000000 ntdll!RtlUserThreadStart+0x21这是Notepad的主线程(第一个线程)。栈的顶部显示了线程正在等待用户界面消息。

10 输入 ~nk 在不切换线程的情况下显示指定线程的调用堆栈如输入 ~0k,可以显示线程0的调用堆栈

代码语言:javascript复制1 0:002> ~0k代码语言:javascript复制2 # Child-SP RetAddr Call Site代码语言:javascript复制3 00 00000096`ddeafd28 00007ffb`f3bc477d win32u!NtUserGetMessage+0x14代码语言:javascript复制4 01 00000096`ddeafd30 00007ff6`4026a3d3 USER32!GetMessageW+0x2d代码语言:javascript复制5 02 00000096`ddeafd90 00007ff6`402802b7 Notepad!WinMain+0x293代码语言:javascript复制6 03 00000096`ddeafe60 00007ffb`f3fa7bd4 Notepad!__mainCRTStartup+0x19f代码语言:javascript复制7 04 00000096`ddeaff20 00007ffb`f5aace71 KERNEL32!BaseThreadInitThunk+0x14代码语言:javascript复制8 05 00000096`ddeaff50 00000000`00000000 ntdll!RtlUserThreadStart+0x21此时我们再调用 ~ 查看线程列表,发现 那个点 已经移到线程2。在线程5上还显示了一个#。

代码语言:javascript复制1 0:002> ~代码语言:javascript复制2 0 Id: 8824.5a80 Suspend: 1 Teb: 00000096`de0af000 Unfrozen代码语言:javascript复制3 1 Id: 8824.48bc Suspend: 1 Teb: 00000096`de0bd000 Unfrozen代码语言:javascript复制4 . 2 Id: 8824.521c Suspend: 1 Teb: 00000096`de0bf000 Unfrozen代码语言:javascript复制5 3 Id: 8824.460 Suspend: 1 Teb: 00000096`de0c3000 Unfrozen代码语言:javascript复制6 4 Id: 8824.5a30 Suspend: 1 Teb: 00000096`de107000 Unfrozen代码语言:javascript复制7 # 5 Id: 8824.9a8 Suspend: 1 Teb: 00000096`de109000 Unfrozen带有#标识的线程是引起最后一个断点的线程(在当前演示中是因为我们初始附加调试器的操作)

11 输入 !teb 命令可以查看当前线程的TEB代码语言:javascript复制1 0:002> !teb 代码语言:javascript复制2 TEB at 00000096de0bf000 代码语言:javascript复制3 ExceptionList: 0000000000000000 代码语言:javascript复制4 StackBase: 00000096de500000 代码语言:javascript复制5 StackLimit: 00000096de4ef000 代码语言:javascript复制6 SubSystemTib: 0000000000000000 代码语言:javascript复制7 FiberData: 0000000000001e00 代码语言:javascript复制8 ArbitraryUserPointer: 0000000000000000 代码语言:javascript复制9 Self: 00000096de0bf000代码语言:javascript复制10 EnvironmentPointer: 0000000000000000代码语言:javascript复制11 ClientId: 0000000000008824 . 000000000000521c代码语言:javascript复制12 RpcHandle: 0000000000000000代码语言:javascript复制13 Tls Storage: 0000026f1b184d80代码语言:javascript复制14 PEB Address: 00000096de0ae000代码语言:javascript复制15 LastErrorValue: 0代码语言:javascript复制16 LastStatusValue: c000000d代码语言:javascript复制17 Count Owned Locks: 0代码语言:javascript复制18 HardErrorMode: 0!teb 后面带其它线程TEB的地址,可以输出其它线程的TEB

代码语言:javascript复制1 0:002> !teb 00000096`de0c3000 代码语言:javascript复制2 TEB at 00000096de0c3000 代码语言:javascript复制3 ExceptionList: 0000000000000000 代码语言:javascript复制4 StackBase: 00000096de600000 代码语言:javascript复制5 StackLimit: 00000096de5ef000 代码语言:javascript复制6 SubSystemTib: 0000000000000000 代码语言:javascript复制7 FiberData: 0000000000001e00 代码语言:javascript复制8 ArbitraryUserPointer: 0000000000000000 代码语言:javascript复制9 Self: 00000096de0c3000代码语言:javascript复制10 EnvironmentPointer: 0000000000000000代码语言:javascript复制11 ClientId: 0000000000008824 . 0000000000000460代码语言:javascript复制12 RpcHandle: 0000000000000000代码语言:javascript复制13 Tls Storage: 0000026f1b184ba0代码语言:javascript复制14 PEB Address: 00000096de0ae000代码语言:javascript复制15 LastErrorValue: 0代码语言:javascript复制16 LastStatusValue: c000000d代码语言:javascript复制17 Count Owned Locks: 0代码语言:javascript复制18 HardErrorMode: 0!teb命令显示的数据含义StackBase和StackLimit:当前线程的用户模式栈基址和限制。

ClientId:进程和线程ID。

LastErrorValue :上一个Win32错误代码( GetLastError )。

Tls Storage:此线程的线程局部存储(TLS )数组。这里不做详细介绍,可以参考以下链接:https://learn.microsoft.com/zh-cn/windows/win32/procthread/using-thread-local-storage

PEB Address:进程环境块(PEB)的地址,可以通过!peb来查看PEB的内容

_teb命令显示的是其背后真正的结构中的部分内容,这里的结构是_TEB,它在ntdll中定义。

可以使用dt(display type) _teb命令查看真正的结构:

图片如果知道结构在哪里定义的,可以在结构名称前带上模块名称,如这里带上ntdll

代码语言:javascript复制1 dt ntdll!_teb在前面的命令中加上一个地址,就可以得到这个结构数据成员的实际值

代码语言:javascript复制0:002> dt _teb 00000096de0bf000

ntdll!_TEB

+0x000 NtTib : _NT_TIB

+0x038 EnvironmentPointer : (null)

+0x040 ClientId : _CLIENT_ID

+0x050 ActiveRpcHandle : (null)

+0x058 ThreadLocalStoragePointer : 0x0000026f`1b184d80 Void

+0x060 ProcessEnvironmentBlock : 0x00000096`de0ae000 _PEB

+0x068 LastErrorValue : 0

+0x06c CountOfOwnedCriticalSections : 0

+0x070 CsrClientThread : (null)

+0x078 Win32ThreadInfo : 0x00000000`0000521c Void

+0x080 User32Reserved : [26] 0

+0x0e8 UserReserved : [5] 0

+0x100 WOW32Reserved : (null)

+0x108 CurrentLocale : 0x804

+0x10c FpSoftwareStatusRegister : 0

+0x110 ReservedForDebuggerInstrumentation : [16] (null)

+0x190 SystemReserved1 : [30] (null)

+0x280 PlaceholderCompatibilityMode : 0 ''

+0x281 PlaceholderHydrationAlwaysExplicit : 0 ''

+0x282 PlaceholderReserved : [10] ""

+0x28c ProxiedProcessId : 0

+0x290 _ActivationStack : _ACTIVATION_CONTEXT_STACK

+0x2b8 WorkingOnBehalfTicket : [8] ""

+0x2c0 ExceptionCode : 0n0

+0x2c4 Padding0 : [4] ""

+0x2c8 ActivationContextStackPointer : 0x00000096`de0bf290 _ACTIVATION_CONTEXT_STACK

+0x2d0 InstrumentationCallbackSp : 0每个成员都会显示出相对于结构起始处的偏移量、成员的名称和值。简单的值直接显示,而结构体的值(例如上面的ClientID )通常会显示成一个超链接。

单击这个超链接会显示出该结构的详情。调试器会使用一个新的dx命令来查看数据

代码语言:javascript复制1 0:002> dx -r1 (*((ntdll!_CLIENT_ID *)0x96de0bf040))代码语言:javascript复制2 (*((ntdll!_CLIENT_ID *)0x96de0bf040)) [Type: _CLIENT_ID]代码语言:javascript复制3 [+0x000] UniqueProcess : 0x8824 [Type: void *]代码语言:javascript复制4 [+0x008] UniqueThread : 0x521c [Type: void *]说明:TEB全程Thread Environment Block(线程环境块),它包含了线程的上下文信息(The Thread Environment Block holds context information for a thread.)

TEB在线程数据结构中的位置如下:

图片 关于TEB,如果想了解更多信息,可以查看《Windows Internals Seventh Edition Part 1》的第4章Thread

12 使用 bp / bu 命令添加断点如我们要在CreateFile函数处设置一个断点,可以输入 bp kernel32!CreateFileW 命令

代码语言:javascript复制1 0:012> bp kernel32!CreateFileW代码语言:javascript复制2 0:012> bl代码语言:javascript复制3 0 e Disable Clear 00007ffb`f3fb2090 0001 (0001) 0:**** KERNEL32!CreateFileW如我们要在 notepad.exe的Main函数放置断点,可以输入 bp notepad!WinMain 命令

代码语言:javascript复制1 0:012> bp notepad!WinMain代码语言:javascript复制2 0:012> bl代码语言:javascript复制3 0 e Disable Clear 00007ff6`479fa140 0001 (0001) 0:**** notepad!WinMain13、使用 bl 命令查看断点列表代码语言:javascript复制1 0:012> bl代码语言:javascript复制2 0 e Disable Clear 00007ffb`f3fb2090 0001 (0001) 0:**** KERNEL32!CreateFileW可以看到断点的索引值(0)是被允许了还是被禁止了( e=被允许,d=被禁止)

并且得到用来禁止( bd命令)/ 启用(be命令)和删除( bc命令)该断点的超链接。单击链接可以禁止和删除断点

图片图片bd/be 后面带*,可以禁用或启用全部断点

bd/be 后面带数字,可以禁用或启用对应的断点

14、输入 g 命令、按下工具栏上的Go按钮或者按F5键,会继续执行进程。调试器会显示正在忙碌,这也就意味着直到下次中断才能输入命令。

图片此时我们回到记事本,用文件菜单打开一个文件,打开文件时会调用CreateFileW函数,调试器会触发断点,然后中断。

此时我们输入 k 查看调用堆栈(如果调试器需要从微软的符号服务器下载符号的话,这里的加载时间会长一点,请耐心等待)

代码语言:javascript复制0:004> ~

0 Id: 9b34.26b0 Suspend: 1 Teb: 00000005`79b86000 Unfrozen

1 Id: 9b34.59a8 Suspend: 1 Teb: 00000005`79b88000 Unfrozen

2 Id: 9b34.3be8 Suspend: 1 Teb: 00000005`79b8a000 Unfrozen

3 Id: 9b34.2364 Suspend: 1 Teb: 00000005`79b8c000 Unfrozen

. 4 Id: 9b34.40ac Suspend: 1 Teb: 00000005`79b8e000 Unfrozen

5 Id: 9b34.57f4 Suspend: 1 Teb: 00000005`79ba4000 Unfrozen

6 Id: 9b34.5b6c Suspend: 1 Teb: 00000005`79b92000 Unfrozen

7 Id: 9b34.2f90 Suspend: 1 Teb: 00000005`79b94000 Unfrozen

8 Id: 9b34.4844 Suspend: 1 Teb: 00000005`79b96000 Unfrozen

9 Id: 9b34.8ab8 Suspend: 1 Teb: 00000005`79baa000 Unfrozen

10 Id: 9b34.5e2c Suspend: 1 Teb: 00000005`79b9a000 Unfrozen

11 Id: 9b34.9288 Suspend: 1 Teb: 00000005`79ba8000 Unfrozen

12 Id: 9b34.7bfc Suspend: 1 Teb: 00000005`79ba0000 Unfrozen

13 Id: 9b34.7188 Suspend: 1 Teb: 00000005`79bac000 Unfrozen

14 Id: 9b34.1a2c Suspend: 1 Teb: 00000005`79bae000 Unfrozen

15 Id: 9b34.44e4 Suspend: 1 Teb: 00000005`79bb0000 Unfrozen

16 Id: 9b34.5418 Suspend: 1 Teb: 00000005`79bb2000 Unfrozen

0:004> k

# Child-SP RetAddr Call Site

00 00000005`79d7e9f8 00007ffb`c01785d6 KERNEL32!CreateFileW

01 00000005`79d7ea00 00007ffb`c01786d2 TortoiseSVN+0x385d6

02 00000005`79d7ea60 00007ffb`c0178913 TortoiseSVN+0x386d2

03 00000005`79d7eaa0 00007ffb`c01778cc TortoiseSVN+0x38913

04 00000005`79d7ed50 00000000`60bb1706 TortoiseSVN+0x378cc

05 00000005`79d7f030 00007ffb`f4334625 TortoiseOverlays+0x1706

06 00000005`79d7f060 00007ffb`f43344f6 SHELL32!CFSIconOverlayManager::_GetFileOverlayInfo+0x111

07 00000005`79d7f140 00007ffb`f2df1331 SHELL32!CFSIconOverlayManager::GetFileOverlayInfo+0x46

08 00000005`79d7f180 00007ffb`f2e5936b windows_storage!CFSFolder::_GetOverlayInfo+0x179

09 00000005`79d7f240 00007ffb`f2e59267 windows_storage!CRegFolder::_GetOverlayInfo+0xbf

0a 00000005`79d7f310 00007ffb`f2d53de6 windows_storage!CRegFolder::GetOverlayIndex+0x47

0b 00000005`79d7f340 00007ffb`f2e5936b windows_storage!CAutoDestItemsFolder::GetOverlayIndex+0xb6

0c 00000005`79d7f3c0 00007ffb`f2e59267 windows_storage!CRegFolder::_GetOverlayInfo+0xbf

0d 00000005`79d7f490 00007ffb`caa1b191 windows_storage!CRegFolder::GetOverlayIndex+0x47

0e 00000005`79d7f4c0 00007ffb`caa42a95 explorerframe!CNscOverlayTask::_Extract+0x51

0f 00000005`79d7f510 00007ffb`caa16362 explorerframe!CNscOverlayTask::InternalResumeRT+0x45

10 00000005`79d7f540 00007ffb`f2e39be4 explorerframe!CRunnableTask::Run+0xb2

11 00000005`79d7f580 00007ffb`f2e39825 windows_storage!CShellTask::TT_Run+0x3c

12 00000005`79d7f5b0 00007ffb`f2e39705 windows_storage!CShellTaskThread::ThreadProc+0xdd

13 00000005`79d7f660 00007ffb`f3b226f6 windows_storage!CShellTaskThread::s_ThreadProc+0x35

14 00000005`79d7f690 00007ffb`f5a6f665 shcore!ExecuteWorkItemThreadProc+0x16

15 00000005`79d7f6c0 00007ffb`f5a745c4 ntdll!RtlpTpWorkCallback+0x165

16 00000005`79d7f7a0 00007ffb`f3fa7bd4 ntdll!TppWorkerThread+0x8d4

17 00000005`79d7fb60 00007ffb`f5aace71 KERNEL32!BaseThreadInitThunk+0x14

18 00000005`79d7fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x2115、 查看内存数据当调试器在CreateFIleW函数中断时,我们能做些什么?

可能想知道现在正在打开什么文件,我们能够根据CreateFilew函数的调用惯例来得到这个信息。由于这是一个64位进程(并且处理器是Intel ),调用惯例中提到了第一个整数或者指针参数通过RCX、RDX、R8和R9寄存器进行传递。因为文件名是CreateFilew的第一个参数,所以相应的寄存器是RCX。

用 r 命令 显示 RCX 寄存器的值

代码语言:javascript复制1 0:004> r rcx代码语言:javascript复制2 rcx=00000163401b4bc8用 db 命令以字节方式显示内存,右边是相应的ASCII字符。

db 后面是内存的地址,也就是上面rcx=后面的值。

代码语言:javascript复制1 0:004> db 00000163401b4bc8代码语言:javascript复制2 00000163`401b4bc8 5c 00 5c 00 2e 00 5c 00-70 00 69 00 70 00 65 00 \.\...\.p.i.p.e.代码语言:javascript复制3 00000163`401b4bd8 5c 00 54 00 53 00 56 00-4e 00 43 00 61 00 63 00 \.T.S.V.N.C.a.c.代码语言:javascript复制4 00000163`401b4be8 68 00 65 00 2d 00 30 00-30 00 30 00 30 00 30 00 h.e.-.0.0.0.0.0.代码语言:javascript复制5 00000163`401b4bf8 3000300030003400-31003100300035000.0.0.4.1.1.0.5.代码语言:javascript复制6 00000163`401b4c08 61 00 35 00 35 00 00 00-00 00 00 00 00 00 00 00 a.5.5...........代码语言:javascript复制7 00000163`401b4c18 01 ba 67 bb 45 02 00 80-43 00 3a 00 5c 00 50 00 ..g.E...C.:.\.P.代码语言:javascript复制8 00000163`401b4c28 72 00 6f 00 67 00 72 00-61 00 6d 00 20 00 46 00 r.o.g.r.a.m. .F.代码语言:javascript复制9 00000163`401b4c38 69 00 6c 00 65 00 73 00-5c 00 54 00 6f 00 72 00 i.l.e.s.\.T.o.r.由于这个字符串是Unicode的,所以使用db命令看起来不是非常方便。

使用 du 命令可以更加方便地查看Unicode字符串

代码语言:javascript复制1 0:004> du 00000163401b4bc8代码语言:javascript复制2 00000163`401b4bc8 "\\.\pipe\TSVNCache-0000000041105"3 00000163`401b4c08 "a55"可以通过给寄存器名字前加@前缀来直接使用寄存器的值

代码语言:javascript复制1 0:004> du @rcx代码语言:javascript复制2 00000163`401b4bc8 "\\.\pipe\TSVNCache-0000000041105"3 00000163`401b4c08 "a55"16、 使用 u 命令查看反汇编指令我们增加一个新的断点,断点在NtCreateFile函数,这个API会被CreateFileW调用

代码语言:javascript复制1 0:014> bp ntdll!ntcreatefile代码语言:javascript复制2 0:014> bl代码语言:javascript复制3 0 e Disable Clear 00007ffb`f3fb2090 0001 (0001) 0:**** KERNEL32!CreateFileW代码语言:javascript复制4 1 e Disable Clear 00007ffb`f5adcaf0 0001 (0001) 0:**** ntdll!NtCreateFile用g 命令继续执行,调试器应该会中断

代码语言:javascript复制1 0:014> g代码语言:javascript复制2 Breakpoint 1 hit代码语言:javascript复制3 ntdll!NtCreateFile:代码语言:javascript复制4 00007ffb`f5adcaf0 4c8bd1 mov r10,rcx用 u 命令列出接下来要执行的8条指令

代码语言:javascript复制1 0:014> u 代码语言:javascript复制2 ntdll!NtCreateFile: 代码语言:javascript复制3 00007ffb`f5adcaf0 4c8bd1 mov r10,rcx 代码语言:javascript复制4 00007ffb`f5adcaf3 b855000000 mov eax,55h 代码语言:javascript复制5 00007ffb`f5adcaf8 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1 代码语言:javascript复制6 00007ffb`f5adcb00 7503 jne ntdll!NtCreateFile+0x15 (00007ffb`f5adcb05) 代码语言:javascript复制7 00007ffb`f5adcb02 0f05 syscall 代码语言:javascript复制8 00007ffb`f5adcb04 c3 ret 代码语言:javascript复制9 00007ffb`f5adcb05 cd2e int 2Eh代码语言:javascript复制10 00007ffb`f5adcb07 c3 ret值0x55被复制到了EAX寄存器。这是NtCreateFile的系统服务号。列表中显示的syscall指令用来转换到内核模式,然后执行NtCreateFile系统服务。

说明:CreateFileW函数在kernel32.dll中实现,这里kernel32.dll是Windows子系统的一个DLL。CreateFileW函数在用户模式运行,因此无法直接打开文件。在进行了一些错误检查之后,它调用了NtCreateFile。这是一个在NTDLL.dll中实现的函数,而NTDLL.dll是—个基础的DLL,它实现了“原生API(Native API)“,并且它实际上是位于用户模式的底层代码。NtCreateFile是一个执行到内核模式的转换API。在进行实际的转换之前,它先把一个叫作系统服务号的数字(NtCreateFile是0x55)放到CPU的寄存器里(Intel/AMD体系结构上是EAX)。然后它会执行一个特殊的CPU指令(在x64系统里是syscall,在x86系统里是sysenter)来实际转换到内核模式,并跳转到一个预定义的被称为系统服务分发器( system service dispatcher )的例程。

系统服务分发器继而使用EAX寄存器中的值作为系统服务分发表( System Service Dispatch Table,SSDT)的入口索引,代码跳转至相应的系统服务中。对上述的记事本例子来说,SSDT中相应的入口会指向IO管理器(I/O Manager )的NtCreateFile函数。请注意,这个函数与NTDLL.dll里的函数有相同的名称,而且还有一样的参数。当系统服务执行完毕后,线程会返回到用户模式,执行紧接着sysenter/syscall的指令。这些事件的顺序如下所示。

图片17、使用 p 命令能够以跳过函数的方式(逐过程)单步执行下一条指令(按F10键也可以)说明: t 命令以进入函数的方式(逐语句)

代码语言:javascript复制1 0:014> p 代码语言:javascript复制2 ntdll!NtCreateFile+0x3: 代码语言:javascript复制3 00007ffb`f5adcaf3 b855000000 mov eax,55h 代码语言:javascript复制4 0:014> p 代码语言:javascript复制5 ntdll!NtCreateFile+0x8: 代码语言:javascript复制6 00007ffb`f5adcaf8 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1 ds:00000000`7ffe0308=00 代码语言:javascript复制7 0:014> p 代码语言:javascript复制8 ntdll!NtCreateFile+0x10: 代码语言:javascript复制9 00007ffb`f5adcb00 7503 jne ntdll!NtCreateFile+0x15 (00007ffb`f5adcb05) [br=0]代码语言:javascript复制10 0:014> p代码语言:javascript复制11 ntdll!NtCreateFile+0x12:代码语言:javascript复制12 00007ffb`f5adcb02 0f05 syscall代码语言:javascript复制13 0:014> p代码语言:javascript复制14 ntdll!NtCreateFile+0x14:代码语言:javascript复制15 00007ffb`f5adcb04 c3 ret由于当前是用户模式下的调试,所以单步跟踪进入syscall指令是不可能的。不管是逐过程还是逐语句,都会执行完该指令并返回结果。

在x64调用惯例下,函数的返回值保存在EAX或者RAX里。对系统调用来说,它是一个NTSTATUS值,因此EAX中包含返回状态:

代码语言:javascript复制1 0:014> r eax代码语言:javascript复制2 eax=018、单击工具栏上的 ”Break“按钮 或按Ctrl + Break键可以强制中断禁用所有断点,并让notepad继续运行

代码语言:javascript复制1 0:014> bd *代码语言:javascript复制2 0:014> g现在没有断点了,可以点击 Break

按钮,再次中断。

19、输入 qd 命令结束调试会话,并使任何用户模式目标应用程序保持运行状态。 这个命令实际上是 .detach命令(结束调试会话,但使任何用户模式目标应用程序保持运行状态)和q命令(结束调试会话)的结合

通过上面的命令,目前已经对WinDbg用户模式下的调试有了初步的认识,后续 我还会补充一些文章,对用户模式下的调试做深入介绍。

tutu洁具怎么样 解析卫浴选购的三大黄金法则|《问道》官网