Windbg既可以显示进程和线程列表,又可以显示指定进程或线程的详细信息。调试命令可以提供比taskmgr更详尽的进程资料,在调试过程中不可或缺。

一、进程命令

  进程命令包括这些内容:显示进程列表、进程环境块、设置进程环境。

  1、进程列表

  多个命令可显示进程列表,但一般只能在特定情况下使用,它们是:|、.this、!process、!dml_proc。

  竖线命令显示当前被调试进程列表的状态信息。

  • | [进程号]

  注意:被调试进程列表。大多数情况下,调试器中只有一个被调试进程,但可以通过.attach或者.create命令同时挂载或创建多个调试对象。当同时对多个进程调试时,进程号是从0开始的整数。切换第一篇中提过。

  • .tlist [选项] [模块名]

  .tlist命令显示当前系统中的进程列表,他是目前唯一可在用户模式下显示系统当前进程列表的命令。它有两个可选项:-v显示进程详细信息,-c只显示当前进程信息。

  内核模式下同样可以使用.tlist,但更好的命令是!process。!process在内核模式下显示进程列表,和指定进程的详细信息,也能显示进程中的线程和调用栈内容。典型格式如下

  • !process:显示调试器当前运行进程信息
  • !process 0 0:显示进程列表
  • !process PID:PID是进程ID,根据进程ID显示此进程详细信息。

  此外,还有一个DML版本的进程列表命令如下:

  • !ldm_proc [进程号|进程地址]

  此命令可以看成"|"和"process"命令的DML合并版本,可在用户与内核模式下使用。显示的进程信息偏重于线程和调用栈。用户模式下次命令和"|"一样,只能显示被调试进程的信息。

  2、进程信息

  进程环境快是内核结构体,使用!peb命令参看其信息,但也可以用dt命令查看完整的结构体定义。格式如下:

  • !peb [地址]

  如果未设置PEB地址,则默认为当前进程。内核模式下可通过!process命令获取PEB结构体地址;用户模式下只能显示当前进程的PEB信息,故而一般不带参数。

  • dt nt!_peb 地址

  此命令显示系统nt模块中所定义的内核结构体PEB详细内容。使用之前必须先熟悉结构体定义。

  3、进程切换

  进程环境的切换,将伴随着与进程相关的寄存器、堆栈的切换。在不同进程环境中进程的调试结果有天壤之别。

  在内核模式中,进程切换不能使用 “| [进程号] s”了,不同于用户模式下的被调试进程间切换,而是系统存在的多进程间切换。内核环境下,以进程地址作为参数,调用如下命令以进行进程环境切换。

  • .process [进程地址]

  如果不使用任何参数,.process命令将显示当前进程地址。所谓进程地址,即ERPCESS结构体地址。

  或以页目录地址为参数,调用下面命令切换用户地址空间:

  • .context [页目录地址]

  如果不使用任何参数,.context命令将显示当前页目录地址。页目录地址就是!process命令中显示的DirBase值。

  进程切换后,为了检测是否正确切换,可再用!peb命令检查当前进程的环境信息。

二、线程命令

  命令"~"能够进行线程相关的操作。不带任何参数的情况下,它列出当前调试进程的线程。

:> ~
. Id: 152c. Suspend: Teb: 7ffdf000 Unfrozen
Id: 152c. Suspend: Teb: 7ffdd000 Unfrozen

  使用此命令可进行的线程操作包括:线程切换、线程环境、线程时间等。

  1、线程冰冻

  参数f与u分别代表freeze和unfress,前者是指冻住指定线程,后者将被冰冻线程解冻。

  • ~2f

  表示把2号线程冻住,在解冻之前,不再分发CPU时间给它。

  若要让指定线程重新运行,需使用参数u:

  • ~2u

  2、线程切换

  查看指定线程的信息,用下面的命令:

  • ~线程号

  线程号是由调试器软件内部维护的线程ID值,是一个从0开始的整数,和线程ID不是一回事。

  线程信息中包括有线程环境块地址,可通过!teb命令查看环境块信息:

  • !teb [teb地址]

  如要在多线程间作切换,需使用~命令的s参数:

  • ~ 线程号 s

  由于线程号在外部是没有太大意义的,所以另一个线程切换命令是以线程ID来标识一个线程的。这个命令比较奇怪,以双波浪线打头,格式如下:

  • ~~[线程ID] s

  注意这个命令中的[]并非可选符,而是命令的一部分。例如命令:~~[11a0] s,它将当前线程切换到线程ID为0x11a0的线程。线程ID是系统维护的系统唯一的ID值。

  线程切换实例:

