4WinDbg-常用命令(!locks )

!locks不可用

一个很有用的调试线程死锁的命令!locks在windbg 最新的版本6.11.1.40X(X为任意数字)不可用了,运行!locks会提示下面错误:

0:001> !locks

NTSDEXTS: Unable to resolve ntdll!RTL_CRITICAL_SECTION_DEBUG type

NTSDEXTS: Please check your symbols

解决方案:回退到版本6.10.3.233,

下载地址:http://msdl.microsoft.com/download/symbols/debuggers/dbg_x86_6.10.3.233.msi

安装上面版本,或者简单替换C:\Program Files\Debugging Tools for Windows (x86)\winxp目录下的ntsdexts.dll为老版本.

vertarget、lm

vertarget

vertarget:显示目标机的Microsoft Windows操作系统版本

用户模式:

0:000> vertarget

Windows 7 Version 7601 (Service Pack 1) MP (4 procs) Free x86 compatible

Product: WinNt, suite: SingleUserTS

kernel32.dll version: 6.1.7601.18847 (win7sp1_gdr.150508-1512)

Machine Name:

Debug session time: Tue Aug 11 10:52:03.810 2015 (GMT+8)

System Uptime: 7 days 1:19:36.017

Process Uptime: 0 days 0:00:15.234

Kernel time: 0 days 0:00:00.031

User time: 0 days 0:00:00.000

内核模式:

lkd> vertarget

Windows XP Kernel Version 2600 (Service Pack 3) UP Free x86 compatible

Product: WinNt, suite: TerminalServer SingleUserTS

Built by: 2600.xpsp.080413-2111

Machine Name:

Kernel base = 0x804d8000 PsLoadedModuleList = 0x80554fc0

Debug session time: Tue Aug 11 10:53:08.240 2015 (GMT+8)

System Uptime: 0 days 0:22:54.406

lm

lm:显示指定的已加载/未加载模块。输出中包含模块状态和路径

0:001> lm o               ///< o 仅显示已加载模块

start    end        module name

012a0000 01335000   PPSeedTool   (deferred)

.............

77dd0000 77f50000   ntdll      (pdb symbols)          C:\Program Files (x86)\Debugging Tools for Windows (x86)\sym\wntdll.pdb\370278F5B1BA4A16B0DC8199E9623C3C2\wntdll.pdb

0:001> lm l           ///< l 仅显示已加载符号信息的模块。

start    end        module name

77dd0000 77f50000   ntdll      (pdb symbols)          C:\Program Files (x86)\Debugging Tools for Windows (x86)\sym\wntdll.pdb\370278F5B1BA4A16B0DC8199E9623C3C2\wntdll.pdb

0:001> lm v ///< 显示详细信息。输出中包含符号文件名、映像文件名、校验和信息、时间戳和该模块是否是托管代码(CLR)的信息

start    end        module name

012a0000 01335000   PPSeedTool   (deferred)

Image path: PPSeedTool.exe

Image name: PPSeedTool.exe

Timestamp:        Wed Aug 05 11:21:35 2015 (55C1813F)

CheckSum:         00000000

ImageSize:        00095000

Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4

5c640000 5c6fc000   DmMain     (deferred)

Image path: I:\GameDL\Tools\PPSeedTool\build\Debug\DmMain.dll

Image name: DmMain.dll

Timestamp:        Fri Jul 24 14:49:39 2015 (55B1E003)

CheckSum:         000ADDF0

ImageSize:        000BC000

Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4

lkd> lm k    ///<(仅内核模式)仅显示内核模式符号信息

start    end        module name

804d8000 806d0480   nt         (pdb symbols)          c:\mysymbol\ntkrnlpa.pdb\30B5FB31AE7E4ACAABA750AA241FF3311\ntkrnlpa.pdb

Unloaded modules:

afef5000 aff20000   kmixer.sys

..................................

babe8000 babed000   Cdaudio.SYS

ba56e000 ba571000   Sfloppy.SYS

0:001> lm 1m     ///< 仅显示名字,用于windbg script传入

PPSeedTool

DmMain

uxtheme

MSVCR90

MSIMG32

COMCTL32

比如用于.foreach中

0:001> .foreach (module {lm1m}) {lm vm ${module}}

start    end        module name

012a0000 01335000   PPSeedTool   (deferred)

Image path: PPSeedTool.exe

Image name: PPSeedTool.exe

Timestamp:        Wed Aug 05 11:21:35 2015 (55C1813F)

CheckSum:         00000000

ImageSize:        00095000

Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4

start    end        module name

5c640000 5c6fc000   DmMain     (deferred)

Image path: I:\GameDL\Tools\PPSeedTool\build\Debug\DmMain.dll

Image name: DmMain.dll

Timestamp:        Fri Jul 24 14:49:39 2015 (55B1E003)

CheckSum:         000ADDF0

ImageSize:        000BC000

Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4

0:001> lm sm      ///< 按文件名排序

start    end        module name

768b0000 76950000   ADVAPI32   (deferred)

75400000 75484000   COMCTL32   (deferred)

// 查找地址所在模块

a Address指定包含在模块中的一个地址。只有包含该地址的模块会被显示出来。如果Address是一个表达式,它必须用圆括号括起来。

0:001> lm a 75400010

start    end        module name

75400000 75484000   COMCTL32   (deferred)

//  通配符

0:001> lm m ker*

start    end        module name

76b50000 76c60000   kernel32   (deferred)

77380000 773c7000   KERNELBASE   (deferred)

可以注意到,上面有deferred这种符号状态缩写符号状态

缩写        含义

deferred 模块已经加载,但是调试器还没有尝试加载它的符号。符号将在需要的时候被加载。

#      符号文件和可执行文件的时间戳或者校验和有一些不匹配。

T      时间戳丢失、不能访问或者等于0。

C      校验和丢失、不可访问或者等于0。

DIA 符号文件通过调试接口访问(Debug Interface Access (DIA))被加载。

Export     没有找到实际的符号文件,所以符号信息是从二进制文件的导出表中获得的。

M    符号文件和可执行文件得时间戳或校验和有些不匹配。但是,因为符号选项的原因符号文件仍然被加载了。

PERF        该二进制文件包含性能优化后的代码。标准的地址计算方法可能产生不正确的结果。

Stripped 调试信息已经从映像文件中剥离出来了。

PDB          符号是.pdb格式的。

COFF        符号是通用对象文件格式(common object file format (COFF))的。

寄存器

r 命令显示或修改寄存器、浮点寄存器、标志位、伪寄存器和预定义别名。

0:000> r   ///<直接用r,会显示当前线程的寄存器状态

eax=00000000 ebx=00000000 ecx=a5cd0000 edx=0011e128 esi=fffffffe edi=00000000

eip=77e7129b esp=0022f740 ebp=0022f76c iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

0:002> ~0s  ///< 切换到0号线程

eax=00000000 ebx=003bf8ec ecx=00000006 edx=00000000 esi=00000003 edi=552a6740

eip=76c07cb0 esp=003bf79c ebp=003bf824 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

kernel32!VDMConsoleOperation+0x1c8:

76c07cb0 83c404          add     esp,4

0:000> r  ///<<span style="font-family: Arial, Helvetica, sans-serif;">直接用r,会显示当前线程的寄存器状态</span><span style="font-family: Arial, Helvetica, sans-serif;"> </span>

eax=00000000 ebx=003bf8ec ecx=00000006 edx=00000000 esi=00000003 edi=552a6740

eip=76c07cb0 esp=003bf79c ebp=003bf824 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

kernel32!VDMConsoleOperation+0x1c8:

76c07cb0 83c404          add     esp,4

0:000> ~0 r ///< 显示0号线程

eax=00000000 ebx=003bf8ec ecx=00000006 edx=00000000 esi=00000003 edi=552a6740

eip=76c07cb0 esp=003bf79c ebp=003bf824 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

kernel32!VDMConsoleOperation+0x1c8:

76c07cb0 83c404          add     esp,4

0:000> ~* r  ///< 显示所有线程

eax=00000000 ebx=003bf8ec ecx=00000006 edx=00000000 esi=00000003 edi=552a6740

eip=76c07cb0 esp=003bf79c ebp=003bf824 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

kernel32!VDMConsoleOperation+0x1c8:

76c07cb0 83c404          add     esp,4

eax=00000001 ebx=00000000 ecx=00000000 edx=01121028 esi=00000000 edi=006cfeb0

eip=011213de esp=006cfde4 ebp=006cfeb0 iopl=0         nv up ei pl nz na po nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202

test1!ThreadProc+0x1e:

011213de b801000000      mov     eax,1

eax=7efd7000 ebx=00000000 ecx=00000000 edx=77e6fb5a esi=00000000 edi=00000000

eip=77de000c esp=0092fb8c ebp=0092fbb8 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000244

ntdll!DbgBreakPoint:

77de000c cc              int     3

改变0号线程寄存器的值

0:000> ~0 r eax=0x12345

0:000> ~0 r

eax=00012345 ebx=003bf8ec ecx=00000006 edx=00000000 esi=00000003 edi=552a6740

0:000> ~0 r eax

eax=00012345

改变所有线程寄存器的值

0:000> ~* r eax=0x11111

0:000> ~* r eax

eax=00011111

eax=00011111

eax=00011111

0x10        显示MMX寄存器。

0:000> ~0 rM 10

mm0=0000000000000000  mm1=0000000000000000

mm2=0000000000000000  mm3=0000000000000000

mm4=0000000000000000  mm5=0000000000000000

mm6=0000000000000000  mm7=0000000000000000

读写内存

d*命令显示给定范围内存的内容。

如果省略掉Range ,命令将会从上一条内存查看命令结束的位置开始。这使得可以连续的进行内存查看。

d*命令显示给定范围内存的内容。这种显示的格式和最近一次d*命令的格式相同。如果之前没有使用过d*命令,d 和db 的效果相同

0:000> d

76c07e36  90 90 90 b8 10 20 00 00-b9 00 00 00 00 8d 54 24  ..... ........T$

da   ASCII 字符

遇NULL结束

0:000> da 0x002e573c

002e573c  "I am string"

db   字节值和ASCII字符

0:000> db 0x002e573c

002e573c  49 20 61 6d 20 73 74 72-69 6e 67 00 25 73 0a 00  I am string.%s..

002e574c  00 00 01 00 00 00 00 00-01 00 00 00 02 00 00 00  ................

dc    双字值(4字节)和ASCII字符

0:000> dc 0x002e573c

002e573c  6d612049 72747320 00676e69 000a7325  I am string.%s..

002e574c  00010000 00000000 00000001 00000002  ................

dd   双字值(4字节)

0:000> dd 0x002e573c

002e573c  6d612049 72747320 00676e69 000a7325

002e574c  00010000 00000000 00000001 00000002

dD   双精度浮点数(8字节)

0:000> dD 0x002e573c

002e573c      2.18174376168e+243     1.45322123485e-308     3.23790861659e-319

002e5754      4.24399158242e-314     1.69759663297e-313     6.79038653188e-313

df    单精度浮点数(4字节)

0:000> df 0x002e573c

002e573c    4.3545723e+027   4.8418253e+030   9.4986638e-039   9.5966104e-040

002e574c    9.1835496e-041                0   1.4012985e-045   2.8025969e-045

dp   指针大小的值。该命令根据目标机的处理器是32位还是64位的,分别等于dd 或dq。

0:000> dp 0x002e573c

002e573c  6d612049 72747320 00676e69 000a7325

002e574c  00010000 00000000 00000001 00000002

dq   四字值(Quad-word values) (8 bytes)。

0:000> dq 0x002e573c

002e573c  72747320`6d612049 000a7325`00676e69

002e574c  00000000`00010000 00000002`00000001

du   Unicode字符 。

遇NULL结束

0:000> .dvalloc 100

Allocated 1000 bytes starting at 00030000

0:000> ezu 00030000 "I am unicode"

0:000> du 00030000

00030000  "I am unicode"

dw WORD值(2字节)。

0:000> dw 00030000

00030000  0049 0020 0061 006d 0020 0075 006e 0069

00030010  0063 006f 0064 0065 0000 0000 0000 0000

dW WORD值(2字节)和ASCII字符。

0:000> dW 00030000

00030000  0049 0020 0061 006d 0020 0075 006e 0069  I. .a.m. .u.n.i.

00030010  0063 006f 0064 0065 0000 0000 0000 0000  c.o.d.e.........

如果尝试显示一个非法地址,它的内容会显示为问号(?)。

e

e命令和d命令非常相似,一个读一个写嘛!

e      输入数据的格式和前一次e* 命令一样

ea   ASCII 字符串(不以NULL结尾)。

0:000> .dvalloc 100

Allocated 1000 bytes starting at 00080000

0:000> ea 00080000 "i am ansi "

0:000> db 00080000

00080000  69 20 61 6d 20 61 6e 73-69 20 00 00 00 00 00 00  i am ansi ......

eb   字节值。

0:000> eb 00080000 'i' 'i' 's'

0:000> db 00080000

00080000  69 69 73 6d 20 61 6e 73-69 20 00 00 00 00 00 00  iism ansi ......

ed   双字值(4字节)。

注意字节序从高到低

0:000> ed 00080000 6d612069

0:000> db 00080000

00080000  69 20 61 6d 20 61 6e 73-69 20 00 00 00 00 00 00  i am ansi ......

eu   Unicode字符串(非NULL结尾)。

eza NULL结尾的ASCII字符串。

ezu NULL结尾的Unicode字符串。

0:000> ezu 00080000 "hi unicode"

0:000> du 00080000

00080000  "hi unicode"

如果省略Values 参数,会提示进行输入。指定的地址和它的内容会显示出来,并且出现Input> 提示符。这时可以进行如下这些操作:

通过键入值和ENTER键来输入新的值。

通过按下SPACE然后按下ENTER来保持内存的当前值。

按下ENTER来退出当前命令

!address、!vadump、!vprot(读取内存状态)

!address

!address 扩展显示目标进程或目标机使用的内存信息

这个学习起来比较简单:我们直接使用!address -?就可以找到它的使用说明:

0:000> !address -?

!address                 - prints information on the entire address space

!address -?              - prints this help

!address <address>       - prints available information about the region

of the address space containing this address

!address -summary        - prints only summary information

!address -RegionUsageXXX - fiters the output limiting the dispaly to one

of the following types:

RegionUsageIsVAD            - `busy` region that could be charcterized better

this includes Virtual-Alloc-ed blocks, SBH heap,

memory from custom allocators, etc

RegionUsageFree             - availalble (neither committed nor reserved) region

RegionUsageImage            - region used by mapped images of binaries

RegionUsageStack            - stack of threads

RegionUsageTeb              - TEB of threads

RegionUsageHeap             - region in used by a heap

RegionUsagePageHeap         - region in use by full page-heap

RegionUsagePeb              - PEB of the process

RegionUsageProcessParametrs - parameters of the process

RegionUsageEnvironmentBlock - environment block

那么一个个说明吧:

!address显示整个地址空间和使用摘要的信息

这个太长了,它会把从0-7ffefff的全打印出来,熟悉核心编程的应该知道,正常的2G用户地址空间是这样划分的:0-ffff为64K空指针区,10000-7ffeffff为用户模式分区

之后64K为禁入分区,之后就是内核模式分区,要看它们的信息,需要用到以下的表,

Filter 值          显示的内存区域

RegionUsageIsVAD          "busy" 区域。包括所有 虚拟分配块、SBH堆、自定义内存分配器(custom allocators)的内存、以及地址空间中所有属于其他分类的内存块。

RegionUsageFree   目标的虚拟地址空间中所有可用内存。包括所有非提交(committed)和非保留(reserved)的内存。

RegionUsageImage         用来映射二进制映像的内存区域。

RegionUsageStack 用作目标进程的线程的堆栈的内存区域。

RegionUsageTeb     用作目标进程中所有线程的线程环境块(TEB)的内存区域。

RegionUsageHeap 用作目标进程的堆的内存区域。

RegionUsagePageHeap 用作目标进程的整页堆(full-page heap)的内存区域。

RegionUsagePeb    目标进程的进程环境块(PEB)的内存区域。

RegionUsageProcessParametrs      用作目标进程启动参数的内存区域。

RegionUsageEnvironmentBlock     用作目标进程的环境块的内存区域。

下面这些Filter值按照内存类型来指定内存。

Filter 值          显示的内存类型

MEM_IMAGE          映射的文件属于可执行映像一部分的内存。

