windbg学习笔记(1)

前言

         这是在四月份写的本系列的第一篇笔记,没有很系统的学习windbg的使用,马上要去四叶草学习,暑假这几天先系统性的学一下windbg的使用,系列笔记会在这篇文章中。

配置篇:

  • 1)安装WinDbg,如果要进入内核模式调试的话需要进行如下步骤:
    • 1)修改虚拟机C盘根目录下配置文件boot.ini
    • 2)修改虚拟机设置,添加一个串行串口
    • 3)在宿主主机启动winbdg,进入内核调试,配置如下
    • PS:如果遇到Fail Open \.\pipe\com_1,只需要在快捷方式后面添加“windbg -b -k com:pipe,port=\.\pipe\com_1,resets=0”即可

进程篇

1.查看进程列表

         有多个命令可以显示进程列表,他们主要是:“|”,“.tlist”,“!process”,“!dml_proc”。

  • | [进程号]
             在用户态下,这个命令显示当前被调试进程的状态信息
             被调试进程列表。大多数情况下,调试器中只有一个被调试进程,但可以通过.attach或者.create命令同时挂载或创建多个调试对象。当同时对多个进程调试时,进程号是从0开始的整数。
  • tlist [选项] [模块名]
             在用户模式下显示系统当前的进程列表,存在两个参数选项:-v:显示进程的详细信息,-c显示当前进程信息。默认是-v
             可以通过“.attach”或者“.create”来创建或者附件多个调试对象
  • !process:显示调试器当前运行的进程信息
             但是这个命令只能在内核调试中使用,如果在用户态中调试,则会出现如下的错误提示。No export prcoess found
  • 显示当前进程列表(内核态)
  • !process PID:显示PID的详细信息(内核态)
  • !process 0 0 XXX,exe:查找指定进程(用户态中)


             以下是根据上述命令查看到的信息包含的主要内容。

  • Cid : 进程id

  • Peb : 进程环境块的地址
  • ParentCid : 父进程id
  • DirBase : 页目录表 (用于转换虚拟地址和物理地址PDT)
  • ObjectTable : 进程的句柄表

2.进程信息

  • !peb[address]:查看指定PEB信息,在内核模式下,address可以通过!process 0 0:来查看。如果!peb不带参数,则指的是当前进程的PEB,在用户模式下,只能查看当前进程的PEB,所以不需要带参数。
  • dt nt!_PEB[address]:此命令显示系统nt模块中所定义的内核结构体PEB详细内容.

3.切换进程

         因为进程的地址空间都是相互独立的,所以我们使用dd 0x400000之类的指令是没有意义的。因为我们不确定当前用户地址空间就是我们的目标进程地址空间。因此我们首先要切换到我们想查看的进程上下文。之后才能查看它私有的地址空间。如果不按这个规定的话,有可能得出的结果是很多?????

  • .process [process address]:切换当前的进程空间。
  • .context [页目录地址]:如果不使用任何参数,.context命令将显示当前页目录地址。页目录地址就是!process命令中显示的DirBase值。进程切换后,为了检测是否正确切换,可再用!peb命令检查当前进程的环境信息。

4.加载dll

         .peb能显示当前进程所加载的dll,但是.dll能显示当前系统所在加载的dll。

线程篇

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

1.线程冰封

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

  • ~2f:暂停该进程的2号线程
  • ~2u:启动该进程的2号线程

2.线程切换

  • ~线程编号(不是TID):切换线程
  • !TEB:查看线程信息块
  • ~ 线程号 s:在多线程间作切换,需使用~命令的s参数
  • ~~【线程ID】 s: 注意这个命令中的[]并非可选符,而是命令的一部分。例如命令:~~[11a0] s,它将当前线程切换到线程ID为0x11a0的线程。线程ID是系统维护的系统唯一的ID值。

3.遍历线程

  • ~*k:显示所有线程的栈信息
  • ~*r:显示线程的寄存器信息
  • ~e -cmd:对线程执行操作:例如:~e k;r就是对线程执行k和r操作。

查找句柄

         利用!handle来查找句柄。

  • !handle <句柄索引><标志><进程ID>;句柄索引为0,代表输出所有句柄,为4是输出第一个句柄。
  • Object标明的是对象的地址。
  • Type标明的是句柄的类型。
  • HandleCount标明的是引用次数。
  • PointerCount标明的是指针指针引用次数。

异常与事件

  • “.lastevent”:显示最近发生的调试事件
  • “.exr [mem]”:显示一个异常记录的详细信息
  • “.exr -1”:显示最近的一条异常信息,可以使用-1代替记录地址。
  • “!anaylze” :用于分析最近的异常事件,并显示分析结果。
    • -v:显示详细信息。尤其适用于调试错误
    • -f:强制所有事件都当成异常来处理
    • -hang:这个选项很有用,对于遇到死锁的情况,它会分析原因。在内核环境中,它分析内核锁和DPC栈;在用户环境中,它分析线程的调用栈。用户环境中,调试器只会对当前线程进行分析,所以一定要将线程环境切换到最可能引起问题的那个线程中去,才有帮助。这个参数非常有用,当真的遇到死锁时,它可以救命(另一个分析死锁的有效命令是!locks)
  • “!error”:此命令和VC里面内置的errlook工具类似。用来根据错误码,查看对应的可读错误信息。微软系统中常用的全局错误码有两套,一套是Win32错误码,通过函数GetLastError()获得的值;另一套是NTSTATUS值。!error命令对这二者都能支持。区别的方法,若错误码后面无参数1,则为Win32错误码;否则就是NTSTATUS错误码。
  • “!gle”: 此命令是Get Last Error的缩写。它调用Win32接口函数GetLastError()取得线程的错误值,并打印分析结果。如果带有-all选项,则针对当前进程的所有线程执行GetLastError()操作;否则仅针对当前线程。