:> ~
. Id: 152c. Suspend: Teb: 7ffdf000 Frozen
Id: 152c. Suspend: Teb: 7ffdd000 Frozen
:> ~~[]s
eax= ebx=7765fe8c ecx= edx= esi=0030d410 edi=
eip=776770f4 esp=0076f82c ebp=0076f9c0 iopl= nv up ei ng nz ac po cy
cs=001b ss= ds= es= fs=003b gs= efl=
ntdll!KiFastSystemCallRet:
776770f4 c3 ret
:> ~
# Id: 152c. Suspend: Teb: 7ffdf000 Frozen
. Id: 152c. Suspend: Teb: 7ffdd000 Frozen

  注意开始时0前面有1个英文句号。切换之后,英文句号变到1前面了。

  3、线程遍历

  仍然是~命令。它出了能够作为线程列表命令外,还可用来对线程进行遍历,并执行指定命令。只需借助通配符"*"即可。

  • ~*k

  显示所有线程栈信息(此命令意指:对所有线程执行k指令)。下图中,当前进程共包含两个线程,显示了这两个线程各自的栈信息:

:> ~*k

#    Id: 152c. Suspend:  Teb: 7ffdf000 Frozen
ChildEBP RetAddr
0011facc 75adcde0 ntdll!KiFastSystemCallRet
0011fad0 75adce13 user32!NtUserGetMessage+0xc
0011faec 009c148a user32!GetMessageW+0x33
0011fb2c 009c16ec notepad!WinMain+0xe6
0011fbbc 7694ed6c notepad!_initterm_e+0x1a1
0011fbc8 776937eb kernel32!BaseThreadInitThunk+0xe
0011fc08 776937be ntdll!__RtlUserThreadStart+0x70
0011fc20 ntdll!_RtlUserThreadStart+0x1b Id: 152c. Suspend: Teb: 7ffdd000 Frozen
ChildEBP RetAddr
0076f828 77676a44 ntdll!KiFastSystemCallRet
0076f82c 7765fe39 ntdll!ZwWaitForMultipleObjects+0xc
0076f9c0 7694ed6c ntdll!TppWaiterpThread+0x33d
0076f9cc 776937eb kernel32!BaseThreadInitThunk+0xe
0076fa0c 776937be ntdll!__RtlUserThreadStart+0x70
0076fa24 ntdll!_RtlUserThreadStart+0x1b

  其他有用的遍历指令包括:

  • ~*r

  显示线程寄存器信息。

  • ~*e

  上面的e是execute(执行)的缩写,后可跟一个或多个Windbg命令。它遍历线程并对每个线程执行指定命令,如:

  • ~*e k;r

  此命令意为:在所用线程环境中(~*),分别执行(e)栈指令(k)和寄存器指令(r)。

~* e !clrstack    //列出所有线程的调用堆栈

  4、线程时间

  当软件调试的时候,若发现某线程占用执行时间过多,就需要当心是否有问题。线程执行时间的多少,其实就是占用CPU执行工作的时间多少。某线程占用越多,其他线程占用的CPU时间就越少。

  线程的时间信息包括三个方面:自创建之初到现在的总消耗时间、用户模式执行时间、内核模式执行时间。需要注意的是,消耗时间一定会远远大于用户时间+内核时间,多出来的是大量空闲时间(为idle进程占用)。  使用下面的命令查看线程时间:

  • .ttime
  • !runaway 7

  在!runaway命令中加入标识值7,将显示线程的全部三种时间值。

  这两个命令的区别之处是,.ttime只能显示当前线程的时间信息,!runaway能显示当前进程的所有线程时间。

:> .ttime
Created: Thu May ::17.973 (UTC + :)
Kernel: days ::00.000
User: days ::00.000
:> !runaway
User Mode Time
Thread Time
: days ::00.031
: days ::00.000
Kernel Mode Time
Thread Time
: days ::00.234
: days ::00.000
Elapsed Time
Thread Time
: days ::40.263
: days ::40.026

