#调试命令窗口 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #使用gflags.exe工具(在windbg所在目录下),让某个进程启动时,拉取windbg进行调试 如下截图:当名称为captcomm.exe的进程启动时,拉起windbg调试 也可通过脚本命令来实现: // 运行captcomm.exe时,启动windbg调试 reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\captcomm.exe" /v Debugger /t REG_SZ /d "C:\Program Files\Debugging Tools for Windows (x86)\windbg.exe" /f // 解除启动时windbg调试 reg delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\captcomm.exe" /f // 64位系统上,也可以设置以下注册表节点 reg add "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\captcomm.exe" /v Debugger /t REG_SZ /d "C:\Program Files\Debugging Tools for Windows (x86)\windbg.exe" /f // 解除启动时windbg调试 reg delete "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\captcomm.exe" /f // 测试发现:HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options和HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution Options指向的是同一数据,修改其中任何一个都可以
#重要说明 (1) windbg命令分为标准命令,元命令和扩展命令。 标准命令提供最基本的调试功能,不区分大小写。如:bp g dt dv k等 元命令提供标准命令没有提供的功能,也内建在调试引擎中,以.开头。如.sympath .reload等 扩展命令用于扩展某一方面的调试功能,实现在动态加载的扩展模块中,以!开头。如!analyze等 (2) 进入调试状态时,直接回车可重复执行上一条命令;按上下方向键可以浏览和选择以前输入过的命令 (3) 神奇的Tab键,进行命令补全;ESC清除当前命令输入框中的文本 (4) 选择Command窗口中的内容,然后右击进行复制 (5) 使用;作为分隔符,可以在同一行输入多条命令 (6) 上图红色框中的“0:000”。【0为当前调试会话的进程号;000为调试会话的线程号】 (7) 当命令提示符显示*BUSY*时,即使命令输入框可以输入命令,但输入的命令不会立即被执行,要等windbg空闲时才能执行。 可使用Ctrl + Break来终止一个长时间未完成的命令 (8) 一次可以执行多条命令,命令间用分号;分隔 【如:bp main;bp `view.cpp:120`】,一次打2个断点 (9) 为了保证windbg流畅运行,在调试时,尽量不要开启Watch、Locals、Registers、Call Stack、Processes and Threads窗口,直接用command来获取信息 #启动调试 windbg -I // 将windbg设置成默认调试器 windbg "notepad.exe" arguments // 使用windbg启动调试notepad.exe windbg -p 4200 // 将windbg附加到一个正在运行的pid为4200的进程上 windbg -pn "notepad.exe" // 将windbg附加到一个正在运行的名为notepad.exe的进程上 windbg –z "c:\mydumpfile.dmp" // 调试mydumpfile.dmp文件 .opendump "c:\mydumpfile.dmp" // 调试mydumpfile.dmp文件 .attach 0n4220 // 4220为十进制pid,使用该命令附加调试时,必须先存在一个调试会话 .detach // 分离调试 .restart // 重启并调试 .kill // 强制结束当前调试 q // 结束当前调试会话,回到基础工作空间,并结束调试进程 qd // 结束当前调试会话,回到基础工作空间,但不结束调试进程 #获取帮助 // 打印出所有标准命令 .help // 打印出所有元命令 .hh // 打开windbg的chm帮助文件 .hh bp // 打开windbg的chm帮助文件bp命令介绍页 command /? // 打印命令command具体参数用法 #注释符 * // 注释整行 $$ // 注释(遇到分号结束) #配置调试环境 注:如果被调试的模块(无论移动到本机的何处)是用本机代码编译产生的,都不需要进行符号和源代码的路径设置 .sympath // 查看当前符号查找路径 .sympath c:\symbols // 将符号查找路径设为:c:\symbols .sympath+ c:\symbols // 将c:\symbols添加到符号查找路径集合中 .symfix // 将符号查找路径设为:SRV*WinDbg安装目录\Sym*http://msdl.microsoft.com/download/symbols .symfix f:\symbols // 将符号查找路径设为:SRV*f:\symbols*http://msdl.microsoft.com/download/symbols .symfix+ f:\symbols // 将SRV*f:\symbols*http://msdl.microsoft.com/download/symbols添加到符号查找路径集合中 .srcpath // 查看当前源文件查找路径 .srcpath f:\src // 将源文件查找路径设为:f:\src 注:必须勾选上菜单“Debug”-“Source Mode”;另外pdb须与exe、dll等执行模块匹配上 .srcpath+ f:\src // 将f:\src添加到源文件查找路径集合中 .exepath // 查看可执行文件查找路径 .exepath f:\bin // 将可执行文件查找路径设为:f:\bin .exepath+ f:\bin // 将f:\bin添加到可执行文件查找路径集合中 #系统信息 vertarget // os信息 !cpuid // cpu信息 #wow64模式 【x64版windbg调试win32程序】 .load wow64exts // [!load wow64exts] 加载wow64exts.dll模块 注:!sw就是wow64exts中的命令 .unload wow64exts // [!unload wow64exts] 卸载wow64exts.dll模块 .effmach // 查看当前调试mode:x86、x64等 .effmach x86 // 切换到x86栈环境 注:需要先执行.load wow64exts来加载wow64exts.dll模块 .effmach . // 切换回x64 !sw // [!wow64exts.sw] 在多个mode:x86、x64上进行循环切换 注:如果win32程序在x64的mode下,会看到地址是64位的 !k // [!wow64exts.k] 打印32位、64位堆栈 !k 5 // [!wow64exts.k 5] 打印32位、64位堆栈,栈帧个数为5 !info // [!wow64exts.info] 输出wow64相关的PEB、TEB和TLS基本信息 !r // [!wow64exts.r] 输出处理器当前上下文信息 !r dumpTest!main // [!wow64exts.r dumpTest!main] 输出main函数地址的上下文信息 #符号加载与查看 除了使用ld和.reload命令直接加载符号文件,某些使用符号的命令也可以触发调试器来加载符号,如:栈回溯命令(k*)和反汇编命令(u)等。 值得说明的是,windbg缺省使用的是懒惰式符号加载策略,当它收到模块加载事件时,它通常是不会加载符号的,符号状态显示为deferred(延迟加载)。 .symopt // 显示当前所有符号选项 .symopt+ flags // 添加符号选项 .symopt- flags // 删除符号选项 !sym noisy // 激活详细符号加载(noisy symbol loading)显示 !sym quiet // 禁止详细符号加载显示 ld * // 为所有模块加载符号 ld kernel32 // 加载kernel32.dll的符号 .reload // 为所有已加载模块载入符号信息 .reload /i // 重新加载不匹配符号的模块【dmp文件没有对应的pdb时使用】 .reload /i TGame.exe // 重新加载不匹配符号的TGame.exe .reload /f /v // f:强制立即模式(不允许延迟载入) v:详细模式 .reload /f @"c:\windows\System32\verifier.dll" // 为指定模块加载符号信息 .reload /f TGame.exe // 为TGame.exe加载符号信息 .reload /u TGame.exe // 卸载TGame.exe及其载符号信息 .chain // 显示已经加载进来的模块 x *! // 列出所有模块对应的符号信息 lm // 列出所有模块(加载和未加载)对应的符号信息 lmv // 列出所有模块(加载和未加载)对应的符号信息 lmvm ntdll // 查看ntdll.dll的详细信息(注意exe、dll等都不要带后缀名) x ConsoleTest!* // 列出ConsoleTest模块中的所有符号 x ConsoleTest!add* // 列出ConsoleTest模块中的所有add开头的符号 x /t /v ConsoleTest!* // 带数据类型、符号类型和大小信息,列出ConsoleTest模块中的所有符号 x kernel32!*LoadLib* // 列出kernel32模块中所有含LoadLib字样的符号 #进程 | // 列出调试进程 |* // 列出调试进程 |N // 参看序数为N的调试进程 |Ns // 切换序数为N的进程为当前调试进程 #线程 ~ // 列出线程 ~* // 所有线程 ~* k // 所有线程堆栈信息 ~* r // 所有线程寄存器信息 ~. // 查看当前线程 ~0s // 查看主线程 ~# // 查看导致当前事件或异常的线程 ~N // 查看序数为N的线程 ~~[n] // 查看线程ID为n的线程 ~Ns // 切换序数为N的线程为当前调试线程 ~N f // 冻结序数为N的线程 ~N u // 解冻序数为N的线程 ~N n // Suspend序数为N的线程 ~N m // Resume序数为N的线程 ~* e !gle // 显示所有线程最后的一个错误信息 e后可以为任意windbg命令 .ttime // 查看当前线程时间信息 !runaway //显示当前进程的所有线程时间信息
#断点 bl // 列出所有断点 bc * // 清除所有断点 bc 1 // 清除1号断点 bc 1 2 5 // 清除1号、2号、5号断点 be * // 启用所有断点 be 1 // 启用1号断点 be 1 2 5 // 启用1号、2号、5号断点 bd * // 禁用所有断点 bd 1 // 禁用1号断点 bd 1 2 5 // 禁用1号、2号、5号断点 bp 7c801b00 // 在7c801b00地址处放置一个断点 bp `ConsoleTest.cpp:36` // 在ConsoleTest.cpp的36行处放置一个断点 bp main // 在main函数的起始处放置一个断点 bp CSecondLoader::CSecondLoader // 在CSecondLoader的构造函数处放置一个断点 bp TestCommon! CTest::add // 在TestCommon.dll的Test.cpp文件的CTest::add()函数起始处放置一个断点 bp `ConsoleTest.cpp:40` ".if (poi(pVar)>5) {}; {g}" // ".if (Condition) {Optional Commands}; {g}" 条件断点 pVar指针指向的值>5,执行空语句(;),断住 否则继续执行 bp `ConsoleTest.cpp:40` "j (poi(pVar)>5) ' '; 'g'" // "j (Condition) 'Optional Commands'; 'g'" j为条件表示式:条件断点 pVar指针指向的值>5,执行空语句(;),断住 否则继续执行 注:Condition表达式语法默认的是MASM表达式语法。使用复杂C++表达式时我们需要用@@c++()将表达式包围住;如:"j @@c++(*pVar>5) ' '; 'g'" --------------------------------- x表示的一个地址 --------------------------------- bp `ConsoleTest.cpp:40` "j @eax = 0xa3 ''; 'g'" // j为条件表示式:条件断点 寄存器eax的值为0xa3时断住 bp kernel32!CreateFileA // 在系统API的CreateFileA函数处放置一个断点 bp kernel32!CreateFileA ".echo;.printf\"CreateFileA(%ma,%p,%p), ret=\",poi(esp+4),dwo(esp+8),dwo(esp+c);gu;.printf\"%N\",eax;.echo;g" // 不断住进程情况下,打印所有的CreateFileA调用 bp kernel32!CreateFileW ".echo;.printf\"CreateFileW(%mu,%p,%p), ret=\",poi(esp+4),dwo(esp+8),dwo(esp+c);kn;g;" // 不断住进程,打印所有的CreateFileW调用及堆栈信息 bp advapi32!RegOpenKeyExA ".echo;.printf\"RegOpenKeyExW(%p,\\\"%ma\\\",%N,%N,%p) returned: \", dwo(esp+4), poi(esp+8), dwo(esp+c), dwo(esp+10), dwo(esp+14);gu;.printf\"%N\",eax;.echo;g" // 不断住进程情况下,打印所有的RegOpenKeyExA调用(打开注册表键值) --------------------------------- 注意:有些函数Symbol Name与导出函数名可能不一致,例如SetWindowPos,这时可以用Dependency查看相应的导出函数地址(Entry Point列):0x00018E5E,然后在windbg菜单“Debug”—〉“Modules...”对话框中获得user32.dll模块起始地址0x77d10000,在两个值相加后的绝对地址处直接设置断点:bp 77d28E5E;也可以通过x user32!*命令列出全部Symbols列表,查找77d28E5E,找到SetWindowPos对应的Symbol Name为“NtUserSetWindowPos”,然后通过符号设置断点:bp user32!NtUserSetWindowPos。通过符号设置断点的好处是当dll代码改变时,不需要修改,windbg会根据符号来自动匹配函数地址。 --------------------------------- bu // 保存断点,其用法和bp一样 bm add_* // 匹配add_开头的函数,并在这些函数起始处都打上断点 ba w4 0x0483dfe0 // 当对0483dfe0地址写操作时停下,前面要带上0x,否则会报错 // ba [r|w|e] [Size] Addr [r=read/write, w=write, e=execute], Size=[1|2|4 bytes] #调试执行控制 g // Go(F5) gH // 执行gH命令强制让调试器返回已经处理了这个异常。【Go with Exception Handled】 // 系统收到这个回复后会停止分发异常(因为调试器声称已经处理了异常),恢复调试目标继续执行, // 但由于异常条件仍在,所以还会产生异常,于是再次分发,WinDBG再次中断到命令模式。 gN // 【Go with Exception Not Handled】 // 执行gN命令强制让调试器返回没有处理了这个异常,那么系统会进一步分发该异常, // 如果没有其他调试器也不处理,最后系统会弹出程序终止对话框。 gu // 执行到当前函数完成时停下 【Go Up】 Ctrl+Break // 暂停正在运行的程序 p // 单步执行(F10) 【Step】 p 2 // 2为步进数目 pc // 执行到下一个函数调用处停下 【Step to Next Call】 pa 7c801b0b // 执行到7c801b0b地址处停下 【Step to Adress】 t // Step into(F11) 【Trace】 tc // 执行到下一个进入点处停下 【Trace to Next Call】 ta 7c801b12 // 执行到7c801b12地址处停下 【Trace to Adress】 # 查看句柄 !handle // 查看所有句柄的ID !handle 000007f8 1 // 查看ID为000007f8的句柄的类型 !handle 000007f8 4 // 查看ID为000007f8的句柄的名称 !handle 0 5 // 查看所有句柄的类型和名称 !htrace -enable // 使用windbg调试运行时,启用跟踪所有打开句柄或关闭句柄的调用以及相应的栈回溯 !htrace // 进程运行结束后,打印所有打开句柄或关闭句柄的调用以及相应的栈回溯 !htrace -diff // 只打印没有关闭的句柄的信息及栈回溯 Handle = 0x00000038 - OPEN 0x049ca83c: +0x049ca83c --------------------------------------
# 查看变量 === 0n(十进制) 0x(十六进制) 0t(8进制) 0y(2进制) 可以使用n [8|10|16]命令来修改数值进制表示方式(输入n可查看当前进制,默认为16进制)=== -- VC6.0的Link选项需要将/pdbtype:sept改为/pdbtype:con, 否则生成的pdb文件中将不包含如自定义结构体,类等信息 dt nRet // 查看局部变量nRet的类型与值(函数参数变量请用dv命令) --注:编译器很多时候会把一些局部变量优化掉,这个时候就会出现找不到nRet符号的情况 dt ntdll!* // 显示ntdll里的所有类型信息 dt *!*IMAGE_DOS* // 显示所有模块中含有IMAGE_DOS字符的类型信息 test1!IMAGE_DOS_HEADER dt myApp!g_app // 表示显示myApp进程里全局变量g_app的内存布局(注:vc6见上述说明) dt WindbgTest!CTest // 查看模块WindbgTest的CTest的内存布局(注:vc6见上述说明) dt WindbgTest!CTest 0x0041f8d4 // 将0x0041f8d4地址处内容按照模块WindbgTest的CTest的内存布局来解析(注:vc6见上述说明) Windbg默认会用寄存器ECX里面的值作为this指针地址,其实这样是有时候是错误的。 dt tagMSG 0x0336e54c // 类型要使用tagMSG,不能使用typedef产生出的MSG typedef struct tagMSG { dt this // 查看this指针的类型及成员变量(注:vc6见上述说明) dt -b this // 查看this指针的类型及成员变量,如果某一成员变量为结构体,则把其结构成员也一一打印出来 // -b开关指定递归显示所有子类型信息,-r[n]指定递归显示的深度,如-r0表示不显示子类型信息 dt -v _PEB @$PEB // 使用详细模式查看PEB(process's environment block)内存结构 dt _TEB ny LastErrorValue // 只查看TEB(thread's environment block)结构成员LastErrorValue this->m_nPen // 查看成员变量的值(注:vc6见上述说明) this // 查看this指针中的成员变量(注:vc6见上述说明) nCount // 显示局部变量nCount的地址(前面4198608为10进制表示地址,004010d0为16进制表示地址) 形如:Evaluate expression: 4198608 = 004010d0 HeapTest!CTest::Add // 显示HeapTest模块中CTest类中的Add函数地址 dv // 显示当前函数内所有局部变量,函数参数的值 dv n* // 显示当前函数内n开头的所有局部变量,函数参数的值 dv nCount // 查看局部变量nCount的值 dv a // 查看函数参数变量a的值 dv /t /i /V /a|/n|/z /***************************************** 更加详细地显示当前函数内所有局部变量,函数参数信息 *****************************************/ x // 用法和dv命令一致,显示当前函数内所有局部变量,函数参数变量的地址与值 --注:编译器很多时候会把一些局部变量优化掉,这个时候就会出现找不到符号的情况 #调用堆栈 k // 显示当前调用堆栈 kn // 带栈编号显示当前调用堆栈 kb // 打印出前3个函数参数的当前调用堆栈 02a9ffec 00000000 01e511f9 0174c570 00000000 kernel32!BaseThreadStart+0x37 ====================================================== Windows 7 Ultimate Service Pack 1 [Build 6.1.7601] CallStack: //0x64D90000为模块MSVCR90.dll的基地址,3AF68为崩溃指令在模块中的偏移值,(0492B5D0,00074685,0000000C,00462482)为参数信息 kb 5 // 只显示最上的5层调用堆栈 kv // 在kb的基础上增加了函数调用约定等信息 kp // 显示每一层函数调用的完整参数,包括参数类型、名字、取值(必须是完整符号的情况下,private symbols);注意:若程序被优化,这些值不一定对 kd // 打印堆栈的地址 dds 02a9ffec // 从02a9ffec地址处,向高地址方向搜索函数调用栈帧 dds // 执行完dds 02a9ffec后,可通过dds命令继续进行搜索 .frame // 显示当前栈帧 .frame n // 设置编号n的栈帧为当前栈帧(n为16进制数) .frame /r n // 设置编号n的栈帧为当前栈帧(n为16进制数) 并显示寄存器变量 !uniqstack // 显示所有线程的调用堆栈 #查看汇编 u . // 反汇编当前eip寄存器地址的后8条指令 u $eip // 反汇编当前eip寄存器地址的后8条指令 ub . // 反汇编当前ip寄存器地址的前8条指令 ub $eip // 反汇编当前eip寄存器地址的前8条指令 u main+0x29 L30 // 反汇编main+0x29地址的后30条指令 u // 反编译下8条指令 uf CTest::add // 反汇编CTest类的add函数 uf /c main // 反汇编main函数,通过/c可以查看main函数中的函数调用(call)都有哪些 ub 000c135d L20 // 查看地址为000c135d指令前的20条指令内容
#寄存器 r // 显示所有寄存器信息及发生core所在的指令 r eax, edx // 显示eax,edx寄存器信息 r eax=5, edx=6 // 对寄存器eax赋值为5,edx赋值为6 #内存 -- 注:windbg不支持中文显示 !address // 查看进程的所有内存页属性 !address 7ffd8000 // 查看7ffd8000地址处内存页属性 dd /c 5 7c801e02 // 从7c801e02内存处开始以dword为单位显示内存(宽度为:5)【默认显示128字节长度的内容】 dd /c 5 7c801e02 L8 // 从7c801e02内存处开始以dword为单位显示内存(宽度为:5)【显示8个dword】 da /c 100 7c80ff03 // 从7c80ff03内存处开始显示Ascii字符串(宽度为:100) du /c 100 7c8022f5 // 从7c8022f5内存处开始显示Unicode字符串(宽度为:100) /***************************************** d[a| u| b| w| W| d| c| q| f| D] [/c 列数] [地址] a = ascii chars *****************************************/ dyb /c 3 7c801e02 // 从7c801e02内存处开始,显示byte及二进制(宽度为:3) /***************************************** dy[b | d] .. // b = binary+byte d = binary+dword *****************************************/ s -w 522e0000 L0x100 0x1212 0x2212 0x1234 // 表示在起始地址522e0000之后的0x100个单位内搜索0x1212 0x2212 0x1234系列的起始地址 s -u 522e0000 527d1000 "web" //表示在522e0000 和527d1000之间搜索Unicode 字符串”web” ea 0x445634 "abc" // 表示在0x445634地址写入Ascii字符串abc, 不包含结束符0 eza 0x445634 "abc" // 表示在0x445634地址写入Ascii字符串abc, 包含结束符0 eu 0x445634 "abc" // 表示在0x445634地址写入Unicode字符串abc, 不包含结束符0 ezu 0x445634 "abc" // 表示在0x445634地址写入Unicode字符串abc, 包含结束符0 ed nCounter 80 // 将变量nCounter的值修改为80(注:80为10进制还是16进制,还是其他,取决于当前进制) .writemem D:\Test\0041a5e4.bin 0041a5e4 L1000 // 将内存地址处0x0041a5e4后面0x1000长度的内容拷贝存储到D:\Test\0041a5e4.bin中 #查看堆(Heap) !heap -s // 显示进程堆的个数(每一项是一个堆,也就是_HEAP结构指针,对应的API是HeapCreate) Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast dt _HEAP 00140000 // 选取一个堆的地址,打印该堆的内存结构 ntdll!_HEAP !heap -a 00140000 // 选取一个堆的地址,打印该堆的信息,比上面打印内存命令更详细直观 Index Address Name Debugging options enabled Heap entries for Segment00 in Heap 00140000 #虚拟内存 !vadump // 查看虚拟内存布局 #设置事件发生时windbg行为 sx // 显示windbg遇到每个异常和事件时的行为 sxr // 将所有异常和事件过滤器的状态重设为默认值 sxe ld // 当加载模块时,立即中断(Break)到调试器中(第一次处理机会) sxd ud // 当卸载模块时,windbg不会在第一次处理机会时中断(虽然会显示信息)。如果其他错误处理器没有处理掉该异常,执行会停止下来并中断(Break)到windbg中(第二次处理机会) sxn et // 当线程退出时,windbg会打印出一条消息 sxi ct // 当线程创建时,windbg不中断也不打印消息 #dump输出 .dump /ma "d:\mydmpfile.dmp" // 将当前调试进程输出Dump文件 #其他元命令 .tlist // 显示所有进程 .cls // 清除屏幕 .logopen c:\1.log // 将command内容输出到c:\1.log文件中 .logclose // 关闭当前打开的日志 #其他扩展命令 !analyze -v // 详细显示当前异常信息 !peb // 格式化输出PEB信息(process's environment block) PEB at 7efde000 !gle // 打印当前线程最近的错误信息LastError !gle -all // 打印所有线程的最近的错误信息 !error 897// 显示错误码为897的详细描述信息 #帮助 中文在线帮助:http://www./windbghelp/index.html windbg cmd: http://www./kewuc/windbg-cmds 【下载】 |
|