MEM_MAPPED       映射的文件不属于可执行映像一部分的内存。这种内存包含哪些从页面文件映射的内存。

MEM_PRIVATE        私有的(即不和其他进程共享)并且未用来映射任何文件的内存。

下面的Filter 值按照状态来指定内存:

Filter 值          显示的内存状态

MEM_COMMIT      当前已提交给目标使用的所有内存。已经在物理内存或者页面文件中为这些内存分配了物理的存储空间。

MEM_RESERVE       所有为目标以后的使用保留的内存。这种内存还没有分配物理上的存储空间。

MEM_FREE     目标虚拟地址空间中所有可用内存。包括所有未提交并且未保留的内存。该Filter 值和RegionUsageFree一样。

比如一般30000不会被分配:

0:000> !address 30000

TEB 7efdd000 in range 7efdb000 7efde000

TEB 7efda000 in range 7efd8000 7efdb000

TEB 7efd7000 in range 7efd5000 7efd8000

ProcessParametrs 00641a40 in range 00640000 00648000

Environment 00640810 in range 00640000 00648000

00030000 : 00030000 - 00010000

Type     00000000

Protect  00000001 PAGE_NOACCESS

State    00010000 MEM_FREE

Usage    RegionUsageFree

表示输出表明这是以地址0x30000开头的一个大的内存区域,该区域中包含一个以0x30000 开头,大小为0x10000的小一些的区域。因此,这个小区域是从0x30000 到0x40000。它的内存类型为0、状态为 MEM_FREE、使用方式为RegionUsageFree。 (这些值的含义,查看前面的表格。)

我们调用.dvalloc来强制分配

0:000> .dvalloc /b 30000 100

Allocated 1000 bytes starting at 00030000

0:000> !address 30000

TEB 7efdd000 in range 7efdb000 7efde000

TEB 7efda000 in range 7efd8000 7efdb000

TEB 7efd7000 in range 7efd5000 7efd8000

ProcessParametrs 00641a40 in range 00640000 00648000

Environment 00640810 in range 00640000 00648000

00030000 : 00030000 - 00001000

Type     00020000 MEM_PRIVATE

Protect  00000040 PAGE_EXECUTE_READWRITE

State    00001000 MEM_COMMIT

Usage    RegionUsageIsVAD

!vadump

这个会显示所有的虚拟内存区域和它的保护属性

0:000> !vadump

BaseAddress:       00000000

RegionSize:        00010000

State:             00010000  MEM_FREE

Protect:           00000001  PAGE_NOACCESS

BaseAddress:       00010000

RegionSize:        00010000

State:             00001000  MEM_COMMIT

Protect:           00000004  PAGE_READWRITE

Type:              00040000  MEM_MAPPED

BaseAddress:       00020000

RegionSize:        00010000

State:             00010000  MEM_FREE

Protect:           00000001  PAGE_NOACCESS

!vprot

!vprot扩展命令显示虚拟内存保护信息。可以用于活动调试和dump文件调试。

0:001> x test1!g_char

00a67004 test1!g_char = 0x00a6573c "I am string"

0:001> !vprot 00a67004

BaseAddress:       00a67000

AllocationBase:    00a50000

AllocationProtect: 00000080  PAGE_EXECUTE_WRITECOPY

RegionSize:        00002000

State:             00001000  MEM_COMMIT

Protect:           00000004  PAGE_READWRITE

Type:              01000000  MEM_IMAGE

0:001> !vprot  30000

BaseAddress:       00030000

AllocationBase:    00000000

RegionSize:        00010000

State:             00010000  MEM_FREE

Protect:           00000001  PAGE_NOACCESS

!runaway、~、|(控制进程、线程切换)

!runaway

!runaway命令显示每个线程消费的时间

Bit 0 (0x1) 让调试器显示每个线程消耗的用户模式时间(user time),默认不加就是0x1

Bit 1 (0x2) 显示每个线程消耗的内核时间(kernel time)。

Bit 2 (0x4) 显示每个线程从创建开始经历了多少时间。

就是三者的组合:1 2 3 4 5 6 7

0:002> !runaway

User Mode Time

Thread       Time

0:890       0 days 0:00:00.031

2:a00       0 days 0:00:00.000

1:1174      0 days 0:00:00.000

0:002> !runaway 1

User Mode Time

Thread       Time

0:890       0 days 0:00:00.031

2:a00       0 days 0:00:00.000

1:1174      0 days 0:00:00.000

0:002> !runaway 2

Kernel Mode Time

Thread       Time

0:890       0 days 0:00:00.062

2:a00       0 days 0:00:00.000

1:1174      0 days 0:00:00.000

0:002> !runaway 3

User Mode Time

Thread       Time

0:890       0 days 0:00:00.031

2:a00       0 days 0:00:00.000

1:1174      0 days 0:00:00.000

Kernel Mode Time

Thread       Time

0:890       0 days 0:00:00.062

2:a00       0 days 0:00:00.000

1:1174      0 days 0:00:00.000

0:002> !runaway 4

Elapsed Time

Thread       Time

0:890       0 days 0:38:34.825

1:1174      0 days 0:38:34.793

2:a00       0 days 0:38:24.528

0:002> !runaway 7

User Mode Time

Thread       Time

0:890       0 days 0:00:00.031

2:a00       0 days 0:00:00.000

1:1174      0 days 0:00:00.000

Kernel Mode Time

Thread       Time

0:890       0 days 0:00:00.062

2:a00       0 days 0:00:00.000

1:1174      0 days 0:00:00.000

Elapsed Time

Thread       Time

0:890       0 days 0:38:41.825

1:1174      0 days 0:38:41.793

2:a00       0 days 0:38:31.528

该扩展命令可以用来快速找出哪些线程循环失去控制消耗了太多CPU时间。输出中以调试器的内部线程号和16进制线程ID来标识每个线程。还会显示调试器ID,当然,主要用于分析dump文件

~ (Thread Status)

波形符(~) 命令显示指定线程或当前进程中的所有线程的信息

0:002> ~

0  Id: 433c.4154 Suspend: 1 Teb: 7efdd000 Unfrozen

1  Id: 433c.33e4 Suspend: 1 Teb: 7efda000 Unfrozen

.  2  Id: 433c.c50 Suspend: 1 Teb: 7efd7000 Unfrozen

0:002> ~*    ///<显示线程和入口函数

0  Id: 433c.4154 Suspend: 1 Teb: 7efdd000 Unfrozen

Start: test1!ILT+120(_wmainCRTStartup) (00a6107d)

Priority: 0  Priority class: 32  Affinity: f

1  Id: 433c.33e4 Suspend: 1 Teb: 7efda000 Unfrozen

Start: test1!ILT+35(?ThreadProcYAKPAXZ) (00a61028)

Priority: 0  Priority class: 32  Affinity: f

.  2  Id: 433c.c50 Suspend: 1 Teb: 7efd7000 Unfrozen

Start: ntdll!DbgUiRemoteBreakin (77e6fb5a)

Priority: 0  Priority class: 32  Affinity: f

0:002> ~0

0  Id: 433c.4154 Suspend: 1 Teb: 7efdd000 Unfrozen

Start: test1!ILT+120(_wmainCRTStartup) (00a6107d)

Priority: 0  Priority class: 32  Affinity: f

0:002> ~0s    ///<切换线程

eax=00000000 ebx=0044f5e4 ecx=00000006 edx=00000000 esi=00000003 edi=71b16740

eip=76c07cb0 esp=0044f494 ebp=0044f51c iopl=0         nv up ei pl zr na pe nc

我们可以发现,~和~*还是有点区别的,~*会把入口函数和优先级都打印出来

//暂停、恢复线程----------------------------------------------------------------------------------------------------

0  Id: 433c.4154 Suspend: 1 Teb: 7efdd000 Unfrozen意思是0号线程,进程ID为433c,线程ID为4154,暂停数为1,未冻结状态,每个线程都包含了一个暂停计数(Suspend Count),以及一个冻结/解冻(Frozen/Unfrozen)的状态。

暂停计数由Windows内核使用的值,可以通过SuspendThread和ResumeThread这两个系统函数来控制的,核心编程里说一个线程创建时,暂停次数为1,

当线程完全初始化后,系统就要查看CREATE_SUSPEND标志是否已经传递给CreateThread。如果该标志没有传递,系统便将线程的暂停计数递减为0,

该线程可以调度到一个进程中

也可以用~<tid>n增加暂停计数,用~<tid>m减少暂停计数,如下:

0:001> ~1n

0:001> ~1

.  1  Id: 433c.33e4 Suspend: 2 Teb: 7efda000 Unfrozen

Start: test1!ILT+35(?ThreadProcYAKPAXZ) (00a61028)

Priority: 0  Priority class: 32  Affinity: f

0:001> g

此时g运行,会发现1号线程被暂停了,必须使用~1m来恢复

冻结状态是调试器的状态,在window操作系统中是不支持这个概念,对于每个被冻结的线程,调试器将记住这个状态,并且在调试事件被处理之前增加线程的挂起计数,当调试事件被处理完毕时,挂起计数将被递减,对应的命令为:

冻结~<tid>f

解冻~<tid>u

冻结命令数量必须和解冻命令数量相等

| (Process Status)

这个在多进程切换时比较有效:

先模拟一个多进程调试:

windbg先打开test1

再附加到test2上:

0:002> |               ///< <span style="font-family: 'Courier New', Courier, mono;">查看总进程列表,目前只有一个</span>

.  0 id: 433c    create      name: test1.exe

0:002> .attach 0n7860  ///< 附加test2

Attach will occur on next execution

0:002> g               ///< 运行

*** wait with pending attach

Symbol search path is: srv*

Executable search path is:

ModLoad: 00300000 0031b000   D:\windbg\test2\Debug\test2.exe

ModLoad: 77dd0000 77f50000   C:\Windows\SysWOW64\ntdll.dll

eax=7efda000 ebx=00000000 ecx=00000000 edx=77e6fb5a esi=00000000 edi=00000000

eip=77de000c esp=0079fe54 ebp=0079fe80 iopl=0         nv up ei pl zr na pe nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

ntdll!DbgBreakPoint:

77de000c cc              int     3

1:004> |<span style="white-space:pre">        </span>     ///< 再次查看总进程列表,目前已有两个

0         id: 433c    create      name: test1.exe

.  1 id: 1eb4    attach       name: D:\windbg\test2\Debug\test2.exe

和线程切换相同:使用:

1:004> |0s

切换回0号进程

windbg-k、u、x(堆栈查看、汇编查看、函数查找)

k*命令显示给定线程的调用堆栈,以及其他相关信息

~0 k表示打印0号线程的调用堆栈,直接用k表示打印当前线程的调用堆栈

0:002> ~0k

ChildEBP RetAddr

0007fddc 77d191be ntdll!KiFastSystemCallRet

0007fdfc 010021b0 USER32!NtUserGetMessage+0xc

0007ff1c 010125e9 calc!WinMain+0x25f

0007ffc0 7c817077 calc!WinMainCRTStartup+0x174

0007fff0 00000000 kernel32!BaseProcessStart+0x23

0:002> k

ChildEBP RetAddr

00bfffc8 7c972119 ntdll!DbgBreakPoint

00bffff4 00000000 ntdll!DbgUiRemoteBreakin+0x2d

0:002> ~2k

ChildEBP RetAddr

00bfffc8 7c972119 ntdll!DbgBreakPoint

00bffff4 00000000 ntdll!DbgUiRemoteBreakin+0x2d

我们注意到2号线程的堆栈,这是windbg创建一个远程线程来执行DbgUiRemoteBreakin函数,它内部会调用DbgBreakPoint执行断点指令,以触发断点异常,强制把程序断了下来,所以windbg打印出来的线程总多了一条,所以不要惊讶为什么线程多了点。

其实我想弄清楚那个ChildEBP/RetAddr分别具体指什么:先K看下堆栈:

0:000> k

ChildEBP RetAddr

0012fb1c 7c95e612 ntdll!DbgBreakPoint

0012fc94 7c94108f ntdll!LdrpInitializeProcess+0xffa

0012fd1c 7c92e437 ntdll!_LdrpInitialize+0x183

00000000 00000000 ntdll!KiUserApcDispatcher+0x7

再打开反汇编窗口:

ntdll!DbgBreakPoint:

7c92120e cc              int     3

当前运行到这一行:再用r ebp查看下值:

0:000> r ebp

ebp=0012fc94

这个值是LdrpInitializeProcess前面的ChildEBP,F10单步调试到ret(也就是7c92120f)

ntdll!DbgBreakPoint:

7c92120e cc              int     3

7c92120f c3              ret

再F10调试一步,退回到LdrpInitializeProcess中(7c95e612):

7c95e60d e8fc2bfcff      call    ntdll!DbgBreakPoint (7c92120e)

7c95e612 8b4368          mov     eax,dword ptr [ebx+68h] ds:0023:7ffd3068=00000070

我们发现这个7c95e612就是DbgBreakPoint的返回地址,也就是返回地址应该是指函数退出后下个EIP的值,我以前还一直以为是那个ret/leave对应的地方,原来是ret运行后的值.

结论:

ChildEBP指的是当前堆栈运行时的ebp值

RetAddr指当前堆栈中函数退出时的下个EIP的值

kb 显示传递给堆栈回溯中的每个函数的前三个参数kp 显示传递给堆栈回溯中的每个函数的所有参数。参数列表包含参数的数据类型、名字和值。p命令是区分大小写的。使用该参数需要完整符号信息。 (事实上我看到的结果和k一样)kP 和p参数一样,显示传递给堆栈回溯中的每个函数的所有参数。但是,使用P ,函数参数在第二行中显示,而不是作为数据的结尾在行末显示。 (事实上我看到的结果和k一样)

int Add(int a,int b,int c,int d,int e,int f)

{

return a+b;

}

int _tmain(int argc, _TCHAR* argv[])

{

int c = Add(0x12,0x34,0x56,0x78,0x9a,0xbc);

return 0;

}

显示的堆栈如下:

0:000:x86> kb

ChildEBP RetAddr  Args to Child

0015fce4 010e1415 00000012 00000034 00000056 test1!Add+0x1e [f:\test1\test1\test1.cpp @ 7]

0:000:x86> kp

ChildEBP RetAddr

0015fce4 010e1415 test1!Add(int a = 0n18, int b = 0n52, int c = 0n86, int d = 0n120, int e = 0n154, int f = 0n188)+0x1e [f:\test1\test1\test1.cpp @ 7]

0:000:x86> kP

ChildEBP RetAddr

0015fce4 010e1415 test1!Add(

int a = 0n18,

int b = 0n52,

int c = 0n86,

int d = 0n120,

int e = 0n154,

int f = 0n188)+0x1e [f:\test1\test1\test1.cpp @ 7]

0:000:x86> kv

ChildEBP RetAddr  Args to Child

0015fce4 010e1415 00000012 00000034 00000056 test1!Add+0x1e (FPO: [Non-Fpo]) (CONV: cdecl) [f:\test1\test1\test1.cpp @ 7]

在某些情况下,只有一部分栈是可用的,此时调试器的k命令无法解析栈,这时因为当前的栈基指针ebp和栈顶指针esp所指向的地址是不可访问的,在这种情况下,可以使用命令K的一种变化形式,在这种形式中可以接受栈基指针、栈顶指针以及指令指针做为参数。

在手动重新构造栈的过程中,最困难的任务就是从内存中找出两个值来表示调用栈中正确的栈帧。找出这两个值的方法之一就是识别出一系列的值,这些值表示当前栈的某个地址,并且在这些值之后是一个可执行的地址,每个地址都可能是一个栈帧,此时需要通过命令k来进行验证,将这个操作重复应用于其他可能的栈帧,直到将栈重构出来并且在执行命令K时能够显示一个正确的栈,如下所示:

0:000> dc esp

000dfc94  010017eb 00000001 00000000 000dfcb4  ................

000dfca4  01001810 00000000 00000001 00000002  ................

000dfcb4  000dfcc8 01001802 00000002 00000001  ................

000dfcc4  00000003 000dfcdc 01001802 00000003  ................

000dfcd4  00000001 00000004 000dfcf0 01001802  ................

000dfce4  00000004 00000001 00000005 000dfd04  ................

000dfcf4  01001802 00000005 00000001 00000006  ................

000dfd04  000dfd18 01001802 00000006 00000001  ................

0:000> *使用被保存的ebp,存储ebp的地址以及返回值

0:000> k=000dfcc8 000dfcb4 01001802

# ChildEBP RetAddr