三、异常与事件

  在调试器语境中,事件是一个基本概念,Windbg是事件驱动的。Windows操作系统的调试子系统,是“事件”的发生源。调试器的所有操作,都是因事件而动,因事件被处理而中继。Windows定义了9类调试事件,异常是其中一类(ID为1)。所以异常和事件,这二者是前者包含于后者的关系。

  系统对各种异常和调试事件进行了分类,执行sx命令可以列出针对当前调试目标的异常或非异常事件的处理。

:> sx
ct - Create thread - ignore
et - Exit thread - ignore
cpr - Create process - ignore
epr - Exit process - ignore
ld - Load module - output
ud - Unload module - ignore
ser - System error - ignore
ibp - Initial breakpoint - ignore
iml - Initial module load - ignore
out - Debuggee output - output av - Access violation - break - not handled
asrt - Assertion failure - break - not handled
aph - Application hang - break - not handled
bpe - Break instruction exception - break
bpec - Break instruction exception continue - handled
eh - C++ EH exception - second-chance break - not handled
clr - CLR exception - second-chance break - not handled
clrn - CLR notification exception - second-chance break - handled
cce - Control-Break exception - break
cc - Control-Break exception continue - handled
cce - Control-C exception - break
cc - Control-C exception continue - handled
dm - Data misaligned - break - not handled
dbce - Debugger command exception - ignore - handled
gp - Guard page violation - break - not handled
ii - Illegal instruction - second-chance break - not handled
ip - In-page I/O error - break - not handled
dz - Integer divide-by-zero - break - not handled
iov - Integer overflow - break - not handled
ch - Invalid handle - break
hc - Invalid handle continue - not handled
lsq - Invalid lock sequence - break - not handled
isc - Invalid system call - break - not handled
3c - Port disconnected - second-chance break - not handled
svh - Service hang - break - not handled
sse - Single step exception - break
ssec - Single step exception continue - handled
sbo - Stack buffer overflow - break - not handled
sov - Stack overflow - break - not handled
vs - Verifier stop - break - not handled
vcpp - Visual C++ exception - ignore - handled
wkd - Wake debugger - break - not handled
wob - WOW64 breakpoint - break - handled
wos - WOW64 single step exception - break - handled * - Other exception - second-chance break - not handled

  可以看到这几个调试事件,当发生进程退出(Exit Process)和初始化断点(Initial breakpoint)事件的时候,调试器应当被中断(Break)。模块加载(Load Modual)以及有调试输出(Debuggen Output)时,需要输出相关信息;其他的都被忽略掉,不做处理(Ignore)。我们分析以下前两个事件。使用调试器调试记事本进程时,不管是用.attach挂载方式还是.create创建方式,在调试器正式侵入记事本进程前,都会有一个中断(Initial breakpoint异常);调试开始后运行一段时间,在外面将记事本关闭,又会发生一个中断(Exit process异常)。

  可以通过导航栏的Debug => Event Filters...打开事件设置对话框。这个对话框中列出了全部调试事件,用户可分别对它们进行设置。

  

  这个对话框列出了对于当前调试会话可用的全部调试事件。针对每个调试事件,可设置其属性。右列Execution和Continue两组单选键,分别表示事件的中断属性与中继属性。右列Argument按钮可设置调试事件执行参数(上图中LoadModule事件有一个Kernel32.dll参数,即当Kernel32.dll模块被加载时,调试器将被中断),Commands按钮可设置事件两轮机会发生时的执行命令。

  • sxr:

  此命令将当前所有对调试事件的设置,恢复到调试器的默认设置。最后一个字母r表示Reset。

  • sx{e|d|n|i} -h

  这4个命令分别代表了图8-38中Execution组(中断属性)中的四个按钮,即Enable、Disable、Output、Ignore。Enable是开启中断,Disable是禁止事件中断(但对于一场,只禁止第一轮机会,第二轮机会到来时仍会中断到调试器),Output是禁止中断但会输出相关信息,Ignore表示完全忽略这个事件(对于异常,Output和Ignore两选项使得两轮机会都不会中断到调试器)。

  • .lastevent

  显示最近发生的一个调试事件,往往是导致中断发生的那个。

:> .lastevent
Last event: 152c.: Break instruction exception - code (first/second chance not available)
debugger time: Thu May ::01.914 (UTC + :)
  • .exr:

