Custom DumpArray – Windbg

The sos has !dumparray for getting contents of the array. But it cannot be used for scripting or automation. Here is an example

using System;
namespace ConsoleApplication
{
 class Program
{
 Test[] arr = new[] { new Test() { ID = 1, Name = "Foo" }, new Test() { ID = 2, Name = "Bar" } };
 static void Main(string[] args)
 {
 var p = new Program();
 Console.WriteLine(p.arr);
 Console.Read();
 }
 }
 class Test
 {
 public int ID;
 public string Name;
 }
}

And here is the output of the arr variable within the debugger

0:000> !da -details 021fbc6c
Name:        ConsoleApplication.Test[]
MethodTable: 64b56c28
EEClass:     648d9698
Size:        24(0x18) bytes
Array:       Rank 1, Number of elements 2, Type CLASS
Element Methodtable: 001938b0
[0] 021fbc84
Name:        ConsoleApplication.Test
MethodTable: 001938b0
EEClass:     00191488
Size:        16(0x10) bytes
File:        C:UsersnaveenDocumentsVisual Studio 2010ProjectsConsoleApplication9binDebugConsoleApplication.exe
Fields:
MT    Field   Offset                 Type VT     Attr    Value Name
64ba2978  4000002        8             System.Int32      1     instance            1     ID
64b9f9ac  4000003        4            System.String      0     instance     021fbc44     Name
[1] 021fbc94
Name:        ConsoleApplication.Test
MethodTable: 001938b0
EEClass:     00191488
Size:        16(0x10) bytes
File:        C:UsersnaveenDocumentsVisual Studio 2010ProjectsConsoleApplication9binDebugConsoleApplication.exe
Fields:
MT    Field   Offset                 Type VT     Attr    Value Name
64ba2978  4000002        8             System.Int32      1     instance            2     ID
64b9f9ac  4000003        4            System.String      0     instance     021fbc58     Name

From the output  we cannot see the values of the  Name variable within the Test class. To get the value we would have manually issue a !dumpobj on each one of these. In this post I will demonstrate how to automate this. In doing so we will also explore the array internals from raw memory perspective.

To start of lets dump the raw memory of the array. The  array address is 021fbc6c

dd 021fbc6c

0:000> dd 021fbc6c
021fbc6c  64b56c28 00000002 001938b0 021fbc84
021fbc7c  021fbc94 00000000 001938b0 021fbc44
021fbc8c  00000001 00000000 001938b0 021fbc58
021fbc9c  00000002 00000000 64ba7490 00000000
021fbcac  00000000 00000000 00000000 00000000
021fbcbc  00000000 64b9f5e8 00000000 40010000
021fbccc  64ba6034 00000007 00000004 00000100
021fbcdc  00000000 64ba6f40 00000000 00000000

Fields

  1. 64b56c28 – Array’s Method table pointer
  2. 00000002 – Array’s length ( this will be used later)
  3. 001938b0 Array contents method table pointer ( Test class)
  4. 021fbc84, 021fbc94 – Contents of the array( 2 instances of the Test class)

I would be using  $t0, $t1   User-Defined Pseudo-Registers within my script  as local variables to maintain state. Think of them as predefined variables that we can use. Here is the script to get just the Name from the array

.for (r $t0=0; @$t0 < poi(021fbc6c+0x4); r$t0=@$t0+1 ) { r$t1 = 0; .if(@$t0 = 0) { r$t1=10} .else { r$t1= 10+ @$t0*4};.echo ************;!do poi(poi((021fbc6c-0x4)+@$t1)+0x4) }

Here is the explanation for the above script

  1. The .for loop is used to iterate through the contents of the array :  “.for (r $t0=0; @$t0 < poi(021fbc6c+0x4); r$t0=@$t0+1 ) ”
    • The loop variable is $t0, which is initialized to zero  “r $t0=0;”,
    • Next is the loop condition check @$to < poi(021fbc6c+0x4) , the poi(021fbc6c+0x4) is the pointer deference to array length which is 00000002
    • And the last statement is the increment command of the loop variable r$t0=@$t0+1
    • Tip :- I am using “@” before the “$” for increased speed within the debugger when accessing registers.
  2. The next statement is “r$t1 = 0” is initializing another pseudo register to zero
  3. After which the command “if(@$t0 = 0) { r$t1=10} .else { r$t1= 10+ @$t0*4}” resets the value of $t1 register either “10” or $t0 * 4, where $to is loop variable. I do this because the first instance of the Test class within the array is in the 10th offset and the rest of them would be on the next 4th offset. So for example the first time loop ,$t1 would be 10 , the second time  $t1 would 14 (10 + 1*4).
  4. The “.echo ************” is just for line separation
  5. The last command is the one which does most of the work
    • The command poi((021fbc6c-0x4)+@$t1) would return the pointer of the each element in the array which is instance of Test class . The first time it would be poi((021fbc6c-0x4)+10) which would point 021fbc84 and the next time it would be poi((021fbc6c-0x4)+14) which would be 021fbc94
    • The outermost “poi 0x4” is to get pointer of the member variable Name and dump its content using !do

And here is the output from the script

    0:000> .for (r $t0=0; @$t0 < poi(021fbc6c   +0x4); r$t0=@$t0+1 ) { r$t1 = 0; .if(@$t0 = 0) { r$t1=10} .else { r$t1= 10+ @$t0*4};.echo ************;  !do poi(poi((021fbc6c-0x4)+@$t1)+0x4) }
    ************
    Name:        System.String
    MethodTable: 64b9f9ac
    EEClass:     648d8bb0
    Size:        20(0x14) bytes
    File:        C:WindowsMicrosoft.NetassemblyGAC_32mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll
    String:      Foo
    Fields:
    MT    Field   Offset                 Type VT     Attr    Value Name
    64ba2978  40000ed        4         System.Int32  1 instance        3 m_stringLength
    64ba1dc8  40000ee        8          System.Char  1 instance       46 m_firstChar
    64b9f9ac  40000ef        8        System.String  0   shared   static Empty
    >> Domain:Value  00745c28:021f1228 <<
    ************
    Name:        System.String
    MethodTable: 64b9f9ac
    EEClass:     648d8bb0
    Size:        20(0x14) bytes
    File:        C:WindowsMicrosoft.NetassemblyGAC_32mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll
    String:      Bar
    Fields:
    MT    Field   Offset                 Type VT     Attr    Value Name
    64ba2978  40000ed        4         System.Int32  1 instance        3 m_stringLength
    64ba1dc8  40000ee        8          System.Char  1 instance       42 m_firstChar
    64b9f9ac  40000ef        8        System.String  0   shared   static Empty
    >> Domain:Value  00745c28:021f1228 <<

 

naveensrinivasan

 

4 thoughts on “Custom DumpArray – Windbg

  1. Pingback: DotNetShoutout
  2. I think there might be a bug in the code snippet. Shouldn’t you be adding 0x04 to your original address rather than subtracting?

Leave a Reply

Your email address will not be published. Required fields are marked *