00 000dfcc8 01001802 02sample!KBTest::Fibonacci_stdcall+0x42 [c:\awd\chapter2\sample.cpp @ 119]

01 000dfcdc 01001802 02sample!KBTest::Fibonacci_stdcall+0x42 [c:\awd\chapter2\sample.cpp @ 119]

02 000dfcf0 01001802 02sample!KBTest::Fibonacci_stdcall+0x42 [c:\awd\chapter2\sample.cpp @ 119]

03 000dfd04 01001802 02sample!KBTest::Fibonacci_stdcall+0x42 [c:\awd\chapter2\sample.cpp @ 119]

04 000dfd18 01001802 02sample!KBTest::Fibonacci_stdcall+0x42 [c:\awd\chapter2\sample.cpp @ 119]

05 000dfd2c 01001802 02sample!KBTest::Fibonacci_stdcall+0x42 [c:\awd\chapter2\sample.cpp @ 119]

06 000dfd40 01001802 02sample!KBTest::Fibonacci_stdcall+0x42 [c:\awd\chapter2\sample.cpp @ 119]

07 000dfd54 01001802 02sample!KBTest::Fibonacci_stdcall+0x42 [c:\awd\chapter2\sample.cpp @ 119]

08 000dfd68 01001802 02sample!KBTest::Fibonacci_stdcall+0x42 [c:\awd\chapter2\sample.cpp @ 119]

因为栈是自高向低增长,而返回值是一个可执行的地址,所以猜01001802是一个函数地址,做为返回值,低位接着就是EPB了,

u

u 命令显示指定的内存中的程序代码的反汇编。

如果要反汇编某一个地址,直接用u 命令加地址

0:002> u 77d2929a

USER32!SendMessageW:

77d2929a 8bff            mov     edi,edi

77d2929c 55              push    ebp

77d2929d 8bec            mov     ebp,esp

77d2929f 56              push    esi

77d292a0 8b750c          mov     esi,dword ptr [ebp+0Ch]

77d292a3 f7c60000feff    test    esi,0FFFE0000h

77d292a9 0f85be800100    jne     USER32!SendMessageW+0x11 (77d4136d)

77d292af 8b4d08          mov     ecx,dword ptr [ebp+8]

如果存在符号文件,也可以这样直接加函数名:

0:002> u user32!SendMessagew

USER32!SendMessageW:

77d2929a 8bff            mov     edi,edi

77d2929c 55              push    ebp

77d2929d 8bec            mov     ebp,esp

77d2929f 56              push    esi

77d292a0 8b750c          mov     esi,dword ptr [ebp+0Ch]

77d292a3 f7c60000feff    test    esi,0FFFE0000h

77d292a9 0f85be800100    jne     USER32!SendMessageW+0x11 (77d4136d)

77d292af 8b4d08          mov     ecx,dword ptr [ebp+8]

注意的是,函数只支持全名,你要是写成u user32!SendMessage,windbg是认不出来的,当然你可以按TAB来让windbg自动匹配

ub 指示要反汇编的区域是向后计算的。如果使用了ubAddress ,反汇编区域是以Address结束的8或9条指令。如果用ubAddressLLength语法指定区域,则反汇编以Address结尾的指定长度的内容。

0:002> ub USER32!SendMessageW

USER32!SendMessageWorker+0x4ed:

77d29290 5b              pop     ebx

77d29291 c9              leave

77d29292 c21400          ret     14h

77d29295 90              nop

77d29296 90              nop

77d29297 90              nop

77d29298 90              nop

77d29299 90              nop

我们可以发现ub的结束后一条刚好是u的开始

同样如果存在符号文件,我们可以用uf来反汇编整个函数:

uf 命令显示内存中指定函数的反汇编代码。

x

x命令显示所有上下文中匹配指定模板的符号。可用字符通配符

x user32!send*

77d53948 USER32!SendNotifyMessageA = <no type information>

77d2fb6b USER32!SendMessageTimeoutA = <no type information>

77d6b88f USER32!SendOpenStatusNotify = <no type information>

77d6b49e USER32!SendIMEMessageExA = <no type information>

77d2d64f USER32!SendNotifyMessageW = <no type information>

77d2cdaa USER32!SendMessageTimeoutW = <no type information>

77d65b26 USER32!SendHelpMessage = <no type information>

77d6b823 USER32!SendMessageToUI = <no type information>

77d6b48d USER32!SendIMEMessageExW = <no type information>

77d2cd08 USER32!SendMessageTimeoutWorker = <no type information>

77d203fc USER32!SendRegisterMessageToClass = <no type information>

77d3c2e7 USER32!SendDlgItemMessageA = <no type information>

77d2d6db USER32!SendMessageCallbackW = <no type information>

77d6b129 USER32!SendMessageCallbackA = <no type information>

77d273cc USER32!SendDlgItemMessageW = <no type information>

77d61930 USER32!SendWinHelpMessage = <no type information>

77d291b3 USER32!SendMessageWorker = <no type information>

77d2929a USER32!SendMessageW = <no type information>

77d2f3c2 USER32!SendMessageA = <no type information></span>

所以,这个可以用来定位函数,

这里介绍下字符串通配符语法

一个星号(*)表示零个或多个字符。这个前面的例子用到了,

一个问号(?)表示任意单个字符,如下例:

<span style="color:#000000;">0:002> x user32!sendMessage?

77d2929a USER32!SendMessageW = <no type information>

77d2f3c2 USER32!SendMessageA = <no type information></span>

一个井号(#)匹配零个或多个前一个字符。例如,Lo#p 将匹配 "Lp", "Lop", "Loop", "Looop" 等等

一个加号(+)匹配一个或多个前一个字符

如果你需要使用 #、 ?、 [, ]、*、+ 字符本身,必须在这些字符前面加一个反斜杠(\)。

x还有个作用,在函数断下来后输入x,会自动打印出当前的局部变量,可以配合.frame使用

0:000:x86> kn

# ChildEBP RetAddr

00 0039fd18 010e19a8 test1!wmain+0x1e [f:\test1\test1\test1.cpp @ 12]

01 0039fd68 010e17ef test1!__tmainCRTStartup+0x1a8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 583]

0:000:x86> x

0039fd20 argc = 0n1

0039fd24 argv = 0x00033438

0039fd10 c = 0n-858993460

/v 命令可以帮助你更好理解二进制文件的内容,它将按照对象或函数所占的字节数以升序来列出序号的类型和大小

0:000> x /v /t  02sample!w*

prv func   01001c60            21 <function> 02sample!wmain (unsigned long, wchar_t **)

prv func   01002176             a <function> 02sample!wmainCRTStartup (void)

dds、dps、dqs、PE文件解析

以下默认windbg加载calc程序

d*s

dds、dps和dqs命令显示给定范围内存的内容,它们是把内存区域转储出来,并把内存中每个元素都视为一个符号对其进行解析,dds是四字节视为一个符号,dqs是每8字节视为一个符号,dps是根据当前处理器架构来选择最合适的长度

比如要看看当前stack 中保存了哪些函数地址,就可以检查ebp  指向的内存

0:000> dds ebp

0007fdfc  0007ff1c

0007fe00  010021b0 calc!WinMain+0x25f

0007fe04  0007fee8

0007fe08  00000000

0007fe0c  00000000

0007fe10  00000000

0007fe14  7c80b741 kernel32!GetModuleHandleA

0007fe18  000a232f

0007fe1c  00000000

由于 COM Interface 和C++ Vtable 里面的成员函数都是顺序排列的,所以这个命令可以方便

地找到虚函数表中具体的函数地址。比如用下面的命令可以找到OpaqueDataInfo 类型中虚

函数对应的实际函数地址:

0:002> x ole32!OpaqueData*

76aa6a41 ole32!OpaqueDataInfo::GetOpaqueData = <no type information>

76aa6b3b ole32!OpaqueDataInfo::UnSerialize = <no type information>

76aa6c16 ole32!OpaqueDataInfo::SerializableQueryInterface = <no type information>

76aa5748 ole32!OpaqueDataInfo::QueryInterface = <no type information>

76aa6393 ole32!OpaqueDataInfo::CopyOpaqueData = <no type information>

76aa5757 ole32!OpaqueDataInfo::AddRef = <no type information>

76a57107 ole32!OpaqueDataInfo::UnSerializeCallBack = <no type information>

76aa5766 ole32!OpaqueDataInfo::Release = <no type information>

769a697c ole32!OpaqueDataInfo::`vftable' = <no type information>

76aa69cb ole32!OpaqueDataInfo::AddOpaqueData = <no type information>

769bfae2 ole32!OpaqueDataInfo::GetOpaqueDataCount = <no type information>

76aa6b24 ole32!OpaqueDataInfo::Serialize = <no type information>

769c9df3 ole32!OpaqueDataInfo::AddRef = <no type information>

769c9ebc ole32!OpaqueDataInfo::Release = <no type information>

76aa6a97 ole32!OpaqueDataInfo::DeleteOpaqueData = <no type information>

76aa6bc9 ole32!OpaqueDataInfo::GetCLSID = <no type information>

76aa57c0 ole32!OpaqueDataInfo::OpaqueDataInfo = <no type information>

769c1cb0 ole32!OpaqueDataInfo::GetAllOpaqueData = <no type information>

76aa54b9 ole32!OpaqueDataInfo::~OpaqueDataInfo = <no type information>

76aa6be9 ole32!OpaqueDataInfo::SetParent = <no type information>

76aa5693 ole32!OpaqueDataInfo::`scalar deleting destructor' = <no type information>

76aa6b78 ole32!OpaqueDataInfo::GetSize = <no type information>

76aa6540 ole32!OpaqueDataInfo::QueryInterface = <no type information>