此命令显示一个异常记录的详细内容,传入一个异常记录地址:

  • .exr 记录地址

  如果仅仅为了显示最近的一条异常记录,可以用-1代替异常记录地址:

  • .exr -1

  由于异常是事件的一种,所以使用.exr -1命令得到的异常,可能和使用.lastevent命令获取的事件,是同一个。但二者显示的信息各有侧重点。

:> .exr -
ExceptionAddress:
ExceptionCode: (Break instruction exception)
ExceptionFlags:
NumberParameters:
  • .bugcheck

  此命令不带参数。在内核环境下,显示当前bug check的详细信息;可用于活动调试或者crash dump调试环境中。用户环境中,此命令不可用。

  • !analyze

  此命令分析当前最近的异常事件(如果在进行dump分析,则是bug check),并显示分析结果。这个异常事件,就是上面.lastevent命令对应的事件。

  1. -v:显示异常的详细信息,这个选项在调试错误的时候,最有用。
  2. -f:f是force的缩写。强制将任何事件都当作异常来分析,即使仅仅是普通的断点事件。将因此多输出一些内容。
  3. -hang:这个选项很有用,对于遇到死锁的情况,它会分析原因。在内核环境中,它分析内核锁和DPC栈;在用户环境中,它分析线程的调用栈。用户环境中,调试器只会对当前线程进行分析,所以一定要将线程环境切换到最可能引起问题的那个线程中去,才有帮助。这个参数非常有用,当真的遇到死锁时,它可以救命(另一个分析死锁的有效命令是!locks)。
  4. -show bug-check-代码 [参数]:在内核环境下,显示指定的bug check的详细信息。
  • !error

  此命令和VC里面内置的errlook工具类似。用来根据错误码,查看对应的可读错误信息。微软系统中常用的全局错误码有两套,一套是Win32错误码,通过函数GetLastError()获得的值;另一套是NTSTATUS值。!error命令对这二者都能支持。区别的方法,若错误码后面无参数1,则为Win32错误码;否则就是NTSTATUS错误码。

  比如,获取错误码为2的Win32错误信息,可用:!error 2

  获取错误码为2的NTSTATUS错误信息,可用:!error 2 1

  • !gle

  此命令是Get Last Error的缩写。它调用Win32接口函数GetLastError()取得线程的错误值,并打印分析结果。如果带有-all选项,则针对当前进程的所有线程执行GetLastError()操作;否则仅针对当前线程。

  • gh/gn

  这两个命令是g命令的扩展。

  1. gh:是go with Exception handled的缩写,意思是:把异常标识为已处理并继续执行程序;gh的作用在于,当遇到某个可以忽略的非致命异常时,将它先跳过,继续执行程序。
  2. gn:是go with Exception not handled的缩写,对异常不进行任何处理,而继续执行程序。这时候,程序自己的异常处理模块将有机会处理异常。

四、局部变量

  有两个命令可以打印当前的局部变量列表:x和dv。x第二篇已讲过,dv是Display local Variable的缩写。

  上图无法体现dv比x命令在显示局部变量上的高明之处。dv命令有几个开关选项。

  • /v:显示虚拟地址(virtual);
  • /i:显示变量详细信息(information),包括局部变量、全局变量、形参、函数变量等。
  • /t:显示变量类型(type),如int、char等等。
  • /f:可指定进行分析的函数,需指定函数名。

  命令中选项/f wmain是指针对wmain函数(即_tmain)分析其局部变量。看第一个变量argc,"prv param"对应/i开关选项;"@ebp_0x08"对应/v开关选项;"int"对应/t开关选项。

五、显示类型

  利用dt命令可以查看结构体的类型定义。命令dt是Display Type的缩写。当我们要查看一些内核结构体的定义时,dt命令是最直接有效的手段。

