Windbg.exe的基础命令介绍
1 背景
工作多年,关于Windbg一直没有写过总结。为了以后查阅和方便分享,特写此文。
windbg.exe的优点就是绿色,体积小,速度快,功能强大。对于测试环境可以很快的部署。
一般情况,用windbg查死机非常方便。本文先介绍一下windbg常用的一些指令。
为了不涉密,选中一款开源软件做为调试目标。
2 调试目标
Notepad++.exe
3 源码&编译:
Notepad++ release 8.4.2.tar.gz
最新版本的Notepad++要求使用VS2019编译。这里不再介绍VS2019的安装.
VS2019编译Notepad++64位Release版本。编译结果如下:
4 抓取DUMP
双击运行Notepad++.exe. 使用procdump.exe抓取一个DUMP
F:\Softwares\SysinternalsSuite_20211125>procdump.exe -ma -64 notepad++.exe ProcDump v10.11 - Sysinternals process dump utility Copyright (C) 2009-2021 Mark Russinovich and Andrew Richards Sysinternals - www.sysinternals.com [17:01:34] Dump 1 initiated: F:\Softwares\SysinternalsSuite_20211125\notepad++.exe_220709_170134.dmp [17:01:34] Dump 1 writing: Estimated dump file size is 118 MB. [17:01:34] Dump 1 complete: 118 MB written in 0.3 seconds [17:01:35] Dump count reached.
5 安装Windbg
下载Windbg.exe
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools
不再演示安装的具体过程。
6 启动调试
由于编译的是Notepad++的64位版本,此处一定要使用64位的Windbg.exe。
调试器的位数要与DUMP的位数保持一致。32位的dump使用32位windbg调试,64位dump使用64位windbg调试。
D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\bin64
7 加载符号
自行编译notepad++就是为了产生符号文件。下面的分析会用到符号的解析:
加载方法:File->Symbol File Path…
8 基本指令
8.1 vertarget命令:
查看环境信息
0:000> vertarget Windows 10 Version 19044 MP (6 procs) Free x64 Product: WinNt, suite: SingleUserTS Edition build lab: 19041.1.amd64fre.vb_release.191206-1406 Machine Name: Debug session time: Sat Jul 9 17:01:34.000 2022 (UTC + 8:00) System Uptime: 11 days 2:38:10.023 Process Uptime: 0 days 0:02:57.000 Kernel time: 0 days 0:00:00.000 User time: 0 days 0:00:00.000
8.2 ~命令:
查看线程列表
0:000> ~ . 0 Id: 2ca0.3858 Suspend: 0 Teb: 0000001a`514b7000 Unfrozen 1 Id: 2ca0.5430 Suspend: 0 Teb: 0000001a`514c5000 Unfrozen 2 Id: 2ca0.709c Suspend: 0 Teb: 0000001a`514c7000 Unfrozen 3 Id: 2ca0.1368 Suspend: 0 Teb: 0000001a`514c9000 Unfrozen 4 Id: 2ca0.81a8 Suspend: 0 Teb: 0000001a`514cb000 Unfrozen 5 Id: 2ca0.6edc Suspend: 0 Teb: 0000001a`514cd000 Unfrozen
8.3 lm命令:
查看模块列表
0:000> lmf start end module name 00007ff7`03c70000 00007ff7`04275000 notepad__ D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\bin64\notepad++.exe 00007ffc`89cd0000 00007ffc`89d0e000 dataexchange C:\Windows\System32\dataexchange.dll 00007ffc`95cc0000 00007ffc`95cc7000 msimg32 C:\Windows\System32\msimg32.dll 00007ffc`97360000 00007ffc`97836000 wininet C:\Windows\System32\wininet.dll
……
8.4 k命令:
打印堆栈。
0:000> k # Child-SP RetAddr Call Site 00 0000001a`513feaa8 00007ffc`b8261b3e win32u!NtUserGetMessage+0x14 01 0000001a`513feab0 00007ff7`03f7f5f2 user32!GetMessageW+0x2e 02 0000001a`513feb10 00007ff7`03fb87b2 notepad__!wWinMain+0x2632 [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\winmain.cpp @ 733] 03 (Inline Function) --------`-------- notepad__!invoke_main+0x21 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 118] 04 0000001a`513ffbb0 00007ffc`b7e17034 notepad__!__scrt_common_main_seh+0x106 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 05 0000001a`513ffbf0 00007ffc`b8ac2651 kernel32!BaseThreadInitThunk+0x14 06 0000001a`513ffc20 00000000`00000000 ntdll!RtlUserThreadStart+0x21
使用~*打印所有线程,再加上p打印函数入参的值。即~* kp
0:000> ~* kp . 0 Id: 2ca0.3858 Suspend: 0 Teb: 0000001a`514b7000 Unfrozen # Child-SP RetAddr Call Site 00 0000001a`513feaa8 00007ffc`b8261b3e win32u!NtUserGetMessage+0x14 01 0000001a`513feab0 00007ff7`03f7f5f2 user32!GetMessageW+0x2e 02 0000001a`513feb10 00007ff7`03fb87b2 notepad__!wWinMain(struct HINSTANCE__ * hInstance = 0x00007ff7`03c70000, struct HINSTANCE__ * __formal = 0x00000000`00000001, wchar_t * pCmdLine = 0x00000000`00000000 "", int __formal = 0n0)+0x2632 [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\winmain.cpp @ 733] 03 (Inline Function) --------`-------- notepad__!invoke_main(void)+0x21 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 118] 04 0000001a`513ffbb0 00007ffc`b7e17034 notepad__!__scrt_common_main_seh(void)+0x106 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 05 0000001a`513ffbf0 00007ffc`b8ac2651 kernel32!BaseThreadInitThunk+0x14 06 0000001a`513ffc20 00000000`00000000 ntdll!RtlUserThreadStart+0x21 1 Id: 2ca0.5430 Suspend: 0 Teb: 0000001a`514c5000 Unfrozen # Child-SP RetAddr Call Site 00 0000001a`51cff608 00007ffc`b644cbc0 ntdll!ZwWaitForMultipleObjects+0x14 01 0000001a`51cff610 00007ffc`b644cabe KERNELBASE!WaitForMultipleObjectsEx+0xf0 02 0000001a`51cff900 00007ff7`03f8e487 KERNELBASE!WaitForMultipleObjects+0xe 03 0000001a`51cff940 00007ffc`b7e17034 notepad__!FolderUpdater::watching(void * params = 0x00000180`e427f790)+0x157 [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\FileBrowser\fileBrowser.cpp @ 1643] 04 0000001a`51cffb80 00007ffc`b8ac2651 kernel32!BaseThreadInitThunk+0x14 05 0000001a`51cffbb0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 2 Id: 2ca0.709c Suspend: 0 Teb: 0000001a`514c7000 Unfrozen # Child-SP RetAddr Call Site 00 0000001a`51dffbc8 00007ffc`b64496de ntdll!NtDelayExecution+0x14 01 0000001a`51dffbd0 00007ff7`03f8f59c KERNELBASE!SleepEx+0x9e 02 (Inline Function) --------`-------- notepad__!ReadDirectoryChangesPrivate::CReadChangesServer::Run(void)+0x1c [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\ReadDirectoryChanges\ReadDirectoryChangesPrivate.h @ 143] 03 0000001a`51dffc70 00007ff7`03fd50f8 notepad__!ReadDirectoryChangesPrivate::CReadChangesServer::ThreadStartProc(void * arg = 0x00000180`e4275cb0)+0x2c [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\ReadDirectoryChanges\ReadDirectoryChangesPrivate.h @ 115]
……
8.5 x命令:
查看模块notepad__的符号,x notepad__!*,直接输入此命令,符合太多。
本处做一些简单过滤,abc开头的没有,就看ab开头的。
0:000> x notepad__!abc* 0:000> x notepad__!ab* 00007ff7`04067230 notepad__!ablWordLists = char *[5] 00007ff7`040ef370 notepad__!abort_action = class __crt_state_management::dual_state_global<void (__cdecl*)(int)> 00007ff7`04067ac8 notepad__!abaqusWordListDesc = char *[7] 00007ff7`04033890 notepad__!AboutDlg::`vftable' = <function> *[16] 00007ff7`03e93f80 notepad__!AboutDlg::destroy (void) 00007ff7`03e0aff0 notepad__!AboutDlg::doDialog (void)
……
8.6 d命令:
查看内存数据。比如查看下AboutDlg的虚表内空。上图中,我们找们找到虚函数表的地址
00007ff7`04033890 notepad__!AboutDlg::`vftable' = <function> *[16]
根据C++的内存模型,我们查看下这个地址中存了什么内容?
使用d命令打印内存,但需要指定参数,a是ASCII字符串,b是字节,d是DWORD型,p是指针类型。
由于64位的内存是64位的,需要使用dp指令。
0:000> dp 00007ff7`04033890 00007ff7`04033890 00007ff7`03e8ed30 00007ff7`03e19e30 00007ff7`040338a0 00007ff7`03e93f80 00007ff7`03e227b0 00007ff7`040338b0 00007ff7`03e229f0 00007ff7`03e22a30 …
内存地址是打印出来了,那具体是什么函数呢?
在dp后面加上s,表示符号解析。效果如下:
0:000> dps 00007ff7`04033890 00007ff7`04033890 00007ff7`03e8ed30 notepad__!RecentFilesHistorySubDlg::`scalar deleting destructor' 00007ff7`04033898 00007ff7`03e19e30 notepad__!Window::init [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\Window.h @ 33] 00007ff7`040338a0 00007ff7`03e93f80 notepad__!RecentFilesHistorySubDlg::destroy [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\AboutDlg\AboutDlg.h @ 46] …
00007ff7`040338f8 00007ff7`03c7fd00 notepad__!Scintilla::Internal::SurfaceD2D::Initialised [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\scintilla\win32\PlatWin.cxx @ 1430] 00007ff7`04033900 00007ff7`03e0b5e0 notepad__!AboutDlg::run_dlgProc [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\AboutDlg\AboutDlg.cpp @ 25] 00007ff7`04033908 00007ff7`040812e0 notepad__!DebugInfoDlg::`RTTI Complete Object Locator'
从上面的符号解析中,我们可以看到:AboutDlg继承了RecentFilesHistorySubDlg和Scintilla::Internal::SurfaceD2D等类。
对于Scintilla::Internal::SurfaceD2D这个类,查看下符号,我们看看其特殊之处:
0:000> x notepad__!Scintilla::Internal::SurfaceD2D* 00007ff7`0406c4c8 notepad__!Scintilla::Internal::SurfaceD2D::`vftable' = <function> *[2] 00007ff7`0406c898 notepad__!Scintilla::Internal::SurfaceD2D::`vftable' = <function> *[47] 00007ff7`03c83c18 notepad__!Scintilla::Internal::SurfaceD2D::Descent (class Scintilla::Internal::Font *) 00007ff7`03c72dd0 notepad__!Scintilla::Internal::SurfaceD2D::FlushCachedState (void) …… 00007ff7`03c81154 notepad__!Scintilla::Internal::SurfaceD2D::Stadium (class Scintilla::Internal::PRectangle *, class Scintilla::Internal::FillStroke *, Scintilla::Internal::Surface::Ends) 00007ff7`03c7fb04 notepad__!Scintilla::Internal::SurfaceD2D::SurfaceD2D (struct ID2D1RenderTarget *, int, int, struct Scintilla::Internal::SurfaceMode, int) 00007ff7`03c83d50 notepad__!Scintilla::Internal::SurfaceD2D::AverageCharWidth (class Scintilla::Internal::Font *)
……
看到没有,这个有两个虚拟表指针。说明是多继承.通过查看代码确实也可以证实这一点
class SurfaceD2D : public Surface, public ISetRenderingParams { SurfaceMode mode; ID2D1RenderTarget *pRenderTarget = nullptr; ID2D1BitmapRenderTarget *pBitmapRenderTarget = nullptr; bool ownRenderTarget = false; int clipsActive = 0; ... }
8.7 u命令:
反汇编。
以上面输出的
00007ff7`03c7fb04 notepad__!Scintilla::Internal::SurfaceD2D::SurfaceD2D
为例进行说明。
0:000> u 00007ff7`03c7fb04 notepad__!Scintilla::Internal::SurfaceD2D::SurfaceD2D [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\scintilla\win32\PlatWin.cxx @ 1372]: 00007ff7`03c7fb04 48895c2418 mov qword ptr [rsp+18h],rbx 00007ff7`03c7fb09 4889742420 mov qword ptr [rsp+20h],rsi 00007ff7`03c7fb0e 57 push rdi 00007ff7`03c7fb0f 4883ec40 sub rsp,40h 00007ff7`03c7fb13 488bda mov rbx,rdx 00007ff7`03c7fb16 488bf9 mov rdi,rcx 00007ff7`03c7fb19 488d0578cd3e00 lea rax,[notepad__!Scintilla::Internal::SurfaceD2D::`vftable' (00007ff7`0406c898)] 00007ff7`03c7fb20 488901 mov qword ptr [rcx],rax 0:000> u notepad__!Scintilla::Internal::SurfaceD2D::SurfaceD2D notepad__!Scintilla::Internal::SurfaceD2D::SurfaceD2D [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\scintilla\win32\PlatWin.cxx @ 1372]: 00007ff7`03c7fb04 48895c2418 mov qword ptr [rsp+18h],rbx 00007ff7`03c7fb09 4889742420 mov qword ptr [rsp+20h],rsi 00007ff7`03c7fb0e 57 push rdi 00007ff7`03c7fb0f 4883ec40 sub rsp,40h 00007ff7`03c7fb13 488bda mov rbx,rdx 00007ff7`03c7fb16 488bf9 mov rdi,rcx 00007ff7`03c7fb19 488d0578cd3e00 lea rax,[notepad__!Scintilla::Internal::SurfaceD2D::`vftable' (00007ff7`0406c898)] 00007ff7`03c7fb20 488901 mov qword ptr [rcx],rax
通过上面的观察发现,可以通过地址来汇编,也可以通过符号进行反汇编。
通常情况下,当程序异常时会记录ExceptionAddress. 这个地址很有用。用它来汇编即可以找到出错代码的详细信息。
8.8 dt命令
查看变量的指令。从字面上面是d命令的变种。
比如静态变量
static const char * const apdlWordListDesc[] = { "processors", "commands", "slashommands", "starcommands", "arguments", "functions", 0 };
通过符号找到静态变量的地址。根据这个地址打印出指针,根据指针即可以找到对应的字符串。
0:000> x notepad__!*apdlWordListDesc* 00007ff7`04068008 notepad__!apdlWordListDesc = char *[7] 0:000> dp 00007ff7`04068008 00007ff7`04068008 00007ff7`0407bbd8 00007ff7`0407bbc8 00007ff7`04068018 00007ff7`0407bbf8 00007ff7`0407bbe8 00007ff7`04068028 00007ff7`0407bc18 00007ff7`0407bc08 00007ff7`04068038 00000000`00000000 00000000`00000000 00007ff7`04068048 00000000`00000000 00000000`00000000 00007ff7`04068058 00000000`00000000 00000001`00000000 00007ff7`04068068 00000000`00000001 00000001`00000000 00007ff7`04068078 00000000`00000000 00000000`00000000 0:000> da 00007ff7`0407bbd8 00007ff7`0407bbd8 "processors" 0:000> da 00007ff7`0407bbc8 00007ff7`0407bbc8 "commands"
对于栈变量
由于栈在运行过程中,变量不断的压栈和出栈,这一部分信息不是静态的,无法追踪到历史信息,只能看到最新的栈变量信息.
建议使用工具栏的”调用栈”窗口,切换到栈。然后工具栏打开“局部变量”窗口,查看栈变量信息。如果下图所示:
对于堆变量
堆变量的查看,先要找到地址,可以从栈变量上下手,可以从静态变更下手,也可以从全局变量下手。只要能追踪到对应的指针地址就可以找到对应的值。
9. 示例:分析单例
对于大部程序中,都有一个全局变量,比如MFC中的theApp。或者一个单例,比如数据库实例或者配置实例。那么尝试从这个角度来追踪下,看看能不能查到更多的信息?
在main函数中,找到一个单例NppParameters,查看下符号,获取函数地址。
0:000> u notepad__!NppParameters::getInstance Matched: 00007ff7`03e04050 notepad__!NppParameters::getInstance (void) Matched: 00007ff7`03f5134c notepad__!NppParameters::getInstance = (inline caller) notepad__!TabBarPlus::doOwnerDrawTab+cc Matched: 00007ff7`03f512bb notepad__!NppParameters::getInstance = (inline caller) notepad__!TabBarPlus::doOwnerDrawTab+3b Matched: 00007ff7`03f5055b notepad__!NppParameters::getInstance = (inline caller) notepad__!StatusBarSubclass+16b Matched: 00007ff7`03f4f4b6 notepad__!NppParameters::getInstance = (inline caller) notepad__!SplitterContainer::runProc+346 … Ambiguous symbol error at 'notepad__!NppParameters::getInstance'
反汇编函数,拿到静态变量的地址。
0:000> u 00007ff7`03e04050 L20 notepad__!NppParameters::getInstance [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\Parameters.h @ 1302]: 00007ff7`03e04050 4883ec28 sub rsp,28h 00007ff7`03e04054 8b0de69f2e00 mov ecx,dword ptr [notepad__!_tls_index (00007ff7`040ee040)] 00007ff7`03e0405a 65488b042558000000 mov rax,qword ptr gs:[58h] 00007ff7`03e04063 ba04000000 mov edx,4 00007ff7`03e04068 488b0cc8 mov rcx,qword ptr [rax+rcx*8] 00007ff7`03e0406c 8b040a mov eax,dword ptr [rdx+rcx] 00007ff7`03e0406f 390593782d00 cmp dword ptr [notepad__!`NppParameters::getInstancePointer'::`2'::$TSS0 (00007ff7`040db908)],eax 00007ff7`03e04075 7f2b jg notepad__!NppParameters::getInstance+0x52 (00007ff7`03e040a2) 00007ff7`03e04077 488b0582782d00 mov rax,qword ptr [notepad__!`NppParameters::getInstancePointer'::`2'::instance (00007ff7`040db900)] 00007ff7`03e0407e 4883c428 add rsp,28h 00007ff7`03e04082 c3 ret 00007ff7`03e04083 48890576782d00 mov qword ptr [notepad__!`NppParameters::getInstancePointer'::`2'::instance (00007ff7`040db900)],rax 00007ff7`03e0408a 488d0d77782d00 lea rcx,[notepad__!`NppParameters::getInstancePointer'::`2'::$TSS0 (00007ff7`040db908)] 00007ff7`03e04091 e8323d1b00 call notepad__!_Init_thread_footer (00007ff7`03fb7dc8) 00007ff7`03e04096 488b0563782d00 mov rax,qword ptr [notepad__!`NppParameters::getInstancePointer'::`2'::instance (00007ff7`040db900)] 00007ff7`03e0409d 4883c428 add rsp,28h 00007ff7`03e040a1 c3 ret 00007ff7`03e040a2 488d0d5f782d00 lea rcx,[notepad__!`NppParameters::getInstancePointer'::`2'::$TSS0 (00007ff7`040db908)] 00007ff7`03e040a9 e87a3d1b00 call notepad__!_Init_thread_header (00007ff7`03fb7e28) 00007ff7`03e040ae 833d53782d00ff cmp dword ptr [notepad__!`NppParameters::getInstancePointer'::`2'::$TSS0 (00007ff7`040db908)],0FFFFFFFFh 00007ff7`03e040b5 75c0 jne notepad__!NppParameters::getInstance+0x27 (00007ff7`03e04077) 00007ff7`03e040b7 b9481b0000 mov ecx,1B48h 00007ff7`03e040bc e8eb371b00 call notepad__!operator new (00007ff7`03fb78ac) 00007ff7`03e040c1 4889442430 mov qword ptr [rsp+30h],rax 00007ff7`03e040c6 4885c0 test rax,rax 00007ff7`03e040c9 74b8 je notepad__!NppParameters::getInstance+0x33 (00007ff7`03e04083) 00007ff7`03e040cb 488bc8 mov rcx,rax 00007ff7`03e040ce e8bd6b0e00 call notepad__!NppParameters::NppParameters (00007ff7`03eeac90) 00007ff7`03e040d3 90 nop 00007ff7`03e040d4 ebad jmp notepad__!NppParameters::getInstance+0x33 (00007ff7`03e04083) 00007ff7`03e040d6 cc int 3 00007ff7`03e040d7 cc int 3
根据上图的反汇编拿到了静态变量instance 的地址。由于instance 是一个指针,需要查看这个指针的内容。拿到对象NppParameters真正的地址。
class NppParameters final { private: static NppParameters* getInstancePointer() { static NppParameters* instance = new NppParameters; return instance; }; public: static NppParameters& getInstance() { return *getInstancePointer(); };
根据真正的地址。按照 notepad__!NppParameters 进行解析。
dt notepad__!NppParameters 00000180`e0273830
0:000> dp 00007ff7`040db900 00007ff7`040db900 00000180`e0273830 00000000`80000001 00007ff7`040db910 00000001`00000000 00000000`00ffffff 00007ff7`040db920 00404050`00202030 00202030`00404050 …… 0:000> dt notepad__!NppParameters 00000180`e0273830 +0x000 _isTaskListRBUTTONUP_Active : 0 +0x004 L_END : 0n86 +0x008 _isFindReplacing : 0 +0x00c _dpiManager : DPIManager +0x018 _pXmlDoc : 0x00000180`e02e7830 TiXmlDocument +0x020 _pXmlUserDoc : 0x00000180`e22e3560 TiXmlDocument +0x028 _pXmlUserStylerDoc : 0x00000180`e22e3b90 TiXmlDocument +0x030 _pXmlUserLangDoc : (null) +0x038 _pXmlUserLangsDoc : std::vector<UdlXmlFileState,std::allocator<UdlXmlFileState> > +0x050 _pXmlToolIconsDoc : (null)
…
关这个命令的扩展
dt notepad__!NppParameters 00000180`e0273830
dt notepad__!NppParameters . 00000180`e0273830
增加一个点会展开一级显示,增加两个点会展开两级显示。