769a69a0 ole32!OpaqueDataInfo::`vftable' = <no type information>

0:002> dds 769a69a0

769a69a0  76aa6540 ole32!OpaqueDataInfo::QueryInterface

769a69a4  769c9df3 ole32!InstanceInfo::AddRef

769a69a8  769c9ebc ole32!InstantiationInfo::Release

769a69ac  76aa69cb ole32!OpaqueDataInfo::AddOpaqueData

769a69b0  76aa6a41 ole32!OpaqueDataInfo::GetOpaqueData

769a69b4  76aa6a97 ole32!OpaqueDataInfo::DeleteOpaqueData

769a69b8  769bfae2 ole32!ServerLocationInfo::GetRemoteServerName

769a69bc  769c1cb0 ole32!CComProcessInfo::GetProcessName

769a69c0  76a57107 ole32!InstanceInfo::UnSerializeCallBack

769a69c4  00000021

769a69c8  76a2d73d ole32!CClassMoniker::QueryInterface

769a69cc  76a339fb ole32!CErrorObject::AddRef

769a69d0  76a0679a ole32!CClassMoniker::Release

769a69d4  76a06a39 ole32!CClassMoniker::GetUnmarshalClass

769a69d8  76a06a56 ole32!CClassMoniker::GetMarshalSizeMax

769a69dc  76a06a99 ole32!CClassMoniker::MarshalInterface

769a69e0  76a2d2b9 ole32!CClassMoniker::UnmarshalInterface

769a69e4  76a07099 ole32!CClassMoniker::ReleaseMarshalData

769a69e8  769e288e ole32!CDdeObject::COleItemContainerImpl::IsRunning

769a69ec  76a2d72e ole32!CClassMoniker::QueryInterface

769a69f0  76a339dd ole32!CErrorObject::AddRef

769a69f4  76a06ab8 ole32!CClassMoniker::Release

769a69f8  76a069d1 ole32!CClassMoniker::GetComparisonData

769a69fc  90909090

769a6a00  76a066c9 ole32!CClassMoniker::QueryInterface

769a6a04  76a05efd ole32!CSCMergedEnum<IEnumCATEGORYINFO,tagCATEGORYINFO>::AddRef

769a6a08  76a067a6 ole32!CClassMoniker::Release

769a6a0c  76a068f3 ole32!CClassMoniker::GetClassID

769a6a10  769acee9 ole32!CDdeServerCallMgr::AddRef

769a6a14  76a2d7f2 ole32!CClassMoniker::Load

769a6a18  76a06931 ole32!CClassMoniker::Save

769a6a1c  76a07055 ole32!CClassMoniker::GetSizeMax

PE文件解析

start    end        module name

01230000 0124b000   test1    C (private pdb symbols)

1.dos头:

0:001> dt IMAGE_DOS_HEADER 01230000

test1!IMAGE_DOS_HEADER

+0x000 e_magic          : 0x5a4d

+0x002 e_cblp           : 0x90

+0x004 e_cp             : 3

+0x006 e_crlc           : 0

+0x008 e_cparhdr        : 4

+0x00a e_minalloc       : 0

+0x00c e_maxalloc       : 0xffff

+0x00e e_ss             : 0

+0x010 e_sp             : 0xb8

+0x012 e_csum           : 0

+0x014 e_ip             : 0

+0x016 e_cs             : 0

+0x018 e_lfarlc         : 0x40

+0x01a e_ovno           : 0

+0x01c e_res            : [4] 0

+0x024 e_oemid          : 0

+0x026 e_oeminfo        : 0

+0x028 e_res2           : [10] 0

+0x03c e_lfanew         : 224

来确认下这个是PE文件:

0:001> da 01230000 +0

01230000  "MZ."

2.nt头

e_lfanew定义了真正的PE文件头的相对偏移量RVA

0:001> da 01230000 +0n224

012300e0  "PE"

0:001> dt IMAGE_NT_HEADERS 01230000 +0n224

test1!IMAGE_NT_HEADERS

+0x000 Signature        : 0x4550

+0x004 FileHeader       : _IMAGE_FILE_HEADER

+0x018 OptionalHeader   : _IMAGE_OPTIONAL_HEADER

3.文件头

0:001> dt IMAGE_FILE_HEADER 01230000 +0n224+0x4

test1!IMAGE_FILE_HEADER

+0x000 Machine          : 0x14c

+0x002 NumberOfSections : 7

+0x004 TimeDateStamp    : 0x55cae429

+0x008 PointerToSymbolTable : 0

+0x00c NumberOfSymbols  : 0

+0x010 SizeOfOptionalHeader : 0xe0

+0x012 Characteristics  : 0x102

由Characteristics  : 0x102可以得出这是个#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // 文件可执行#define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32位机器

4.扩展文件头

0:001> dt _IMAGE_OPTIONAL_HEADER 01230000 +0n224+0x18

test1!_IMAGE_OPTIONAL_HEADER

+0x000 Magic            : 0x10b

+0x002 MajorLinkerVersion : 0x9 ''

+0x003 MinorLinkerVersion : 0 ''

+0x004 SizeOfCode       : 0x3600

+0x008 SizeOfInitializedData : 0x4200

+0x00c SizeOfUninitializedData : 0

+0x010 AddressOfEntryPoint : 0x1107d

+0x014 BaseOfCode       : 0x1000

+0x018 BaseOfData       : 0x1000

+0x01c ImageBase        : 0x1230000

+0x020 SectionAlignment : 0x1000

+0x024 FileAlignment    : 0x200

+0x028 MajorOperatingSystemVersion : 5

+0x02a MinorOperatingSystemVersion : 0

+0x02c MajorImageVersion : 0

+0x02e MinorImageVersion : 0

+0x030 MajorSubsystemVersion : 5

+0x032 MinorSubsystemVersion : 0

+0x034 Win32VersionValue : 0

+0x038 SizeOfImage      : 0x1b000

+0x03c SizeOfHeaders    : 0x400

+0x040 CheckSum         : 0

+0x044 Subsystem        : 3

+0x046 DllCharacteristics : 0x8140

+0x048 SizeOfStackReserve : 0x100000

+0x04c SizeOfStackCommit : 0x1000

+0x050 SizeOfHeapReserve : 0x100000

+0x054 SizeOfHeapCommit : 0x1000

+0x058 LoaderFlags      : 0

+0x05c NumberOfRvaAndSizes : 0x10

+0x060 DataDirectory    : [16] _IMAGE_DATA_DIRECTORY

5.区块表

紧接着IMAGE_NT_HEADERS后的是区块表

0:001> ?? sizeof(IMAGE_NT_HEADERS)

unsigned int 0xf8

0:001> ? 01230000 +0n224 + 0xf8

Evaluate expression: 19071448 = 012301d8

这是起始地址

注意查找结构体时使用dt,不要使用x

0:001> dt *!*IMAGE_SECTION*

test1!IMAGE_SECTION_HEADER

test1!PIMAGE_SECTION_HEADER

test1!_IMAGE_SECTION_HEADER

test1!_IMAGE_SECTION_HEADER

test1!_IMAGE_SECTION_HEADER::<unnamed-type-Misc>

MSVCR90D!IMAGE_SECTION_HEADER

MSVCR90D!PIMAGE_SECTION_HEADER

MSVCR90D!_IMAGE_SECTION_HEADER

0:001> dt IMAGE_SECTION_HEADER 012301d8

test1!IMAGE_SECTION_HEADER

+0x000 Name             : [8]  ".textbss"

+0x008 Misc             : _IMAGE_SECTION_HEADER::<unnamed-type-Misc>

+0x00c VirtualAddress   : 0x1000

+0x010 SizeOfRawData    : 0

+0x014 PointerToRawData : 0

+0x018 PointerToRelocations : 0

+0x01c PointerToLinenumbers : 0

+0x020 NumberOfRelocations : 0

+0x022 NumberOfLinenumbers : 0

+0x024 Characteristics  : 0xe00000a0

0:001> ?? sizeof(IMAGE_SECTION_HEADER)

unsigned int 0x28

0:001> dt IMAGE_SECTION_HEADER 012301d8+0x28

test1!IMAGE_SECTION_HEADER

+0x000 Name             : [8]  ".text"

+0x008 Misc             : _IMAGE_SECTION_HEADER::<unnamed-type-Misc>

+0x00c VirtualAddress   : 0x11000

+0x010 SizeOfRawData    : 0x3600

+0x014 PointerToRawData : 0x400

+0x018 PointerToRelocations : 0

+0x01c PointerToLinenumbers : 0

+0x020 NumberOfRelocations : 0

+0x022 NumberOfLinenumbers : 0

+0x024 Characteristics  : 0x60000020

0:001> dt IMAGE_SECTION_HEADER 012301d8+0x28*2

test1!IMAGE_SECTION_HEADER

+0x000 Name             : [8]  ".rdata"

+0x008 Misc             : _IMAGE_SECTION_HEADER::<unnamed-type-Misc>

+0x00c VirtualAddress   : 0x15000

+0x010 SizeOfRawData    : 0x1e00

+0x014 PointerToRawData : 0x3a00

+0x018 PointerToRelocations : 0

+0x01c PointerToLinenumbers : 0

+0x020 NumberOfRelocations : 0

+0x022 NumberOfLinenumbers : 0

+0x024 Characteristics  : 0x40000040

0:001> dt IMAGE_SECTION_HEADER 012301d8+0x28*3

test1!IMAGE_SECTION_HEADER

+0x000 Name             : [8]  ".data"

+0x008 Misc             : _IMAGE_SECTION_HEADER::<unnamed-type-Misc>

+0x00c VirtualAddress   : 0x17000

+0x010 SizeOfRawData    : 0x200

+0x014 PointerToRawData : 0x5800

+0x018 PointerToRelocations : 0

+0x01c PointerToLinenumbers : 0

+0x020 NumberOfRelocations : 0

+0x022 NumberOfLinenumbers : 0

+0x024 Characteristics  : 0xc0000040

0:001> dt IMAGE_SECTION_HEADER 012301d8+0x28*4

test1!IMAGE_SECTION_HEADER

+0x000 Name             : [8]  ".idata"

+0x008 Misc             : _IMAGE_SECTION_HEADER::<unnamed-type-Misc>

+0x00c VirtualAddress   : 0x18000

+0x010 SizeOfRawData    : 0xa00

+0x014 PointerToRawData : 0x5a00

+0x018 PointerToRelocations : 0

+0x01c PointerToLinenumbers : 0

+0x020 NumberOfRelocations : 0

+0x022 NumberOfLinenumbers : 0

+0x024 Characteristics  : 0xc0000040

0:001> dt IMAGE_SECTION_HEADER 012301d8+0x28*5

test1!IMAGE_SECTION_HEADER

+0x000 Name             : [8]  ".rsrc"

+0x008 Misc             : _IMAGE_SECTION_HEADER::<unnamed-type-Misc>

+0x00c VirtualAddress   : 0x19000

+0x010 SizeOfRawData    : 0xe00

+0x014 PointerToRawData : 0x6400

+0x018 PointerToRelocations : 0

+0x01c PointerToLinenumbers : 0

+0x020 NumberOfRelocations : 0

+0x022 NumberOfLinenumbers : 0

+0x024 Characteristics  : 0x40000040

0:001> dt IMAGE_SECTION_HEADER 012301d8+0x28*6

test1!IMAGE_SECTION_HEADER

+0x000 Name             : [8]  ".reloc"

+0x008 Misc             : _IMAGE_SECTION_HEADER::<unnamed-type-Misc>

+0x00c VirtualAddress   : 0x1a000

+0x010 SizeOfRawData    : 0x600

+0x014 PointerToRawData : 0x7200

+0x018 PointerToRelocations : 0

+0x01c PointerToLinenumbers : 0

+0x020 NumberOfRelocations : 0

+0x022 NumberOfLinenumbers : 0

+0x024 Characteristics  : 0x42000040

0:001> dt IMAGE_SECTION_HEADER 012301d8+0x28*7

test1!IMAGE_SECTION_HEADER

+0x000 Name             : [8]  ""

+0x008 Misc             : _IMAGE_SECTION_HEADER::<unnamed-type-Misc>

+0x00c VirtualAddress   : 0

+0x010 SizeOfRawData    : 0

+0x014 PointerToRawData : 0

+0x018 PointerToRelocations : 0

+0x01c PointerToLinenumbers : 0

+0x020 NumberOfRelocations : 0

+0x022 NumberOfLinenumbers : 0

+0x024 Characteristics  : 0

5.导入表:

导入表的入口在_IMAGE_DATA_DIRECTORY[1]中,所以取得地址

0:001> dt _IMAGE_OPTIONAL_HEADER 01230000 +0n224+0x18

test1!_IMAGE_OPTIONAL_HEADER

+0x058 LoaderFlags      : 0

+0x05c NumberOfRvaAndSizes : 0x10

+0x060 DataDirectory    : [16] _IMAGE_DATA_DIRECTORY

0:001> ?? sizeof(_IMAGE_DATA_DIRECTORY)

unsigned int 8

0:001> dt _IMAGE_DATA_DIRECTORY 01230000 +0n224+0x18+0x68

test1!_IMAGE_DATA_DIRECTORY

+0x000 VirtualAddress   : 0x18000

+0x004 Size             : 0x3c

发现IMAGE_IMPORT_DESCRIPTOR dt不到

typedef struct _IMAGE_IMPORT_DESCRIPTOR {

union {

DWORD   Characteristics;            // 0 for terminating null import descriptor

DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)

};

DWORD   TimeDateStamp;                  // 0 if not bound,

// -1 if bound, and real date\time stamp

//     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)

// O.W. date/time stamp of DLL bound to (Old BIND)

DWORD   ForwarderChain;                 // -1 if no forwarders

DWORD   Name;

DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)

} IMAGE_IMPORT_DESCRIPTOR;

大小明显是20,那么我们用dd来划分吧:

0:001> dd 01230000 +0x18000 L5

01248000  0001803c 00000000 00000000 00018324

01248010  000181a8

0:001> da 00018324+01230000

01248324  "KERNEL32.dll"

0:001> dd 01230000 +0x18000+0n20 L5

01248014  000180e8 00000000 00000000 00018346

01248024  00018254

0:001> da 00018346+01230000

01248346  "MSVCR90D.dll"

0:001> dd 01230000 +0x18000+0n20*2 L5

01248028  00000000 00000000 00000000 00000000

01248038  00000000

Name为0表示没有其他导入表了

6.查找导入函数

这是在运行中的PE文件,所以使用FirstThunk

0:001> dds 0x181a8+0x1230000

012481a8  76b63485 kernel32!CreateThread

012481ac  76b617e9 kernel32!GetCurrentProcess

012481b0  76b7d7d2 kernel32!TerminateProcess

012481b4  76b63478 kernel32!FreeLibrary

012481b8  76b64412 kernel32!VirtualQuery

012481bc  76b64908 kernel32!GetModuleFileNameW

012481c0  76b614c9 kernel32!GetProcessHeap

012481c4  77dfe046 ntdll!RtlAllocateHeap

012481c8  76b614a9 kernel32!HeapFree

012481cc  76b634b9 kernel32!GetSystemTimeAsFileTime

012481d0  76b611f8 kernel32!GetCurrentProcessId

012481d4  76b61430 kernel32!GetCurrentThreadId

012481d8  76b6110c kernel32!GetTickCount

012481dc  76b61705 kernel32!QueryPerformanceCounter

012481e0  76b68781 kernel32!SetUnhandledExceptionFilter

012481e4  76b6498f kernel32!LoadLibraryA

012481e8  76b61222 kernel32!GetProcAddress

012481ec  76b65a03 kernel32!lstrlen

012481f0  76b6190e kernel32!MultiByteToWideChar

012481f4  76b616ed kernel32!WideCharToMultiByte

012481f8  76be4755 kernel32!DebugBreak

012481fc  76b6585e kernel32!RaiseException

01248200  76b64a15 kernel32!IsDebuggerPresent

01248204  76b61464 kernel32!InterlockedCompareExchange

01248208  76b610ff kernel32!Sleep

0124820c  76b61442 kernel32!InterlockedExchange

01248210  76b876f7 kernel32!UnhandledExceptionFilter

01248214  00000000

0:001> dds 00018254+0x1230000

01248254  6a4df8d0 MSVCR90D!__dllonexit [f:\dd\vctools\crt_bld\self_x86\crt\src\onexit.c @ 267]

01248258  6a44d430 MSVCR90D!_lock [f:\dd\vctools\crt_bld\self_x86\crt\src\mlock.c @ 333]

0124825c  6a44d480 MSVCR90D!_unlock [f:\dd\vctools\crt_bld\self_x86\crt\src\mlock.c @ 371]

01248260  6a44e170 MSVCR90D!_decode_pointer [f:\dd\vctools\crt_bld\self_x86\crt\src\tidtable.c @ 161]

01248264  6a4eca80 MSVCR90D!_except_handler4_common

01248268  6a4ec880 MSVCR90D!_crt_debugger_hook [f:\dd\vctools\crt_bld\self_x86\crt\src\dbghook.c @ 62]

0124826c  6a4df460 MSVCR90D!_invoke_watson [f:\dd\vctools\crt_bld\self_x86\crt\src\invarg.c @ 137]

01248270  6a4fbc20 MSVCR90D!_controlfp_s

01248274  6a4c0280 MSVCR90D!terminate [f:\dd\vctools\crt_bld\self_x86\crt\prebuild\eh\hooks.cpp @ 95]

01248278  6a44c040 MSVCR90D!_initterm_e [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 938]

0124827c  6a44c010 MSVCR90D!_initterm [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 889]

01248280  6a4de7b0 MSVCR90D!_CrtDbgReportW [f:\dd\vctools\crt_bld\self_x86\crt\src\dbgrpt.c @ 252]

01248284  6a4e3010 MSVCR90D!_CrtSetCheckCount [f:\dd\vctools\crt_bld\self_x86\crt\src\dbgheap.c @ 3241]

01248288  6a525768 MSVCR90D!__winitenv

0124828c  6a44b9d0 MSVCR90D!exit [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 411]

01248290  6a44ba10 MSVCR90D!_cexit [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 426]

01248294  6a4e32e0 MSVCR90D!_XcptFilter [f:\dd\vctools\crt_bld\self_x86\crt\src\winxfltr.c @ 206]

01248298  6a44b9f0 MSVCR90D!_exit [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 419]

0124829c  6a44c600 MSVCR90D!__wgetmainargs [f:\dd\vctools\crt_bld\self_x86\crt\src\crtlib.c @ 98]

012482a0  6a44ba50 MSVCR90D!_amsg_exit [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 460]

012482a4  6a44ad60 MSVCR90D!__set_app_type [f:\dd\vctools\crt_bld\self_x86\crt\src\errmode.c @ 87]

012482a8  6a44e070 MSVCR90D!_encode_pointer [f:\dd\vctools\crt_bld\self_x86\crt\src\tidtable.c @ 86]

012482ac  6a44cf90 MSVCR90D!__p__fmode [f:\dd\vctools\crt_bld\self_x86\crt\src\crtlib.c @ 748]

012482b0  6a44cef0 MSVCR90D!__p__commode [f:\dd\vctools\crt_bld\self_x86\crt\src\crtlib.c @ 714]

012482b4  6a5266cc MSVCR90D!_adjust_fdiv

012482b8  6a44ada0 MSVCR90D!__setusermatherr

012482bc  6a4e6d80 MSVCR90D!_configthreadlocale [f:\dd\vctools\crt_bld\self_x86\crt\src\setlocal.c @ 420]

012482c0  6a4eb420 MSVCR90D!_CRT_RTC_INITW

012482c4  6a462450 MSVCR90D!getchar [f:\dd\vctools\crt_bld\self_x86\crt\src\fgetchar.c @ 45]

012482c8  6a46abb0 MSVCR90D!printf [f:\dd\vctools\crt_bld\self_x86\crt\src\printf.c @ 49]

012482cc  6a4df660 MSVCR90D!_onexit [f:\dd\vctools\crt_bld\self_x86\crt\src\onexit.c @ 85]

012482d0  6a4cd680 MSVCR90D!operator new [f:\dd\vctools\crt_bld\self_x86\crt\src\new.cpp @ 57]

也可以通过

OriginalFirstThunk

来查找,先找到列表:1803c为Kernel32项第一个DWORD

typedef struct _IMAGE_THUNK_DATA32 {

union {

DWORD ForwarderString;      // PBYTE 指向一个转向者字符串的RVA

DWORD Function;             // PDWORD被输入函数的内存地址

DWORD Ordinal;              // 被输入的API序数号

DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME

} u1;

} IMAGE_THUNK_DATA32;

这个函数同样在我的exe上没有找到,但很明显,这是一个4字节的数组

0:001> dd 0x1803c+0x1230000

0124803c  00018314 000186e4 000186d0 000186c2

0124804c  000186b2 0001869c 0001868a 0001867e

0124805c  00018672 00018658 00018642 0001862c

0124806c  0001861c 00018602 000185e4 000185d4

0124807c  000185c2 000185b6 000185a0 0001858a

0124808c  0001857c 0001856a 00018556 00018538

0124809c  00018530 0001851a 000186f8 00000000

012480ac  00000000 00000000 00000000 00000000

然后这里面的内存又指向以下结构

typedef struct _IMAGE_IMPORT_BY_NAME {

WORD    Hint;

BYTE    Name[1];

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

所以有:

0:001> da 00018314+0x1230000+2

01248316  "CreateThread"

0:001> da 000186e4+0x1230000+2

012486e6  "GetCurrentProcess"

.cxr、.frame、dt(查找设置设备上下文、切换局部上下文、查找结构体)

使用一个debug程序:

.cxr

设备上下文的常用含义是一组寄存器,表示处理器在某个特定时刻的状态,因此也称为寄存器上下文,当生成异常时,寄存器上下文可以通过异常分发器的保码保存到栈上,并且在引发异常时可以用来恢复寄存器的值。

如何找到这个上下文?最简单的方式是从异常分发过程中使用的各种函数的参数中获取上下文,或者通过栈中搜索上下文,无论通过何种方式找到寄存器上下文,都可以通过.cxr <context address>来将其设置为当前的上下文

KiUserExceptionDispatcher( PEXCEPTION_RECORD pExcptRec, CONTEXT * pContext )

第二个参数就是上下文件地址了

0:005> dt CONTEXT

02sample!CONTEXT

+0x000 ContextFlags     : Uint4B

+0x004 Dr0              : Uint4B

+0x008 Dr1              : Uint4B

+0x00c Dr2              : Uint4B

+0x010 Dr3              : Uint4B

+0x014 Dr6              : Uint4B

第一个参数ContextFlags的取值是定义的长量,一盘为0x0001003f,表示常量CONTEXT_ALL,所以我们在栈中进行搜索某个内存块的含义时,这个标志是非常有用的,我们可以通过这个标志来找到上下文,并将其设置为当前线程的上下文,从而了解在异常发生之前处理器的状态是什么

0:000> *在地址空间的前256M中搜索完整的上下文

0:000> s -d 0 L10000000/4 0001003f

0009fd24  0001003f 00000000 00000000 00000000  ?...............

001c3b08  0001003f 0001004d 00000000 00000400  ?...M...........

005efd24  0001003f 00000000 00000000 00000000  ?...............

007efd24  0001003f 00000000 00000000 00000000  ?...............

0:000> *将找到的上下文设置到这个地址上

0:000> .cxr 0009fd24

eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000

eip=7746e8ac esp=000dfd34 ebp=000dfe50 iopl=0         nv up ei pl nz na po nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202

ntdll!NtDeviceIoControlFile+0xc:

7746e8ac c22800          ret     28h

0:000> k

*** Stack trace for last set context - .thread/.cxr resets it

# ChildEBP RetAddr

WARNING: Stack unwind information not available. Following frames may be wrong.

00 000dfe50 74a3d108 ntdll!NtDeviceIoControlFile+0xc

01 000dfe78 74aa16b4 KERNELBASE!GetConsoleOutputCP+0x58

02 000dfea8 765fa582 KERNELBASE!SetConsoleMode+0x24

03 000dfee4 765fa4a8 msvcrt!getwch+0x112

04 000dff18 01001d63 msvcrt!getwch+0x38

05 000dff30 01001c7b 02sample!AppInfo::Loop+0xb3 [c:\awd\common\menu.h @ 47]

06 000dff3c 01002038 02sample!wmain+0x1b [c:\awd\chapter2\sample.cpp @ 228]

07 000dff80 73fd8674 02sample!__wmainCRTStartup+0x102 [d:\vistartm\base\crts\crtw32\dllstuff\crtexe.c @ 711]

08 000dff94 77464b47 KERNEL32!BaseThreadInitThunk+0x24

09 000dffdc 77464b17 ntdll!RtlGetAppContainerNamedObjectPath+0x137

0a 000dffec 00000000 ntdll!RtlGetAppContainerNamedObjectPath+0x107

0:000> *恢复到默认的上下文

0:000> .cxr

Resetting default scope

0:000> k

# ChildEBP RetAddr

00 000dff18 01001d63 02sample!RaiseAV+0x10 [c:\awd\chapter2\sample.cpp @ 55]

01 000dff30 01001c7b 02sample!AppInfo::Loop+0xb3 [c:\awd\common\menu.h @ 47]

02 000dff3c 01002038 02sample!wmain+0x1b [c:\awd\chapter2\sample.cpp @ 228]

03 000dff80 73fd8674 02sample!__wmainCRTStartup+0x102 [d:\vistartm\base\crts\crtw32\dllstuff\crtexe.c @ 711]

WARNING: Stack unwind information not available. Following frames may be wrong.

04 000dff94 77464b47 KERNEL32!BaseThreadInitThunk+0x24

05 000dffdc 77464b17 ntdll!RtlGetAppContainerNamedObjectPath+0x137

06 000dffec 00000000 ntdll!RtlGetAppContainerNamedObjectPath+0x107

.frame

.frame命令指定使用哪个局部上下文(作用域)来解析局部变量,或者显示当前的局部上下文

所谓局部上下文,是指局部变量所基于的语境,局部变量是指定义在函数内部的变量,这些变量的含义与当前的执行位置密切相关,在调试时,调试器默认显示的是当前函数(程序指针)所对应的局部上下文,因为当前函数和局部变量都是与栈帧密切相关的,所以windbg通常使用栈帧号来表示局变上下文

帧序号(frame number)是堆栈帧在堆栈回溯中的位置。可以使用k (Display Stack Backtrace)命令或者Calls 窗口查看堆栈回溯。第一行 (当前帧) 的帧序号是0。后面的行分别是1、2、3等等。

0:000> kn

# ChildEBP RetAddr

00 0012f78c 7c92daea ntdll!KiFastSystemCallRet

01 0012f790 7c932298 ntdll!ZwRequestWaitReplyPort+0xc

02 0012f7b0 7c872a51 ntdll!CsrClientCallServer+0x8c

03 0012f8ac 7c872b98 kernel32!ReadConsoleInternal+0x1be

04 0012f934 7c8018b7 kernel32!ReadConsoleA+0x3b

05 0012f98c 102c207c kernel32!ReadFile+0x64

06 0012fa20 102c19c9 MSVCR90D!_read_nolock+0x62c [f:\dd\vctools\crt_bld\self_x86\crt\src\read.c @ 233]

07 0012fa70 10253e43 MSVCR90D!_read+0x219 [f:\dd\vctools\crt_bld\self_x86\crt\src\read.c @ 93]

08 0012fa98 102523e8 MSVCR90D!_filbuf+0x113 [f:\dd\vctools\crt_bld\self_x86\crt\src\_filbuf.c @ 136]

09 0012faf0 10252440 MSVCR90D!getc+0x208 [f:\dd\vctools\crt_bld\self_x86\crt\src\fgetc.c @ 76]

0a 0012fafc 1025245a MSVCR90D!_fgetchar+0x10 [f:\dd\vctools\crt_bld\self_x86\crt\src\fgetchar.c @ 37]

0b 0012fb04 0041160b MSVCR90D!getchar+0xa [f:\dd\vctools\crt_bld\self_x86\crt\src\fgetchar.c @ 47]

0c 0012fbe4 004114b2 test2!MyCls::hold+0x2b [d:\project1\test2\test2\test2.cpp @ 28]

0d 0012fcec 0041167a test2!foo1+0xa2 [d:\project1\test2\test2\test2.cpp @ 39]

0e 0012fdc0 004116ea test2!foo2+0x3a [d:\project1\test2\test2\test2.cpp @ 45]

0f 0012fe94 00411743 test2!foo3+0x3a [d:\project1\test2\test2\test2.cpp @ 51]

10 0012ff68 00411ce8 test2!main+0x23 [d:\project1\test2\test2\test2.cpp @ 56]

11 0012ffb8 00411b2f test2!__tmainCRTStartup+0x1a8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 586]

12 0012ffc0 7c817077 test2!mainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 403]

13 0012fff0 00000000 kernel32!BaseProcessStart+0x23

0:000> .frame d

0d 0012fcec 0041167a test2!foo1+0xa2 [d:\project1\test2\test2\test2.cpp @ 39]

0:000> x

0012fce4 pcls = 0x00392a28

0012fcd8 rawptr = 0x00392a28

.frame 帧号,可以把局部上下文切换到指定的栈帧

之后跟x,可以显示当前这个函数里面的局部变量

对应d帧的代码:

void foo1()

{

MyCls *pcls=new MyCls();

void *rawptr=pcls;

pcls->set("abcd");

pcls->output();

pcls->hold();

};

x显示的是pcls和rawptr

dt

dt命令显示局部变量、全局变量或数据类型的信息。它也可以仅显示数据类型。即结构和联合(union)的信息

dt最方便处是查找结构体,查找结构体一定要使用dt,不要使用x

0:000:x86> dt *!*IMAGE_DOS*

test1!IMAGE_DOS_HEADER

test1!PIMAGE_DOS_HEADER

test1!_IMAGE_DOS_HEADER

ntdll!_IMAGE_DOS_HEADER

ntdll32!_IMAGE_DOS_HEADER

MSVCR90D!IMAGE_DOS_HEADER

MSVCR90D!PIMAGE_DOS_HEADER

MSVCR90D!_IMAGE_DOS_HEADER

dt -b this可以打印this指针,只在类中起作用

0:000:x86> dt -b this

Local var @ 0x1cfd00 Type CLook*

0x00683488

+0x000 m_i              : 0n1524615000

+0x004 m_j              : 37 'Unknown format character

如果只想显示某个字段,可以用-ny 加上搜索选项,如:

0:000> dt -v ntdll!_PEB -ny BeingDebugged @$peb

struct _PEB, 91 elements, 0x248 bytes

+0x002 BeingDebugged : 0x1 ''

-v是详细输出。这会输出结构的总大小和字段数量这样的附加信息

也可以直接这样看大小:

0:000:x86> ?? sizeof(IMAGE_DOS_HEADER)

unsigned int 0x40

也可以用-y 来连接通配符,如:

0:000> dt -v ntdll!_PEB -y B* @$peb

struct _PEB, 91 elements, 0x248 bytes

+0x002 BeingDebugged : 0x1 ''

+0x003 BitField : 0x8 ''

0:000> dt pcls

Local var @ 0x12fce4 Type MyCls*

0x00392a28

+0x000 str              : 0x00415800  "abcd"

+0x004 inobj            : innner

0:000> dt rawptr

Local var @ 0x12fcd8 Type void*

0x00392a28

0:000> dt -b-r pcls

Local var @ 0x12fce4 Type MyCls*

0x00392a28

+0x000 str              : 0x00415800  "abcd"

+0x004 inobj            : innner

+0x000 arr              :  "abcd"

[00] 97 'a'

[01] 98 'b'

[02] 99 'c'

[03] 100 'd'

[04] 0 ''

[05] 0 ''

[06] 0 ''

[07] 0 ''

[08] 0 ''

[09] 0 ''

.dump(转储文件)

.dump 命令创建一个用户模式或内核模式崩溃转储文件。分析工具:https://msdn.microsoft.com/en-us/library/windows/desktop/ee416349(v=vs.85).aspx#writing_a_minidump

程序崩溃(crash)的时候, 为了以后能够调试分析问题, 可以使用WinDBG要把当时程序内存空间数据都保存下来,生成的文件称为dump 文件。 步骤:

1) 打开WinDBG并将之Attach 到crash的程序进程

2) 输入产生dump 文件的命令

直接用.dump -?可以看到它的简单说明:

0:000> .dump -?

Usage: .dump [options] filename

Options are:

/a - Create dumps for all processes (requires -u)

/b[a] - Package dump in a CAB and delete dump

/c <comment> - Add a comment (not supported in all formats)

/j <addr> - Provide a JIT_DEBUG_INFO address

/f - Create a legacy style full dump

/m[acdfFhiprRtuw] - Create a minidump (default)

/o - Overwrite any existing file

/u - Append unique identifier to dump name

/o :覆盖具有相同名字的dump文件。如果没有使用该选项又存在一个相同名字的文件,则dump文件不会被写入:比如我的C盘原有一个myapp.dmp文件:

0:000> .dump c:/myapp.dmp

Unable to create file 'c:/myapp.dmp' - Win32 error 0n80

"文件存在。"

0:000> .dump /o c:/myapp.dmp

Creating c:/myapp.dmp - mini user dump

Dump successfully written

/f (用户模式:) 创建一个完整用户模式dump,这里要注意不要字面理解,

完整用户模式dump是基本的用户模式dump文件。这种dump文件包含进程的完整内存空间、程序本身的可执行映像、句柄表和其他对调试器有用的信息

注意 和名字无关,最大的"minidump"文件实际上可以提供比完整用户模式dump更多的信息。例如,.dump /mf 或.dump /ma将创建比.dump /f更大更完整的文件。

用户模式下,使用.dump /m[MiniOptions] 是最好的选择。通过这个开关创建的dump文件可以很小也可以很大。通过指定合适的MiniOptions 可以控制究竟需要包含哪些信息。

0:000> .dump /o/f c:/myapp.dmp

*****************************************************************************

* .dump /ma is the recommend method of creating a complete memory dump      *

* of a user mode process.                                                   *

*****************************************************************************

Creating c:/myapp.dmp - user full dump

Dump successfully written

我们看到了,系统给出了提示:.dump /ma是创建完整dump的推荐方式(用户模式下)

/m[MiniOptions] 创建一个小内存dump(内核模式)或者minidump(用户模式)。如果没有指定 /f 和/m ,/m 是默认选项。用户模式下,/m 后面可以跟附加的MiniOptions 用来指定dump文件中包含的数据。如果没有使用MiniOptions ,dump文件包含模块、线程和调用堆栈信息,但是没有其他附加信息

MiniOption     作用

a      创建一个包含所有附加选项的minidump。/ma选项相当于/mfFhut —它会在minidump中添加完整的内存数据、句柄数据、已卸载模块信息、基本内存信息和线程时间信息。

f       在minidump中包含完整内存数据。目标程序拥有的所有 可访问的已交付的页面(committed pages)都会包含进去。

F      在minidump中添加所有基本内存信息。这会将一个流加入到包含完整基本内存信息的minidump中,而不单是可使用的内存。这样可以使得调试器能够重建minidump生成时进程的完整虚拟内存布局。

h      在minidump中包含和目标进程相关的句柄信息。

u      在minidump中包含已卸载模块信息。仅在Windows Server 2003和之后版本的Windows中可用。

t      在minidump中包含附加的线程信息。包括可以在调试minidump时使用!runaway扩展命令或.ttime (Display Thread Times)命令进行显示的线程时间。

i       在minidump中包含次级内存(secondary memory)。次级内存是由堆栈中的指针或备份存储(backing store)中引用到的任何内存,加上该地址周围的一小段区域。

p      在minidump中包含进程环境块(PEB)和线程环境块(TEB)。这在想访问程序的进程和线程相关的Windows系统信息时很有用。

w     将所有已交付的可读写的私有页面包含进minidump。

d      在minidump中包含可执行映像中所有可读写的数据段。

c      加入映像中的代码段。

r      从minidump中去掉对重建调用堆栈无用的堆栈和存储内存部分。局部变量和其他数据类型值也被删除。这个选项不会使得minidump变小(因为这些内存节仅仅是变成0),但是当想保护其他程序中的机密信息时有用。

R     在minidump中去掉完整的模块路径。仅包含模块名。如果想保护用户的目录结构时该选项有用。

选项(1): /m

命令行示例:.dump /m C:/dumps/myapp.dmp

注解: 缺省选项,生成标准的minidump, 转储文件通常较小,便于在网络上通过邮件或其他方式传输。 这种文件的信息量较少,只包含系统信息、加载的模块(DLL)信息、 进程信息和线程信息。

选项(2): /ma

命令行示例:.dump /ma C:/dumps/myapp.dmp

注解: 带有尽量多选项的minidump(包括完整的内存内容、句柄、未加载的模块,等等),文件很大,但如果条件允许(本机调试,局域网环境), 推荐使用这中dump。

选项(3):/mFhutwd

命令行示例:.dump /mFhutwd C:/dumps/myapp.dmp

注解:带有数据段、非共享的读/写内存页和其他有用的信息的minidump。包含了通过minidump能够得到的最多的信息。是一种折中方案。

Fhutwd按字母对应上面的MiniOptions表示

//-xp自动生成dump-----------------------------------------------------------------------------------------------------------------------------------------------------------------

那怎么自动生成dump文件呢,比如对方的电脑没有windbg,这里用到一个window XP系统自带工具,Dr.Watson

运行方式很简单:

直接run-输入drwtsn32 -i就可以了,会提示这样的:

这个命令真难记,实话,记华生医生吧,福尔摩斯中的

如果有程序崩溃,会自动生成dump,这时再输入drwtsn32就会运行这个程序:

找到对应路径的DMP文件就行了,一般放在如下路径:

C:\Documents and Settings\All Users\Application Data\Microsoft\Dr Watson

//-win7自动生成dump-----------------------------------------------------------------------------------------------------------------------------------------------------------------

win7下需打开regedit--> 找到:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting]

在它下面加一项LocalDumps,并做如下项配置:

Value       描述        Type        默认值

DumpFolder   文件保存路径        REG_EXPAND_SZ    %LOCALAPPDATA%CrashDumps

DumpCount    dump文件的最大数目          REG_DWORD 10

DumpType      指定生成的dump类型:

0:Custom dump

1:Mini dump

2:Full dump    REG_DWORD 1

CustomDumpFlags          仅在DumpType为0时使用

为MINIDUMP_TYPE的组合          REG_DWORD

MiniDumpWithDataSegs|

MiniDumpWithUnloadedModules|

MiniDumpWithProcessThreadData

可以写成.bat:

@echo off

echo 设置Dump...

reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps"

reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpFolder /t REG_EXPAND_SZ /d "C:\MyDump" /f

reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpType /t REG_DWORD /d 2 /f

reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpCount /t REG_DWORD /d 10 /f

echo Dump已经设置

pause

@echo on

@echo off

echo 正在取消设置Dump...

reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /f

echo Dump已经取消设置

pause

@echo on

LocalDumps是全局的,如果想针对指定进程单独设置,如test1.exe,则在/LocalDumps下新建子项test1.exe,同时在test1目录下复制上表的选项,这样,系统就会先读全局设置,再读子项test1.exe的设置

.lastevent、!analyze(dump分析、异常错误码查询)

.lastevent

.lastevent 命令显示最近一次发生的异常或事件。

0:000> .lastevent

Last event: 1534.f4c: Break instruction exception - code 80000003 (first chance)

debugger time: Tue May 22 10:47:26.962 2012 (GMT+8)

0:000> ~

.  0  Id: 1534.e8c Suspend: 1 Teb: 7ffdf000 Unfrozen

1  Id: 1534.1338 Suspend: 1 Teb: 7ffde000 Unfrozen

#  2  Id: 1534.f4c Suspend: 1 Teb: 7ffdd000 Unfrozen

我们可以看出,当前为2号线程发生异常,线程0前面的点号(.)表示它是当前线程。线程2前面的数字号(#)表示它是产生异常或调试器附加到进程时活动的线程。如果使用CTRL+C、 CTRL+BREAK或Debug | Break中断到调试器,总是会产生一个 0x80000003异常代码。

0:000> .lastevent

Last event: 1664.4184: Access violation - code c0000005 (first/second chance not available)

debugger time: Thu Aug 13 11:20:44.037 2015 (GMT+8)

异常错误码查询

异常

描述

EXCEPTION_ACCESS_VIOLATION

0xC0000005

程序企图读写一个不可访问的地址时引发的异常。例如企图读取0地址处的内存。

EXCEPTION_ARRAY_BOUNDS_EXCEEDED

0xC000008C

数组访问越界时引发的异常。

EXCEPTION_BREAKPOINT

0x80000003

触发断点时引发的异常。

EXCEPTION_DATATYPE_MISALIGNMENT

0x80000002

程序读取一个未经对齐的数据时引发的异常。

EXCEPTION_FLT_DENORMAL_OPERAND

0xC000008D

如果浮点数操作的操作数是非正常的,则引发该异常。所谓非正常,即它的值太小以至于不能用标准格式表示出来。

EXCEPTION_FLT_DIVIDE_BY_ZERO

0xC000008E

浮点数除法的除数是0时引发该异常。

EXCEPTION_FLT_INEXACT_RESULT

0xC000008F

浮点数操作的结果不能精确表示成小数时引发该异常。

EXCEPTION_FLT_INVALID_OPERATION

0xC0000090

该异常表示不包括在这个表内的其它浮点数异常。

EXCEPTION_FLT_OVERFLOW

0xC0000091

浮点数的指数超过所能表示的最大值时引发该异常。

EXCEPTION_FLT_STACK_CHECK

0xC0000092

进行浮点数运算时栈发生溢出或下溢时引发该异常。

EXCEPTION_FLT_UNDERFLOW

0xC0000093

浮点数的指数小于所能表示的最小值时引发该异常。

EXCEPTION_ILLEGAL_INSTRUCTION

0xC000001D

程序企图执行一个无效的指令时引发该异常。

EXCEPTION_IN_PAGE_ERROR

0xC0000006

程序要访问的内存页不在物理内存中时引发的异常。

EXCEPTION_INT_DIVIDE_BY_ZERO

0xC0000094

整数除法的除数是0时引发该异常。

EXCEPTION_INT_OVERFLOW

0xC0000095

整数操作的结果溢出时引发该异常。

EXCEPTION_INVALID_DISPOSITION

0xC0000026

异常处理器返回一个无效的处理的时引发该异常。

EXCEPTION_NONCONTINUABLE_EXCEPTION

0xC0000025

发生一个不可继续执行的异常时,如果程序继续执行,则会引发该异常。

EXCEPTION_PRIV_INSTRUCTION

0xC0000096

程序企图执行一条当前CPU模式不允许的指令时引发该异常。

EXCEPTION_SINGLE_STEP

0x80000004

标志寄存器的TF位为1时,每执行一条指令就会引发该异常。主要用于单步调试。

EXCEPTION_STACK_OVERFLOW

0xC00000FD

栈溢出时引发该异常。

!analyze

!analyze扩展显示当前异常或bug check的信息。一般使用!analyze -v

分析参考:https://msdn.microsoft.com/en-us/library/windows/hardware/ff560201(v=vs.85).aspx

如下述为一个对NULL指针赋值生成的dump,自动生成dump可以参考16.windbg-.dump(转储文件)

0:000> !analyze -v

*******************************************************************************

*                                                                             *

*                        Exception Analysis                                   *

*                                                                             *

*******************************************************************************

*************************************************************************

***                                                                   ***

***                                                                   ***

***    Your debugger is not using the correct symbols                 ***

***                                                                   ***

***    In order for this command to work properly, your symbol path   ***

***    must point to .pdb files that have full type information.      ***

***                                                                   ***

***    Certain .pdb files (such as the public OS symbols) do not      ***

***    contain the required information.  Contact the group that      ***

***    provided you with these symbols if you need this command to    ***

***    work.                                                          ***

***                                                                   ***

***    Type referenced: kernel32!pNlsUserInfo                         ***

***                                                                   ***

*************************************************************************

*************************************************************************

***                                                                   ***

***                                                                   ***

***    Your debugger is not using the correct symbols                 ***

***                                                                   ***

***    In order for this command to work properly, your symbol path   ***

***    must point to .pdb files that have full type information.      ***

***                                                                   ***

***    Certain .pdb files (such as the public OS symbols) do not      ***

***    contain the required information.  Contact the group that      ***

***    provided you with these symbols if you need this command to    ***

***    work.                                                          ***

***                                                                   ***

***    Type referenced: kernel32!pNlsUserInfo                         ***

***                                                                   ***

*************************************************************************

首先被提示,这是没有pdb文件 的

FAULTING_IP:

test2+1002

01211002 8900            mov     dword ptr [eax],eax

FAULTING_IP:出现错误时的指令,这句明显就是把eax赋到[eax]中

EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)

EXCEPTION_RECORD:崩溃时的异常记录,可以使用.exr查看

ExceptionAddress: 01211002 (test2+0x00001002)

ExceptionCode: c0000005 (Access violation)

ExceptionFlags: 00000000

NumberParameters: 2

Parameter[0]: 00000001

Parameter[1]: 00000000

Attempt to write to address 00000000

这个就很详细了,尝试向0地址写入,它其实就是调试中用到的结构体:可以直接通过MSDN查到它的定义

typedef struct _EXCEPTION_RECORD {

DWORD    ExceptionCode;

DWORD ExceptionFlags;

struct _EXCEPTION_RECORD *ExceptionRecord;

PVOID ExceptionAddress;

DWORD NumberParameters;

ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];

} EXCEPTION_RECORD;

注意的是:上面的NumberParameter:2表示ExceptionInformation有两个附加异常码,1和0,对于多数异常来说,这些附加异常码是没有什么用的,MSDN:For most exception codes, the array elements are undefined.只有以下两个被定义了

当ExceptionCode为EXCEPTION_ACCESS_VIOLATION时,ExceptionInformation[0]=0表示线程试图读取不可访问的数据ExceptionInformation[0]=1

表示线程试图写入不可访问的地址,很明显,这里表示第二种,具体参考MSDN

DEFAULT_BUCKET_ID:  NULL_POINTER_READ

DEFAULT_BUCKET_ID:表示了本次错误属于哪种通用失败

BUGCHECK_STR:  APPLICATION_FAULT_NULL_POINTER_READ_NULL_POINTER_WRITE

The BUGCHECK_STR field shows the exception code. The name is a misnomer—the term bug check actually signifies a kernel-mode crash. In user-mode debugging, the exception code will be displayed—in this case, 0x80000003.

LAST_CONTROL_TRANSFER:  from 76b6337a to 01211002

堆栈的最后调用:

The LAST_CONTROL_TRANSFER field shows the last call on the stack. In this case, the code at address0x76b6337acalled a function at 0x1211002. You can use these addresses with the ln (List Nearest Symbols) command to determine what modules and functions these addresses reside in.

STACK_TEXT:

WARNING: Stack unwind information not available. Following frames may be wrong.

0020fbb8 76b6337a 7efde000 0020fc04 77e092b2 test2+0x1002

0020fbc4 77e092b2 7efde000 596d1564 00000000 kernel32!BaseThreadInitThunk+0xe

0020fc04 77e09285 012112b6 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70

0020fc1c 00000000 012112b6 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b

堆信息

STACK_COMMAND:  ~0s; .ecxr ; kb

打印堆栈的命令

SYMBOL_NAME:  test2+1002

对应的符号名称

IMAGE_NAME:  test2.exe

对应的模块名称

3.符号文件简介:

符号文件对于调试程序是相当重要的,通常符号文件中包含以下内容

全局变量的名字和地址

函数名,地址及其原型

帧指针优化数据

局部变量的名字和地址

源文件路径以及每个符号的行号

变量,结构等的类型信息

ln、伪寄存器(Pseudo-Register Syntax)、自定义伪寄存器

ln

ln 命令显示给定地址处的或者最近的符号。

ln表示list near,ln命令将尽可能地给出与特定地址相关的符号,如果没有符号能够精确地与这个地址匹配,那么调试器将通过指针算法对靠近这地址的符号进行运逄,

并返回运算结果符号

0:000> ln 01012475

(01012475)   calc!WinMainCRTStartup   |  (0101263c)   calc!__CxxFrameHandler

Exact matches:

calc!WinMainCRTStartup = <no type information>

0:000> ln 01012475+1

(01012475)   calc!WinMainCRTStartup+0x1   |  (0101263c)   calc!__CxxFrameHandler

我们发现,第一个显示为Exact matches:表示精确匹配了一个地址,如果不是精确匹配,我们要小心,是否模块进行了优化,在优化后,一个函数,可能被拆分为多个部分

分别位于不同的地址,经过优化的映像可以通过lm查看:会有perf标识

当你在查看某部分数据,却不知道这部分数据所表示的内容时,这个命名能带来极大的帮助

伪寄存器

伪寄存器都是通过r来操作的

输入Pseudo-Register Syntax查询

对于那些偶尔使用调试器的用户是很难记得所有平台的指令指针寄存器名字(或其他的名字),为了克服这个问题,调试器的开发团队引入了各种伪寄存器,由调试器把这些伪寄存器对应到不同的硬件架构上,形式为$name,与标准的寄存器一样,如果要在表达式中使用伪寄存器,那么必须使用转义字符@

$exentry

当前进程的入口地址

0:002> r $exentry

$exentry=01012475

一般可以直接在这下断点,

这个就对应PE文件中的ImageBase+AddressOfEntryPoint(_IMAGE_OPTIONAL_HEADER)

$ip

指令指针寄存器

在X86架构上,$ip = eip

在x64架构上,$ip = rip

在Itanium架构上, $ip = iip

x86/x64/ia-64的区别

0:000> r @$ip

$ip=7c92120e

0:000> r eip

eip=7c92120e

注意到下面显示的分别是$ip,eip,虽然它们在X86下是同一个东东.

$ra

当前函数的返回地址

0:000> r $ra

$ra=7c95e612

0:000> kb

ChildEBP RetAddr  Args to Child

0012fb1c 7c95e612 7ffdd000 7ffde000 00000000 ntdll!DbgBreakPoint

0012fc94 7c94108f 0012fd30 7c920000 0012fce0 ntdll!LdrpInitializeProcess+0xffa

0012fd1c 7c92e437 0012fd30 7c920000 00000000 ntdll!_LdrpInitialize+0x183

00000000 00000000 00000000 00000000 00000000 ntdll!KiUserApcDispatcher+0x7

其实也是对应当前线程,如果要看所有线程的当前函数的返回地址:

0:000> ~* r $ra

$ra=77d191be

$ra=7c92df2c

$ra=7c92df3c

$ra=7c970010

$retreg

主要的值寄存器,在函数调用返回后,函数的结果将放在这个寄存器中,根据处理器架构的不同,$retreg的值分别为

在x86架构上,$retreg = eax

在x64架构上,$retreg = rax

在Itanium架构上,$retreg = ret0

0:000> r $retreg

$retreg=00251eb4

0:000> r eax

eax=00251eb4

$csp

当前的栈指针,根据处理器架构的不同,$csp的值分别为

在x86架构上,$csp = esp

在x64架构上,$csp = rsp

在Itanium架构上,$csp = bsp

0:000> r $csp

$csp=0012fb24

0:000> r esp

esp=0012fb24

$tpid

当前进程的标识(PID)

0:000> r $tpid

$tpid=000013f4

$tid

当前线程的标识(TID0

0:000> r $tid

$tid=000014a0

伪寄存器        描述

$ea 最后一条被执行指令的有效地址(effective address)。如果这条指令没有一个有效地址,将显示"Bad register error"。如果这条指令有两个有效地址,则显示第一个地址。

$ea2        最后一条被执行指令的第二个有效地址,如果这条指令没有两个有效地址,将显示"Bad register error"。

$exp        最后一个被求值的表达式。

$ra 当前堆栈的返回地址。

这个在执行命令中特别有用。例如,g @$ra 将一直执行到返回地址处(虽然,对于“步出(stepping out)”当前函数gu (Go Up)是一个更加准备有效的方法)。

$ip 指令指针寄存器:

x86 处理器:和 eip 相同

Itanium 处理器:涉及 iip(请看表后的注解)

x64处理器:和rip相同

$eventip          当前事件发生时的指令指针,通常和 $ip 匹配,除非你切换了线程或者手动改变了指令指针的值。

$previp   前一个事件发生时的指令指针。(中断进入调试器算做一个事件。)

$relip       和当前事件相关的指令指针,当你正在跟踪分支指令时,这个是分支来源指针。

$scopeip          当前局部上下文(也称为作用域)的指令指针。

$exentry          当前进程的第一个可执行的入口点地址。

$retreg    主要的返回值寄存器:

x86 处理器:和 eax 相同

Itanium 处理器:和 ret0 相同

x64 处理器:和 rax 相同

$retreg64        主要的返回值寄存器,以64位格式。

x86处理器:和edx:eax 相同

$csp         当前调用堆栈指针,是一个通常表示调用堆栈深度的寄存器。

x86 处理器:和 esp 相同

Itanium 处理器:和 bsp 相同

x64 处理器:和 rsp 相同

$p   最后一条 d* (Display Memory)命令打印的值。

$proc       当前进程的地址(换句话说,就是 EPROCESS 块的地址)。

$thread 当前线程的地址(换句话说,就是 ETHREAD 块的地址)。

$peb        当前进程的进程环境块(PEB)的地址。

$teb         当前线程的线程环境块(TEB)的地址。

$tpid       当前线程所在进程的进程 ID(PID)。

$tid          当前线程的线程 ID。

$bpNumber    对应断点的地址。例如,$bp3(或者 $bp03)引用断点 ID 为 3 的断点。Number 总是一个十进制数,如果没有哪个断点的 ID 为Number,则$bpNumber 求值为 0,详细请看使用断点。

$frame    当前帧索引,这个和.frame (Set Local Context) 命令常用的 frame number 相同。

$dbgtime         当前时间,根据调试器运行的计算机。

$callret   被.call (Call Function)命令调用的或者被.fnret /s命令使用的最后函数得到的返回值,$callret 的数据类型就是返回值的数据类型。

$lastclrex         仅托管代码调试: 最近一次遇到的公共语言运行时(CLR)异常对象的地址。

$ptrsize   指针大小。在内核模式下,指目标计算机上的指针大小。

$pagesize        一个内存页的大小(也就是占用的字节数目),在内核模式下,指目标计算机上的页大小。

自定义伪寄存器

有二十个自定义伪寄存器:$t0, $t1, ..., $t19。它们是可以通过调试器读写的变量。能用来保存任意整数值。做为循环变量时非常有用

0:000> x

0026fb80 argc = 1

0026fb84 argv = 0x002d33a0

0026fb70 p = 0x002d1b90

0:000> r $t0

$t0=00000000

0:000> r $t0 = poi(p)

0:000> r $t0

$t0=002d1b90

除非 r 命令使用了 ? 开关选项,否则一个伪寄存器总是具有整数类型。如果用到了 ? 选项,则伪寄存器可以获得赋给它的任意类型,例如,下面的命令把 UNICODE_STRING** 类型和 0x0012FFBC 值赋给了 $t15(译注:这里好像是 UNICODE_STRING 类型吧!另外,如果 UNICODE_STRING 解析不了,可以用 _UNICODE_STRING)。

0:000> r? $t15 = * (UNICODE_STRING*) 0x12ffbc

下面可以看出r和r?的区别,比如我的代码有个char* g_char = "I am string";

00bc7004 test1!g_char = 0x00bc573c "I am string"

0:000> r? $t0 = test1!g_char

0:000> r $t0

$t0=00bc573c

0:000> r $t1 = test1!g_char

0:000> r $t1

$t1=00bc7004

可以看到$t0保存的是真实的test1!g_char的指针地址,而$t1只是保存了它的对象地址

所以:

0:000> da $t0

00bc573c  "I am string"

0:000> da $t1

00bc7004  "<W."

0:000> da test1!g_char

00bc7004  "<W."

0:000> da poi(test1!g_char)

00bc573c  "I am string"

dv(显示局部变量)

dv(display Local variable)

dv 命令显示当前作用域的所有局部变量的名字和值。

0:000> x Simple1Demo!CSimple1DemoApp::InitInstance

00640080 Simple1Demo!CSimple1DemoApp::InitInstance (void)

0:000> bp 00640080

0:000> bl

0 e 00640080     0001 (0001)  0:**** Simple1Demo!CSimple1DemoApp::InitInstance

0:000> g

ModLoad: 62c20000 62c29000   C:\WINDOWS\system32\LPK.DLL

ModLoad: 73fa0000 7400b000   C:\WINDOWS\system32\USP10.dll

ModLoad: 5adc0000 5adf7000   C:\WINDOWS\system32\uxtheme.dll

Breakpoint 0 hit

eax=0062c312 ebx=7ffdd000 ecx=00b24d28 edx=00a765dc esi=0342f76e edi=0342f6f2

eip=00640080 esp=0012fedc ebp=0012fefc iopl=0         nv up ei pl nz na po nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202

Simple1Demo!CSimple1DemoApp::InitInstance:

00640080 55              push    ebp

0:000> dv

this = 0x0012fecc

dlg = class CSimple1DemoDlg

InitCtrls = struct tagINITCOMMONCONTROLSEX

nResponse = -16843010

我的代码是这样的:

BOOL CSimple1DemoApp::InitInstance()

{

INITCOMMONCONTROLSEX InitCtrls;

InitCtrls.dwSize = sizeof(InitCtrls);

InitCtrls.dwICC = ICC_WIN95_CLASSES;

InitCommonControlsEx(&InitCtrls);

CWinAppEx::InitInstance();

AfxEnableControlContainer();

SetRegistryKey(_T("应用程序向导生成的本地应用程序"));

m_hDll = LoadLibrary(TEXT("SkinHgy.dll"));

if (m_hDll)

{

#ifdef UNICODE

SkinLoadFileSkin = (fun_SkinLoadFileSkin)GetProcAddress(m_hDll, "SkinLoadFileSkinW");

#else

SkinLoadFileSkin = (fun_SkinLoadFileSkin)GetProcAddress(m_hDll, "SkinLoadFileSkinA");

#endif

if (SkinLoadFileSkin)

{

TCHAR wcPath[MAX_PATH] = {0};

::GetModuleFileName(NULL, wcPath, MAX_PATH);

CString szSkinPath = wcPath;

szSkinPath = szSkinPath.Left(szSkinPath.ReverseFind('\\'));

szSkinPath += TEXT("\\skin\\skin.xml");

SkinLoadFileSkin(szSkinPath);

}

}

m_bDragFull = IsDragFullWindows();

if (m_bDragFull)

{

::SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, FALSE, NULL, 0);

}

CSimple1DemoDlg dlg;

m_pMainWnd = &dlg;

INT_PTR nResponse = dlg.DoModal();

if (nResponse == IDOK)

{

}

else if (nResponse == IDCANCEL)

{

}

return FALSE;

}

我们可以看到,的确显示的四个局部变量,当然,出现的顺序可能并不一致,

/i 使得输出中显示变量的类型:局部、全局、参数、函数或未知:

0:000> dv /i

prv local             this = 0x0012fecc

prv local              dlg = class CSimple1DemoDlg

prv local        InitCtrls = struct tagINITCOMMONCONTROLSEX

prv local        nResponse = -16843010

数据结构和陌生的数据类型不会完整显示,而只显示他们的类型名。要显示整个结构或结构中的特定成员,使用dt (Display Type)命令。

0:000> dt InitCtrls

Local var @ 0x12feb0 Type tagINITCOMMONCONTROLSEX

+0x000 dwSize           : 0xb1c238

+0x004 dwICC            : 4

贴一下tagINITCOMMONCONTROLSEX的定义:

typedef struct tagINITCOMMONCONTROLSEX {

DWORD dwSize;             // size of this structure

DWORD dwICC;              // flags indicating which classes to be initialized

} INITCOMMONCONTROLSEX, *LPINITCOMMONCONTROLSEX;

我们继续F10单步调试,到了系统函数COMCTL32!InitCommonControlsEx中,F11进入,这时再dv

0:000> t

eax=0012feb0 ebx=7ffdd000 ecx=00b24d28 edx=00a765dc esi=0012f7c0 edi=0012fecc

eip=77184088 esp=0012f7b8 ebp=0012fed8 iopl=0         nv up ei pl nz na po nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202

COMCTL32!InitCommonControlsEx:

77184088 8bff            mov     edi,edi

0:000> dv

Unable to enumerate locals, HRESULT 0x80004005

Private symbols (symbols.pri) are required for locals.

Type ".hh dbgerr005" for details.

显示不了local,这是因为dv命令是显示当前函数的局部变量,而当前函数是系统的函数(比如ntdll!DbgBreakPoint),那么因为没有系统模块的私有符号,所以会出错。

建议你切换到0号线程(~0s),然后使用单步或者gu命令执行到CSimple1Demo模块中的函数,然后再用dv命令

我们dt this看看:

0:000> dt this

Local var @ 0x12fec0 Type CSimple1DemoApp*

0x0012fecc

+0x000 __VFN_table : 0x0012fed8

=00a85844 CObject::classCObject : CRuntimeClass

=00a78d54 CCmdTarget::classCCmdTarget : CRuntimeClass

=00a78d18 CCmdTarget::_commandEntries : [1] AFX_OLECMDMAP_ENTRY

=00a78d24 CCmdTarget::commandMap : AFX_OLECMDMAP

=00a78c8c CCmdTarget::_dispatchEntries : [1] AFX_DISPMAP_ENTRY

=00b1c484 CCmdTarget::_dispatchEntryCount : 0xffffffff

=00b1c488 CCmdTarget::_dwStockPropMask : 0xffffffff ......................未完

我们可以看到,第一个就是指向虚表的指针:__VFN_table

补充点小细节,用dt /f <addr>可以用它来查看任何地方的任何代码处有些什么参数和局部变量。它会关闭对值得显示并隐含/V。/f 必须是最后一个标志

0:000> dv /i /v /f VerifyTxSignDemo!WinMain

prv param  @ebp+0x08       hInstance

prv param  @ebp+0x0c   hPrevInstance

prv param  @ebp+0x10       lpCmdLine

prv param  @ebp+0x14        nCmdShow

也可以使用通配符,注意用双引号

0:000> dv /f  VerifyTxSignDemo!WinMain "*cmd*"

@ebp+0x10       lpCmdLine

@ebp+0x14        nCmdShow

bp、 bm、 bu、 bl、 bc、 ba(断点、硬件断点)

以下以skinhgy为例,windbg附加运行

bp

bp 命令是在某个地址下断点, 可以 bp 0x7783FEB 也可以 bp MyApp!SomeFunction 。

对于后者,WinDBG 会自动找到MyApp!SomeFunction 对应的地址并设置断点。 但是使用bp的问题在于:

1)当代码修改之后,函数地址改变,该断点仍然保持在相同位置,不一定继续有效;

2)WinDBG 不会把bp断点保存工作空间中

bp  Address或bp 伪寄存器或bp符号名称:

0:000> x Simple1Demo!CSimple1DemoApp::InitInstance

00640080 Simple1Demo!CSimple1DemoApp::InitInstance (void)

0:000> bp 00640080

0:000> bl

0 e 00640080     0001 (0001)  0:**** Simple1Demo!CSimple1DemoApp::InitInstance

0:000> x Kernel32!LoadLibraryW

7c80aeeb kernel32!LoadLibraryW = <no type information>

0:000> bp Kernel32!LoadLibraryW

0:000> bl

0 e 00640080     0001 (0001)  0:**** Simple1Demo!CSimple1DemoApp::InitInstance

1 e 7c80aeeb     0001 (0001)  0:**** kernel32!LoadLibraryW

0:000> bp $exentry

0:000> bl

0 e 00640080     0001 (0001)  0:**** Simple1Demo!CSimple1DemoApp::InitInstance

1 e 7c80aeeb     0001 (0001)  0:**** kernel32!LoadLibraryW

2 e 0061c895     0001 (0001)  0:**** Simple1Demo!ILT+14480(_wWinMainCRTStartup)

上例说明三种用法作用是一样的,都是bp Address(windbg内部会换成符号文件对应的地址,或伪寄存器的地址)

bp /1 Address表示该断点为一次性断点,有点类似于F4作用于OD,一旦激活就自动删除了:

如bp /1 00640080

bp Address Passes表示指定断点激活之前要忽略的次数

默认情况下,断点在第一次执行断点位置的代码时被激活。这种默认情况和把Passes 设置为1是一样的。要使得断点在程序至少执行该代码一次之后才激活,可以将这个值设置为2或更大。例如,值为2时,使得断点在第二次执行到该代码时被激活。该参数创建一个在每次执行断点处的代码时被减少1的计数器。要查看Passes 计数器的初始值和当前值,使用bl (Breakpoint List)。Passes 仅当程序响应g (Go)命令并执行通过断点时才减少。单步或跟踪(tracing)通过它是不会减少的。当Passes 到达1时,可以通过清除并重设断点来重置它。

我们来试试,用bc把以前断点都删除,再设置在第三次运行LoadLibraryW时激活该处断点

0:000> bc*

0:000> bl

0:000> bp 7c80aeeb 3

0:000> bl

0 e 7c80aeeb     0003 (0003)  0:**** kernel32!LoadLibraryW

我们注意到这个断点显示的是0003 (0003) F5运行:

0:000> g

Breakpoint 0 hit

eax=00000002 ebx=7ffdc000 ecx=00000000 edx=00a8660c esi=0263f76e edi=0263f6f2

eip=7c80aeeb esp=0012fd68 ebp=0012fdb0 iopl=0         nv up ei pl nz na po nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202

kernel32!LoadLibraryW:

7c80aeeb 8bff            mov     edi,edi

0:000> bl

0 e 7c80aeeb     0001 (0003)  0:**** kernel32!LoadLibraryW

我们注意到这个断点现在显示的是0001 (0003),表示前面忽略了两次,

bu 命令是针对某个符号下断点。 比如 bu MyApp!SomeFunction 。 在代码被修改之后, 该断点可以随着函数地址改变而自动更新到最新位置。  而且bu 断点会保存在WinDbg工作空间中, 下次启动 Windbg 的时候该断点会自动设置上去。另外,在模块没有被加载的时候,bp 断点会失败(因为函数地址不存在),而bu 断点则可以成功。 新版的WinDBG中 bp失败后会自动被转成bu

bm 命令也是针对符号下断点。 但是它支持匹配表达式。 很多时候你下好几个断点。 比如,把MyClass 所有的成员函数都下断点:bu MyApp!MyClass::* , 或者把所有以CreateWindow开头的函数都下断点:bu user32!CreateWindow*

这个函数比较有用,比如我想对Draw开头的函数都下断点:

0:000> bc*

0:000> bl

0:000> bm *!draw*

1: 00695930 @!"Simple1Demo!DrawState"

2: 0175c790 @!"SkinLog!DrawState"

3: 019f65d0 @!"SkinScroll!DrawState"

4: 10119d10 @!"SkinHgy!DrawState"

0:000> bl

1 e 00695930     0001 (0001)  0:**** Simple1Demo!DrawState

2 e 0175c790     0001 (0001)  0:**** SkinLog!DrawState

3 e 019f65d0     0001 (0001)  0:**** SkinScroll!DrawState

4 e 10119d10     0001 (0001)  0:**** SkinHgy!DrawState

bl(breakpoint list) 命令列出已存在的断点的信息

对于每个断点,该命令显示以下信息:

断点ID。该ID是一个可以在其他命令中引用这个断点的十进制数字。

断点状态。它可以是e (启用) 或d (禁用)。

如果出现字母"u",说明断点是未定的。即,该断点中的符号引用还没有和任何当前已加载的模块匹配。

断点位置的虚拟地址或符号表达式。如果启用了源码行号加载,bl 命令显示文件和行号信息而不是地址偏移。如果该断点未定,则它的地址会被省略并出现在列表末尾。

(仅数据断点) 数据断点的类型和大小信息会显示出来。类型可以是e (执行)、 r (读/写)、w (写)或 i (输入/输出)。类型后面是以字节为单位的大小。关于这种类型断点的更多信息,查看ba (Break on Access)。

断点被激活前需要忽略的剩余次数,后面是在圆括号中的初始次数。(这种断点的更多信息,查看bp, bu, bm (Set Breakpoint)中对Passes参数的说明。)

关联的进程和线程。如果线程是用三个星号("***")表示的,说明这不是一个指定线程的断点。

符合断点地址的模块和函数以及偏移。如果是未定断点,这里会用括号括起来的断点地址替代。如果断点设置在合法地址,但是没有符号信息,这个域为空。

该断点触发时要自动执行的命令。这个命令以引号括起来。

bc(breakpoint clear) 命令在系统中移除先前设置的断点。

使用星号(*)来指定所有断点

内存断点(硬件断点)

ba 命令就是针对数据下断点的命令, 该断点在指定内存被访问时触发。 命令格式为

ba Access Size [地址]

Access 是访问的方式, 比如 e (执行), r (读/写), w (写)

Size 是监控访问的位置的大小,以字节为单位。 值为 1、2或4,还可以是 8(64位机)。

如果Access是e,Size必须是1

比如要对内存0x0483DFE进行写操作的时候下断点,可以用命令 ba w4 0x0483DFE

在Access 和Size 之间不能加入空格

0:000> bc*

0:000> ba r4 00a76748

0:000> bl

0 e 00a76748 r 4 0001 (0001)  0:**** Simple1Demo!`string'