Windbg 进程与线程 《第三篇》的更多相关文章

  1. 线程池 异步I/O线程 <第三篇>

    在学习异步之前先来说说异步的好处,例如对于不需要CPU参数的输入输出操作,可以将实际的处理步骤分为以下三步: 启动处理: 实际的处理,此时不需要CPU参数: 任务完成后的处理: 以上步骤如果仅仅使用一 ...

  2. 转载 线程池 异步I/O线程 <第三篇>

    在学习异步之前先来说说异步的好处,例如对于不需要CPU参数的输入输出操作,可以将实际的处理步骤分为以下三步: 启动处理: 实际的处理,此时不需要CPU参数: 任务完成后的处理: 以上步骤如果仅仅使用一 ...

  3. Java中的进程与线程(总结篇)

    详细文档: Java中的进程与线程.rar 474KB 1/7/2017 6:21:15 PM 概述: 几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进 ...

  4. java中进程与线程的三种实现方式

    一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是一个线程. 进程:进程是指 ...

  5. python 学习笔记八 进程和线程 (进阶篇)

    什么是线程(thread)? 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执 ...

  6. python的进程与线程(三)

    线程的锁 1.几个概念 讲起线程的锁,先要了解几个概念:什么是并行?什么是并发?什么是同步?什么是异步?          并发:是指系统具有处理多个任务(动作)的能力          并行:是指系 ...

  7. Java-多线程第三篇3种创建的线程方式、线程的生命周期、线程控制、线程同步、线程通信

    1.Java使用Thread类代表线程.     所有的线程对象必须是Thread类或其子类的实例. 当线程继承Thread类时,直接使用this即可获取当前线程,Thread对象的getName() ...

  8. Linux 平台如何查看某个进程的线程数?

    Linux 平台如何查看某个进程的线程数?   三种方法:1. 使用top命令,具体用法是 top -H 加上这个选项,top的每一行就不是显示一个进程,而是一个线程. 2. 使用ps命令,具体用法是 ...

  9. Python之路第一课Day9--随堂笔记之二(进程、线程、协程篇)

    本节内容 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者消费者模型 Queu ...

随机推荐

  1. Hibernate第一个程序

    1. 下载资源:www.hibernate.org 2. 资源介绍hibernate-release-4.3.10.Final a) Documentation  相关文档 b) Lib 相关jar包 ...

  2. Codeforces Round #367 (Div. 2) Hard problem

    Hard problem 题意: 有n个字符串,对第i个字符串进行反转操作代价为ci. 要使n个字符串按照字典序从小到大排列,最小的代价是多少. 题解: 反转就是reverse操作,比如说45873反 ...

  3. Android 开发中常用 ADB 命令总结

    adb 的全称为 Android Debug Bridge,就是起到调试桥的作用.通过 adb 我们可以在 Eclipse 中方便通过 DDMS 来调试 Android 程序,说白了就是 debug ...

  4. SQL查詢數據字典

    SELECT d.name AS 表名 , f.value AS 表说明, 字段序号 = a.colorder , 字段名 = a.name , 标识 THEN '√' ELSE '' END , 主 ...

  5. Java SE 第二十二讲----接口interface

    1.接口:interface:接口的地位等同于class,接口中的所有方法都是抽象方法.在声明接口中的方法的时候,可以使用abstract关键字也可以不使用.通常情况下,都会省略掉abstract关键 ...

  6. oracle安装—Windows7旗舰版32位安装oracle10g方法

    首先要下载支持Vista版本的Oracle 10g下载完成后解压出来:http://download.oracle.com/otn/nt/oracle10g/10203/10203_vista_w2k ...

  7. C#开发COM组件供其他开发环境或工具调用介绍(转)

    由于工作原因涉及到这一块的开发,由于之前并未接触过,所以本篇文章也是在参考了各种资料后,自己实现并通过通过测试之后所整理的备忘录以及一些个人观点. 希望对刚接触这类型开发的朋友有所帮助,若有不足之处还 ...

  8. 组播(Multicast)传输

    组播(Multicast)传输: 在发送者和每一接收者之间实现点对多点网络连接. 如果一台发送者同时给多个的接收者传输相同的数据,也只需复制一份的相同数据包.它提高了数据传送效率.减少了骨干网络出现拥 ...

  9. Django session 详解-part II-session

    Django中的session是一个高级工具,它可以让用户存储个人信息以便在下次访问网站中使用这些信息.session的基础还是cookie,但是它提供了一些更加高级的功能.请看下面的一个例子: 使用 ...

  10. Highcharts 对数组的要求

    function Reflush(phaid,proid) { $.post('GetProjectSummer.ashx', { proid: proid, phaid: phaid }, func ...