使用WinDbg —— .NET篇 (三)

来源:互联网 发布:ubuntu eric pyqt5 编辑:程序博客网 时间:2024/05/22 06:41

四、观察对象

在讲解观察对象前,同样先看一段Code

using System;

usingSystem.Collections.ObjectModel;

 

namespace Ext2

{

   structValueType

   {

       publicint number;

       publicstringplainText;

 

       publicValueType(int number,stringplainText)

       {

           this.number = number;

           this.plainText = plainText;

       }

   }

 

   classReferenceType

   {

       privateint number = 12;

       publicint Number

       {

           get

           {

               return number;

           }

           set

           {

               number = value;

           }

       }

 

       privatestringplainText = "Gone with the wind";

       publicstringPlainText

       {

           get

           {

               returnplainText;

           }

           set

           {

               plainText = value;

           }

       }

 

       privateValueTypevalueType;

       publicValueTypeValueType

       {

           get {returnvalueType; }

           set {valueType = value; }

       }

   }

 

   classProgram

   {

       privatestaticint[]staticArray = newint[2] { 0x1234, 0x5678 };

       privateint[]instanceArray = newint[5] { 8, 9, 4, 56, 5 };

 

       staticvoid Main(string[]args)

       {

           ValueTypevalueType = newValueType(1,"valueType1");

           ReferenceTypereferType = newReferenceType();

           referType.Number = 100;

           referType.PlainText = "Nothing can replacehardwork.";

           referType.ValueType = valueType;

 

           Collection<string> collection = newCollection<string>();

           collection.Add("abc");

           collection.Add("bef");

 

           newProgram();

           Console.WriteLine("Press any key to throw exception:");

           Console.Read();

           ThrowException(null);

       }

 

       publicstaticvoidThrowException(object @object)

       {

           if (@object ==null)

           {

               thrownewArgumentNullException("@object");

           }

       }

   }

}

在上面的code中几乎没涉及到逻辑的编写,分别定义了一个结构和一个类,也分别构造了两种类型的实例,并创建了一个集合和一个数组,最后抛出一个异常。接下来要讲解的分别是怎么观察引用类型对象、值类型实例、数组、集合和异常对象。

首先利用Windbg通过打开执行或者附加进程的方式建立调试会话,然后执行到“Press any key to throw exception:”弹出的时候中断执行,这时候通过切换线程到主线程(ID0),然后打印出当前调用的参数:

0:004> ~0s

eax=00000001 ebx=0023f374 ecx=00000000 edx=00000000 esi=00000003 edi=02614a90

eip=751f7cb0 esp=0023f1ec ebp=0023f274 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:

751f7cb0 83c404          add     esp,4

0:000> !clrstack -a

OS Thread Id: 0xd20 (0)

Child SP       IP Call Site

0023f2dc 751f7cb0 [InlinedCallFrame: 0023f2dc]

0023f2d8 78e40a27 *** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\ce5f61c5754789df97be8dc991c47d07\mscorlib.ni.dll

*** ERROR: Module load completed but symbols could not be loaded for C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\ce5f61c5754789df97be8dc991c47d07\mscorlib.ni.dll

DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32ByRef, IntPtr)

    PARAMETERS:

       <no data>

        <no data>

        <no data>

        <no data>

        <no data>

 

0023f2dc 7958f020 [InlinedCallFrame: 0023f2dc] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32ByRef, IntPtr)

0023f340 7958f020 System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32ByRef)

    PARAMETERS:

        hFile = <no data>

        bytes = <no data>

        offset = <no data>

        count = <no data>

        useFileAPIs = <no data>

        isPipe = <no data>

        bytesRead = <no data>

    LOCALS:

        <no data>

        0x0023f348 = 0x02614a90

        <no data>

        <no data>

        <no data>

        <no data>

        <no data>

 

0023f374 7958ef17 System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)

    PARAMETERS:

        this = <no data>

        buffer = <no data>

        offset = <no data>

        count = <no data>

    LOCALS:

        <no data>

        <no data>

 

0023f394 78dd8058 System.IO.StreamReader.ReadBuffer()

    PARAMETERS:

        this (<CLR reg>) = 0x02614a2c

    LOCALS:

        <no data>

        <no data>

 

0023f3a8 7947a596 System.IO.StreamReader.Read()

    PARAMETERS:

        this (<CLR reg>) = 0x02614a2c

    LOCALS:

        <no data>

 