有时我们只想让程序断在某个线程上:

可以用:

0:005> ~1  bp Simple1Demo!DrawState

0:005> bl

0 e 0134bfc0     0001 (0001)  0:~001 Simple1Demo!DrawState

0:005> bp Simple1Demo!DrawState

0:005> bl

0 e 0134bfc0     0001 (0001)  0:~001 Simple1Demo!DrawState

前面~1 表示只有当指定的线程ID为1执行到达断点的地址上时,调试器才会停止.

在X86下dr0-dr3记录了断点地址值,dr6是断点的状态寄存器,dr7是断点的控制寄存器。

另外,在初始断点命中时,尚不能设置硬件断点,如果设置,会得到如下错误:

0:000> ba r1 7c92120f

^ Unable to set breakpoint error

The system resets thread contexts after the process

breakpoint so hardware breakpoints cannot be set.

Go to the executable's entry point and set it then.

初始断点后系统会重设线程上下文,因此不能设置硬件断点,建议执行到程序的入口后再设置

0:002> ba e1 00bc1b3a

breakpoint 0 redefined

0:002> r dr0

dr0=00bc1b3a

!gle、g(错误码、g系列)

!gle

!gle 扩展显示当前线程的最后一个错误码。

这个太好记了,getlasterror取首字母:

<span style="font-size:18px;">0:002> !gle

LastErrorValue: (Win32) 0 (0) - <Unable to get error code text>

LastStatusValue: (NTSTATUS) 0 - STATUS_WAIT_0

</span>

-all 显示目标系统中每个用户模式线程的最终错误。如果在用户模式下省略该参数,调试器显示当前线程的最终错误。如果内核模式下省略该参数,调试器显示当前的寄存器上下文指定的线程的最终错误。

<span style="font-size:18px;">0:002> !gle

LastErrorValue: (Win32) 0 (0) - <Unable to get error code text>

LastStatusValue: (NTSTATUS) 0 - STATUS_WAIT_0

0:002> !gle -all

Last error for thread 0:

LastErrorValue: (Win32) 0 (0) - <Unable to get error code text>

LastStatusValue: (NTSTATUS) 0xc0000135 - {

Last error for thread 1:

LastErrorValue: (Win32) 0 (0) - <Unable to get error code text>

LastStatusValue: (NTSTATUS) 0 - STATUS_WAIT_0

Last error for thread 2:

LastErrorValue: (Win32) 0 (0) - <Unable to get error code text>

LastStatusValue: (NTSTATUS) 0 - STATUS_WAIT_0

</span>

!gle扩展显示GetLastError的值并尝试解码它。

g

g(go)命令开始指定进程或线程的执行。这种执行将会在程序结束、遇到BreakAddress 或者其他造成调试器停止的事件发生时停止。

这个我们太经常用到了,

1.如果直接用g不带参数,表示无条件恢复调试目标的执行

2.g Address,相当于设了一个一次性断点,然后将调试目标执行到断点

3.gu 用于使调试目标执行完当前函数并且返回到调用者,由于这个命令知道当前的栈指针,因此它可以从递归函数调用中返回

4.运行到光标处,可以使用Ctrl+F10

5.gc 命令使用和遇到断点时一样的方式(单步、跟踪或自由执行)来从一个条件断点恢复执行。

6.gn和gN 命令继续给定线程的执行,但是不将异常标记为已处理。这样使得应用程序的异常处理器可以处理该异常

7.gh命令将给定线程的异常标识为已处理,并且允许该线程从产生异常的指令继续执行。

---------------------

sx、ld(设置异常处理、设置模块加载时断下)

1.sx

sx* 命令用来控制被调试的程序发生某个异常或特定事件时,调试器要采取的动作

sx 命令显示当前进程的异常列表和所有非异常的事件列表,并且显示调试器遇到每个异常和事件时的行为。

sxr 命令将所有异常和事件过滤器的状态重设为默认值。命令被清除、中断和继续选项被重设为默认值,等等。

sx这个命令的输出信息可以分为三个部分:

第一部分是事件处理与相应处理模式的交互,第二部分是标准的异常交互和处理行为,最后一部分是用户自定义的异常交互和处理行为

以下面为例,我们先输入sxr再输入sx看下默认的处理行为都是怎么样的:

0:000> sxr

sx state reset to defaults

0:000> 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

随便找个出来ld - Load module - output,说明在加载模块时的行为是输出,OK,我们把它断掉:

sxe         Break

(Enabled)         当发生该异常时,在任何错误处理器被激活之前目标立即中断到调试器中。这种处理类型称为第一次处理机会。

sxd Second chance break

(Disabled)        发生该类异常时,调试器不会在第一次处理机会时中断(虽然会显示信息)。如果其他错误处理器没有处理掉该异常,执行会停止下来并中断到调试器。这种处理类型称为第二次处理机会。

sxn Output

(Notify)   当该异常发生时,目标程序不中断到调试器中。但是,会通过一条消息提示发生了异常。

sxi   Ignore     异常发生时,目标程序不中断到调试器,并且不会显示信息。

使用sxe ld试试

:000> sxe ld

0:000> sx

ct - Create thread - ignore

et - Exit thread - ignore

cpr - Create process - ignore

epr - Exit process - ignore

ld - Load module - break

再次调用sx查看,我们发现现在变成了ld - Load module - break,处理行为变成了break,运行试试

0:000> g

ModLoad: 73fa0000 7400b000   C:\WINDOWS\system32\USP10.dll

eax=77ef23d4 ebx=00000000 ecx=77ef7c79 edx=62c25200 esi=00000000 edi=00000000

eip=7c92e514 esp=0012e8c0 ebp=0012e9b4 iopl=0         nv up ei ng nz ac pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000296

ntdll!KiFastSystemCallRet:

7c92e514 c3              ret

0:000> kb

ChildEBP RetAddr  Args to Child

0012e8bc 7c92d52a 7c93adfb 000007d0 ffffffff ntdll!KiFastSystemCallRet

0012e8c0 7c93adfb 000007d0 ffffffff 0012e998 ntdll!NtMapViewOfSection+0xc

0012e9b4 7c93c880 001531a0 7ffdfc00 00000000 ntdll!LdrpMapDll+0x330

0012ec14 7c9446f2 001531a0 62c202d4 62c20000 ntdll!LdrpLoadImportModule+0x174

0012ec58 7c94469b 7ffd9000 001531a0 00253128 ntdll!LdrpHandleOneNewFormatImportDescriptor+0x53

0012ec78 7c9447d5 7ffd9000 001531a0 00253128 ntdll!LdrpHandleNewFormatImportDescriptors+0x20

0012ecf4 7c936227 001531a0 00253128 c0150008 ntdll!LdrpWalkImportDescriptor+0x19e

0012efa4 7c93643d 00000000 001531a0 0012f298 ntdll!LdrpLoadDll+0x24e

0012f24c 7c801bbd 001531a0 0012f298 0012f278 ntdll!LdrLoadDll+0x230

0012f2b4 7c80aefc 77ef1a1c 00000000 00000000 kernel32!LoadLibraryExW+0x18e

0012f2c8 77f1da06 77ef1a1c 00000000 7ffdf000 kernel32!LoadLibraryW+0x11

0012f2f0 77f14361 0000001f 00000000 77d712a0 GDI32!GdiInitializeLanguagePack+0x15

0012f304 77d1a03d 00000000 00000000 7c946102 GDI32!GdiProcessSetup+0x11d

0012f444 77d1a143 7c92e473 0012f458 00000000 USER32!ClientThreadSetup+0x33

0012f448 7c92e473 0012f458 00000000 77ef67c4 USER32!__ClientThreadSetup+0x5

0012f454 77ef67c4 77ef6553 0012f5a8 0012f9f0 ntdll!KiUserCallbackDispatcher+0x13

0012f464 77d1b473 77d10000 00000001 0012fd30 GDI32!NtGdiInit+0xc

0012f9f0 7c92118a 77d10000 00000001 0012fd30 USER32!_UserClientDllInitialize+0x315

0012fa10 7c93b5d2 77d1b217 77d10000 00000001 ntdll!LdrpCallInitRoutine+0x14

0012fb18 7c93fbdc 0012fd30 7ffdf000 7ffd9000 ntdll!LdrpRunInitializeRoutines+0x344

0:000> du 77ef1a1c

77ef1a1c  "LPK.DLL"

果然断下来了,断在加载LPK.dll模块时,那么如果我们只想让它在加载SkinHgy.dll时断下来怎么办呢?需要介绍下ld了

两轮机会

对于每个异常Windows系统会最多给予两轮处理机会,对于每一轮机会Windows都会试图先分发给调试器,然后再寻找异常处理器(VEH、SEH等)。这样看来,对于每个异常,调试器最多可能收到两次处理机会,每次处理后调试器都应该向系统返回一个结果,说明它是否处理了这个异常。

对于第一轮异常处理机会,调试器通常是返回没有处理异常,然后让系统继续分发,交给程序中的异常处理器来处理。对于第二轮机会,如果调试器不处理,那么系统便会采取终极措施:如果异常发生在应用程序中,那么系统会启动应用程序错误报告过程并终止应用程序;如果发生在内核代码中,那么便启用蓝屏机制停止整个系统。所以对于第二轮处理机会,调试器通常是返回已经处理,让系统恢复程序执行,这通常会再次导致异常,又重新分发异常,如此循环。值得说明的是,对于断点异常和调试异常,调试器是在第一轮就返回已经处理的。

ld

ld(load symbols)命令加载指定模块的符号并刷新所有模块信息。

ModuleName  指定要加载符号的模块名。ModuleName 可以包含各种通配符和修饰符。

可以包含通配符,这是个好消息

0:000> lm

start    end        module name

00400000 00b89000   Simple1Demo   (deferred)

7c920000 7c9b6000   ntdll      (pdb symbols)          c:\mysymbol\ntdll.pdb\CEFC0863B1F84130A11E0F54180CD21A2\ntdll.pdb

0:000> ld Simple1Demo

*** WARNING: Unable to verify checksum for Simple1Demo.exe

Symbols loaded for Simple1Demo

0:000> lm

start    end        module name

00400000 00b89000   Simple1Demo C (private pdb symbols)  D:\project\代码\新控件库\SkinHgy\Debug\Simple1Demo.pdb

7c920000 7c9b6000   ntdll      (pdb symbols)          c:\mysymbol\ntdll.pdb\CEFC0863B1F84130A11E0F54180CD21A2\ntdll.pdb

再加载次试试:

0:000> ld Simple1Demo

Symbols already loaded for Simple1Demo

提示已加载,看来还是.reload好用啊。

那么接着1,我们来设置只在ld skinhgy时断下来:

0:000> sx

ct - Create thread - ignore

et - Exit thread - ignore

cpr - Create process - ignore

epr - Exit process - ignore

ld - Load module - break

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

0:000> sex ld skinhgy.dll

Couldn't resolve error at 'ex '

0:000> sxe ld skinhgy.dll

0:000> sx

ct - Create thread - ignore

et - Exit thread - ignore

cpr - Create process - ignore

epr - Exit process - ignore

ld - Load module - break

(only break for skinhgy.dll)

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

我们已经restart了,但ld的状态还没有变,所以WinDBG会自动把一些东西记录到工作空间(Workspace)里,因为工作空间是隐式管理的,所以容易让初用WinDBG的人摸不着头脑,像MJ说的那样操作一下,并且保存到工作空间中(结束调试时,WinDBG询问要不要保存工作空间时选YES),或者干脆删除工作空间就可以了,当然也可以写成

0:000> sxe ld skin*

0:000> sx

ct - Create thread - ignore

et - Exit thread - ignore

cpr - Create process - ignore

epr - Exit process - ignore

ld - Load module - break

(only break for skin*)

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

这样所有的Skin*模块都会触发断点

sx{e|d|i|n} [-c "Cmd1"] [-c2 "Cmd2"] [-h] {Exception|Event|*}

-c "Cmd1"

指定一个当异常或事件发生时要执行的命令。该命令在异常的第一次处理机会时执行(也就是第一轮异常),不管该异常是否会中断到调试器。Cmd1 字符串必须包含在引号中。该字符串可以包含多条用分号分隔的命令。-c和括起来的命令字符串之间的空格是可选的。

-c2 "Cmd2"

指定当异常或事件发生并且没有在第一次处理机会被处理时执行的命令。该命令在异常的第二次处理机会时执行,(也就是第二轮异常),不管它是否会中断到调试器。Cmd2 字符串必须包含在引号中。该字符串可以包含多条用分号分隔的命令。-c2 和括起来的命令字符串之间的空格是可选的。

-h

改变指定事件的处理状态而不是中断状态。如果Event 是cc、hc、bpec或ssec,-h 选项不是一定需要。

比如我要在第一次加载SkinHgy.dll时断下来并打印MSG:

0:000> sxe -c".echo 'skinhgy.dll loading'" ld:skinhgy.dll

0:000> sx

ct - Create thread - ignore

et - Exit thread - ignore

cpr - Create process - ignore

epr - Exit process - break

ld - Load module - break

Command: ".echo 'skinhgy.dll loading'"

(only break for skinhgy.dll)

ud - Unload module - ignore

运行后:

0:000> g

ModLoad: 10000000 10301000   D:\project\代码\新控件库\SkinHgy\Debug\SkinHgy.dll

'skinhgy.dll loading'

这里介绍种GUI使用的方法:

Debug--Event Filters打开:

我们看到我们先前加载的SkinHgy.dll都在,事件(-c和-c2)对应Commands按钮来修改,中断状态可以通过"Execution"来修改,还可以通过Add和Remove来增加或删除异常码.

至于

Enabled对应Break

Disabled对应Second chance break

sxe Handled 执行返回时,事件被标识为已处理。

sxd,

sxn,

sxi   Not Handled   执行返回时,事件被标识为未处理。

windbg帮助上写得很清楚了.

补充点:

av - Access violation - break - not handled

eh - C++ EH exception - second-chance break - not handled

Sxe av  //当access violation发生就停止

Sxd eh//当C++ exception发生,调试器什么都不做

这两个很有用,今天在调程序时发现内存访问一直被断,用sxi av就行了,也就是忽视av这种状态

Windebug专题相关推荐

  1. Microsoft .Net Remoting系列专题之二:Marshal、Disconnect与生命周期以及跟踪服务

    Microsoft .Net Remoting系列专题之二 一.远程对象的激活 在Remoting中有三种激活方式,一般的实现是通过RemotingServices类的静态方法来完成.工作过程事实上是 ...

  2. CS131专题-6:图像特征(Blob检测、LoG算子、Harris-Laplacian)

    本专题介绍的技术应用已不广,但是这些有利于理解SIFT算法的原理,也有助于感悟CV传统技术的发展变迁,以及解决问题的思路. 速记要点: blob是什么:blob是描述图像中局部区域的平均像素强度的特征 ...

  3. CS131专题-4:拟合(最小二乘、RANSAC、霍夫变换)

    本专题目的:了解最小二乘.RANSAC.霍夫变换这3个算法的基本原理,能够做到脱口而出,并从零编程实现. 目录 1 前言 2 最小二乘 2.1 基本原理 2.2 求解方法 3 RANSAC 算法 3. ...

  4. CS131专题-3:图像梯度、边缘检测(sobel、canny等)

    目录 1 前言 2 图像梯度 2.1 梯度公式的离散形式 2.2 图像的梯度表示 2.3 图像梯度的最简单计算方法 2.4 直接应用梯度找图像边缘的问题以及解决方案 3 边缘检测 3.1 好的边缘检测 ...

  5. CS131专题-2:高斯核、噪声、滤波

    目录 1 高斯核 1.1 一维定义 1.2 二维定义 1.2.1 应用高斯核的过程 1.2.2 不同σ值的高斯核情况 1.2.3 不同核宽度情况 1.2.4 高斯核(滤波)特性 1.2.5 使用高斯核 ...

  6. 图的算法专题——最小生成树

    概要: Prim算法 Kruskal算法 1.Prim算法 算法流程: (1)对图G(V,E)设置集合S来存放已被并入的顶点,然后执行n次(2)(3) (2)每次从未并入顶点集合中选择与集合S最近的一 ...

  7. [C#]网络编程系列专题二:HTTP协议详解

    转自:http://www.cnblogs.com/zhili/archive/2012/08/18/2634475.html 我们在用Asp.net技术开发Web应用程序后,当用户在浏览器输入一个网 ...

  8. 织梦在哪写html,织梦专题页去.html后缀的方法

    很多人在使用DedeCMS建站的过程中,创建专题页的时候,会发现生成的专题页页面是类似于http://www.xxx.com/zhuanti.html这种,后缀带有.html,这个问题在网上看了不少教 ...

  9. v-html解析的相对地址img 显示不出来_人工智能专题作品解析——APPInventor安卓应用开发AI图像识别简单应用...

    人工智能专题作品解析-- APPInventor安卓应用开发 <AI图像识别简单应用> 本程序使用APPInventor 2019桌面板. 一.思路 平时忙于工作和学习,很少涉猎其它领域的 ...

最新文章

  1. Visual Studio 2015安装教程(附激活码)
  2. 测试软件测试赢在测试2:中国软件测试专家访谈录
  3. 基于事件驱动架构构建微服务第8部分:在应用程序上实现事件溯源
  4. 1583. 统计不开心的朋友
  5. Performance of Every Day Things by Jeffrey Richter PPT and Code
  6. Pr 入门教程,如何进行创意调整?
  7. SPSS安装以及如何解决can not create java virtual machine问题
  8. 14565B简单使用教程
  9. K-means聚类算法原理及python实现
  10. No Silver Bullet: Essence and Accidents of Software | 没有银弹:软件开发中的主要问题和次要问题
  11. 隐藏win10任务栏输入法M图标
  12. ATS667LSG:真零速、高精度齿传感器 IC
  13. 微信开放平台第三方平台授权小程序权限集id对应中文说明
  14. 初中计算机卡片的制作教案,《运用Word制作电子贺卡》教学设计
  15. CentOS7-查看IP
  16. 3D点云初探:基于全卷积神经网络实现3D物体识别
  17. css弹性布局笔记一(垂直居中、水平居中、居中)(display:flex)
  18. 期货亏损过多人怎么心态走出来(期货怎么总是亏)
  19. Hi3516DV300的UVC功能测试
  20. 【剑桥摄影协会】数码相机像素(Pixel)基础

热门文章

  1. 从 B 站火到 GitHub,国人开发者又一黑科技面世!
  2. 饥荒联机版专用服务器怎么修改小偷包,饥荒联机小偷背包代码 | 手游网游页游攻略大全...
  3. 使用Trinamic TMC2300步进驱动器做一个迪斯科灯项目
  4. JSR-133: JavaTM Memory Model and Thread Specification原文解析
  5. mysql window系统备份远程数据库到本地
  6. 配置Web DashBoard
  7. 卸载landesk的方法
  8. 庖丁解牛Transformer原理
  9. hadoop学习笔记4:java实现hdfs -ls/-lsr
  10. html怎么设置默认图片,怎样设置默认照片浏览器