0023f3b4 79595e15 System.IO.TextReader+SyncTextReader.Read()

    PARAMETERS:

        this (<CLR reg>) = 0x02614da4

 

0023f3c4 79476622 System.Console.Read()

 

0023f3cc 003001d9 *** WARNING: Unable to verify checksum for Ext2.exe

Ext2.Program.Main(System.String[]) [f:\Code\CSharp\UsedForWinDbg\Ext2\Program.cs @ 73]

    PARAMETERS:

        args (0x0023f3ec) = 0x026122d4

    LOCALS:

        0x0023f3e4 =0x026122f8

        0x0023f3dc =0x026123dc

        0x0023f3d8 =0x02612428

 

0023f584 0f893de2 [GCFrame: 0023f584]

在输出结果中能看到执行Main方法的时候有三个局部变量(0x026122f8,0x026123dc, 0x02612428,根据这三个局部变量的值可以大致猜出这三个值都是指向堆中的地址,所以这三个值都是指向对象,引用类型的对象可以通过命令!dumpobj打印出对应的详细信息:

0:000>!dumpobj 0x026122f8

Name:       System.String

MethodTable: 78e7224c

EEClass:    78aa3444

Size:       34(0x22) bytes

File:       C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

String:     valueType1

Fields:

     MT    Field   Offset                Type VT     Attr   Value Name

78e73aa8 40000aa        4         System.Int32 1 instance       10 m_stringLength

78e72c44 40000ab        8          System.Char 1 instance       76 m_firstChar

78e7224c 40000ac        c        System.String 0   shared   static Empty

   >> Domain:Value  005f67d0:NotInit <<

0:000>!do 0x026123dc

Name:       Ext2.ReferenceType

MethodTable: 0028399c

EEClass:    0028186c

Size:       24(0x18) bytes

File:       F:\Code\CSharp\UsedForWinDbg\Ext2\bin\Debug\Ext2.exe

Fields:

     MT    Field   Offset                Type VT     Attr   Value Name

78e73aa8 4000003        8         System.Int32 1 instance      100 number

78e7224c 4000004        4        System.String 0 instance 0261231c plainText

002838c0 4000005        c       Ext2.ValueType 1 instance 026123e8 valueType

0:000>!do 0x02612428

Name:       System.Collections.ObjectModel.Collection`1[[System.String,mscorlib]]

MethodTable: 78aab900

EEClass:    78afcfec

Size:       16(0x10) bytes

File:       C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

Fields:

     MT    Field   Offset                Type VT     Attr   Value Name

78aa8208 4000ca1        4 ...Canon, mscorlib]] 0 instance 02612438 items

78e725ec 4000ca2        8        System.Object 0 instance 00000000 _syncRoot

通过上表观察到“!do”命令也具有同样的作用,这是因为“!do”命令是“!dumpobj”的别名。打出来的信息里面包含对象的名称、方法表地址、EEClass地址、对象大小、程序集所在文件路径、和一个Fields的详细信息表格。其中这个表格的MT表示该Field的方法表地址;Field是该Field元数据的Token,这个值基本上用不上;Offset表示该Field的的相对偏移位置,比如说第二个对象(0x026123dc),第一个字段number的地址就是0x026123dc+ 8Type表示该Field的类型;VT列的值为1表示该Field是值类型,为0表示引用类型;Value表示该Field的值,如果该Field是引用类型,那么这个值就是该Field的地址,如果是值类型,有可能是这个值类型的值,也有可能是这个值类型的地址;Name表示该Field的名字。

通过观察第二个对象(0x026123dc)的详细信息,可以看到numbervalueType都是值类型,其中number的值,直接从表格中看的出来,为100,而valueType的值却是一个地址,而且还是处在堆中,对于值类型,可以通过命令“!dumpvc”来打印出这个值得详细信息,前提是需要知道这个值的地址和方法表地址:

0:000>!dumpvc 002838c0 026123e8

Name:       Ext2.ValueType

MethodTable: 002838c0

EEClass:    002817d4

Size:       16(0x10) bytes

File:       F:\Code\CSharp\UsedForWinDbg\Ext2\bin\Debug\Ext2.exe

Fields:

     MT    Field   Offset                Type VT     Attr   Value Name

78e73aa8 4000001        4         System.Int32 1 instance        1 number

78e7224c 4000002        0        System.String 0 instance 026122f8 plainText

其中第一个参数是方法表地址,第二个参数为这个值的地址。看到这里追根究底的读者也许有个疑问:从代码上看在中构建了值类型的实例valueType,然后赋给了引用类型的对象referType,相当于将valueType中的值拷贝到了给referType分配的内存中,这个内存是在堆上。至于堆上的这个值类型实例,可以很方便的打印出来,但是,怎么查看这个中的valueType呢?笔者的办法是猜,猜这个变量的地址(当然需要结合一点编译原理基础知识),referType的地址储存在中,地址为0x0023f3dcvalueType的大小为8个字节,所以根据这点可以猜出来valueType的地址为0x0023f3dc + 8;同样根据第一个对象(string类型,值为“valueType1”)是ValueType构造函数的最后一个参数,是第一个被push中的,其次是number,根据这点可以推出valueType的地址为0x0023f3e4(存储第一个参数的中的位置):

0:000>!dumpvc 002838c0 0x0023f3dc+8

Name:       Ext2.ValueType

MethodTable: 002838c0

EEClass:    002817d4

Size:       16(0x10) bytes

File:       F:\Code\CSharp\UsedForWinDbg\Ext2\bin\Debug\Ext2.exe

Fields:

     MT    Field   Offset                Type VT     Attr   Value Name

78e73aa8 4000001        4        System.Int32  1 instance        1 number

78e7224c 4000002        0        System.String 0 instance 026122f8 plainText

对于Collection对象,想要找出其中的items的各个值,这个时候就需要了解源代码的一点知识了,对应的源代码可以在http://referencesource.microsoft.com/中找到。从源代码可以看出items其实就是对List<T>的一个封装,然后List<T>中的items其实就是数组,对于数组对象,可以使用命令“!dumparray”或者!da”命令打印出详细各个item的信息(02612438collection变量中对应的items字段的地址,由上面的“!do 0x02612428”打印结果可知):

0:000>!do 02612438

Name:       System.Collections.Generic.List`1[[System.String,mscorlib]]

MethodTable: 78aa941c

EEClass:    78b02f98

Size:       24(0x18) bytes

File:       C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

Fields:

     MT    Field   Offset                Type VT     Attr   Value Name

78e5fe8c 4000c91        4      System.Object[] 0 instance 02612460 _items

78e73aa8 4000c92        c         System.Int32 1 instance        2 _size

78e73aa8 4000c93       10         System.Int32 1 instance        2 _version

78e725ec 4000c94        8        System.Object 0 instance 00000000 _syncRoot

78e5fe8c 4000c95        0      System.Object[] 0   shared   static _emptyArray

   >> Domain:Value dynamic statics NYI 005f67d0:NotInit <<

0:000>!dumparray 02612460

Name:       System.String[]

MethodTable: 78e5fe8c

EEClass:    78b019e4

Size:       32(0x20) bytes

Array:      Rank 1, Number of elements 4, Type CLASS

ElementMethodtable: 78e7224c

[0]02612364

[1] 02612378

[2] null

[3] null

0:000>!do 02612364

Name:       System.String

MethodTable: 78e7224c

EEClass:    78aa3444

Size:       20(0x14) bytes

File:       C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

String:     abc

Fields:

     MT    Field   Offset                Type VT     Attr   Value Name

78e73aa8 40000aa        4         System.Int32 1 instance        3 m_stringLength

78e72c44 40000ab        8          System.Char 1 instance       61 m_firstChar

78e7224c 40000ac        c       System.String  0  shared   static Empty

   >> Domain:Value  005f67d0:NotInit <<

!dumparray”命令可以使用开关“details”,打印出各个item的详细信息:

0:000>!da -details 02612460

Name:       System.String[]

MethodTable: 78e5fe8c

EEClass:    78b019e4

Size:       32(0x20) bytes

Array:      Rank 1, Number of elements 4, Type CLASS

ElementMethodtable: 78e7224c

[0] 02612364

   Name:        System.String

   MethodTable: 78e7224c

   EEClass:     78aa3444

   Size:        20(0x14) bytes

   File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

   String:          abc   

   Fields:

             MT    Field   Offset                Type VT     Attr   Value Name

       78e73aa8  40000aa        4            System.Int32      1     instance           3     m_stringLength

       78e72c44  40000ab        8             System.Char      1    instance           61     m_firstChar

       78e7224c  40000ac        c           System.String      0      shared   static     Empty

       >> Domain:Value      005f67d0:NotInit     <<

[1] 02612378

   Name:        System.String

   MethodTable: 78e7224c

   EEClass:     78aa3444

   Size:        20(0x14) bytes

   File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

   String:          bef   

   Fields:

             MT    Field   Offset                Type VT     Attr   Value Name

       78e73aa8  40000aa        4            System.Int32      1     instance           3     m_stringLength

       78e72c44  40000ab        8             System.Char      1    instance           62     m_firstChar

       78e7224c  40000ac        c           System.String      0      shared   static     Empty

       >> Domain:Value      005f67d0:NotInit     <<

[2] null

[3] null

对于非局部变量,要怎么找出类的实例变量和静态变量的地址呢?在这里个人总结出三种不同的方法,读者可以根据需要结合使用:

1. 通过“!name2ee”先找出类的EEClass地址,然后利用“!dumpclass”打印出对应的EEClass信息,这里面就包含静态变量地址的信息:

0:000>!name2ee Ext2.exe Ext2.Program

Module:     00282eac

Assembly:   Ext2.exe

Token:      02000004

MethodTable: 00283834

EEClass:    00281330

Name:       Ext2.Program

0:000>!dumpclass 00281330

Class Name:     Ext2.Program

mdToken:        02000004

File:           F:\Code\CSharp\UsedForWinDbg\Ext2\bin\Debug\Ext2.exe

Parent Class:   78aa34f8

Module:         00282eac

Method Table:   00283834

Vtable Slots:   4

Total Method Slots: 6

Class Attributes:   100000 

Transparency:       Critical

NumInstanceFields:  1

NumStaticFields:    1

     MT    Field   Offset                Type VT     Attr   Value Name

78e73a70 4000007        4       System.Int32[] 0 instance           instanceArray

78e73a70 4000006        4       System.Int32[] 0   static 026122e4 staticArray

2. 通过“!dumpstackobjects”或“!dso”打印出当前线程中栈中所有的对象的引用,然后找到对应的引用类型对象:

0:000>!dso

OS Thread Id: 0xd20 (0)

ESP/REG Object   Name

0023F240 026149f0 Microsoft.Win32.SafeHandles.SafeFileHandle

0023F2F8 026149f0 Microsoft.Win32.SafeHandles.SafeFileHandle

0023F31C 026149f0 Microsoft.Win32.SafeHandles.SafeFileHandle

0023F328 026149f0 Microsoft.Win32.SafeHandles.SafeFileHandle

0023F350 026149f0 Microsoft.Win32.SafeHandles.SafeFileHandle

0023F37C 02614a2cSystem.IO.StreamReader

0023F398 02614a2cSystem.IO.StreamReader

0023F3A8 02614da4System.IO.TextReader+SyncTextReader

0023F3B4 026122d4System.Object[]    (System.String[])

0023F3CC02612480 Ext2.Program

0023F3D0 02612428 System.Collections.ObjectModel.Collection`1[[System.String,mscorlib]]

0023F3D4 026123dc Ext2.ReferenceType

0023F3D8 02612428 System.Collections.ObjectModel.Collection`1[[System.String,mscorlib]]

0023F3DC 026123dc Ext2.ReferenceType

0023F3E4 026122f8System.String    valueType1

0023F3EC 026122d4System.Object[]    (System.String[])

0023F470 026122d4System.Object[]    (System.String[])

0023F5D4 026122d4System.Object[]    (System.String[])

0023F60C 026122d4System.Object[]    (System.String[])

0023FB48 02611238System.SharedStatics

3. 还可以通过“!dumpheap”命令和“type”开关打印出对应类型的所有实例对象和静态对象:

0:000>!dumpheap -type Ext2.Program

 Address      MT     Size

02612480 00283834      12    

 

Statistics:

     MT    Count    TotalSize Class Name

00283834       1           12 Ext2.Program

Total 1 objects

最后使用命令g让程序继续运行,在控制台上任意输入一个字符抛出一个异常,这个时候Windbg自动中断,到输入模式:

0:000>g

ModLoad: 70c10000 70c27000  C:\Windows\SysWOW64\CRYPTSP.dll

ModLoad: 70bd0000 70c0b000  C:\Windows\SysWOW64\rsaenh.dll

ModLoad: 70b20000 70b2e000  C:\Windows\SysWOW64\RpcRtRemote.dll

ModLoad: 77080000 77103000  C:\Windows\syswow64\CLBCatQ.DLL

ModLoad: 6ec10000 6ecfc000  image6ec10000

ModLoad: 00f10000 00ffc000  image00f10000

(1680.d20): CLR exception - code e0434352 (first chance)

ModLoad: 734e0000 734e9000  C:\Windows\SysWOW64\VERSION.dll

ModLoad: 5d8b0000 5d984000  C:\Windows\Microsoft.NET\Framework\v4.0.30319\diasymreader.dll

(1680.d20): CLR exception - code e0434352 (!!! secondchance !!!)

*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Windows\syswow64\KERNELBASE.dll -

*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll -

eax=0023f1e4ebx=00000005 ecx=00000005edx=00000000 esi=0023f2a4edi=00000001

eip=7595c42desp=0023f1e4 ebp=0023f234iopl=0         nv upei pl nz ac pe nc

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

KERNELBASE!RaiseException+0x58:

7595c42d c9             leave

这个时候能看到程序运行到了ThrowException方法的结尾:

0:000>!clrstack

OS Thread Id: 0xd20 (0)

Child SP      IP Call Site

0023f2f8 7595c42d [HelperMethodFrame: 0023f2f8]

0023f3b4 00300445 Ext2.Program.ThrowException(System.Object)[f:\Code\CSharp\UsedForWinDbg\Ext2\Program.cs @ 83]

0023f3cc 003001e5 Ext2.Program.Main(System.String[]) [f:\Code\CSharp\UsedForWinDbg\Ext2\Program.cs @ 74]

0023f584 0f893de2 [GCFrame: 0023f584]

通过“!threads”,就能看到这个异常的地址,并可以通过“!printexception”或“!pe”命令打印出该异常的详细信息:

0:000>!threads

ThreadCount:     2

UnstartedThread: 0

BackgroundThread: 1

PendingThread:   0

DeadThread:      0

Hosted Runtime:  no

                                                                        Lock 

      ID OSID ThreadOBJ    State GC Mode    GC Alloc Context  Domain  Count Apt Exception

  0    1  d20 005fdb40    2a020 Preemptive  0261DB48:00000000 005f67d0 0    MTA System.ArgumentNullException 02614dd4

  2    2  4d8 00637f88    2b220 Preemptive  00000000:00000000 005f67d0 0    MTA (Finalizer)

0:000>!pe 02614dd4

Exception object: 02614dd4

Exception type:  System.ArgumentNullException

Message:         值不能为 null

InnerException:  <none>

StackTrace (generated):

   SP       IP       Function

   0023F3B4 00300444 Ext2!Ext2.Program.ThrowException(System.Object)+0x6c

   0023F3CC 003001E5 Ext2!Ext2.Program.Main(System.String[])+0x10d

 

StackTraceString: <none>

HResult: 80004003

或者直接通过“!pe”命令打印出当前线程中的Exception信息:

0:000> !pe

Exception object: 02614dd4

Exception type:  System.ArgumentNullException

Message:         值不能为 null

InnerException:  <none>

StackTrace (generated):

   SP       IP       Function

   0023F3B4 00300444 Ext2!Ext2.Program.ThrowException(System.Object)+0x6c

   0023F3CC 003001E5 Ext2!Ext2.Program.Main(System.String[])+0x10d

 

StackTraceString: <none>

HResult: 80004003

还有一个比较常用的常用的关于“!pe”的命令,那就是打印出所有线程中的Exception信息:

0:000>~*e !pe

Exception object: 02614dd4

Exception type:  System.ArgumentNullException

Message:         值不能为 null

InnerException:  <none>

StackTrace (generated):

   SP       IP       Function

   0023F3B4 00300444 Ext2!Ext2.Program.ThrowException(System.Object)+0x6c

   0023F3CC 003001E5 Ext2!Ext2.Program.Main(System.String[])+0x10d

 

StackTraceString: <none>

HResult: 80004003

The current thread is unmanaged

There is no current managed exception on this thread

The current thread is unmanaged

The current thread is unmanaged

The current thread is unmanaged

The current thread is unmanaged

The current thread is unmanaged

 

 

0 0
原创粉丝点击