.netenumswindbgsos

How do I list the values of an enum from a memory dump using SOS and Windbg?


I have a mini dump that I have attached windbg to. The mini dump is from a .NET 4.6.1 ASP.NET site running on IIS. I would like to get the definition of my enum but whenever I get the MethodTable of the Class I am just getting the following.

0:000> !DumpMT /d 256db60c
EEClass:         256c773c
Module:          201fcfb0
Name:            MyDll.eDefaultRelatedObjects
mdToken:         02000029
File:            C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\303e164d\216bec4f\assembly\dl3\bdb2a421\004bd941_fee1d501\MyDll.dll
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 23
Number of IFaces in IFaceMap: 3


0:000> !DumpClass /d 256c773c 
Class Name:      MyDll.eDefaultRelatedObjects 
mdToken:         02000029 
File:            C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\303e164d\216bec4f\assembly\dl3\bdb2a421\004bd941_fee1d501\MyDll.dll 
Parent Class:    717f17cc 
Module:          201fcfb0 
Method Table:    256db60c 
Vtable Slots:    17 
Total Method Slots:  17 
Class Attributes: 101   
Transparency:        Critical 
NumInstanceFields:   1 
NumStaticFields:     0
      MT    Field   Offset                 Type VT     Attr    Value Name 
71c9f54c  400055f      124        System.Char[]  0   shared   static enumSeperatorCharArray
     >> Domain:Value  09671520:NotInit  306d85b0:0d8e822c 306da248:NotInit  306d98c0:NotInit  306dc868:138690b0 << 
71ca0994  400007d        4         System.Int32  1 instance           value__

I have looked at the values in the appdomains but they are just commas.

0:000> !DumpObj /d 0d8e822c
Name:        System.Char[]
MethodTable: 71c9f54c
EEClass:     71874c84
Size:        14(0xe) bytes
Array:       Rank 1, Number of elements 1, Type Char (Print Array)
Content:     ,
Fields:
None

What do I need to do get how the enum was defined from the object on the heap?

EDIT1: I have access to the PDBs if that makes a difference.


Solution

  • Simple things first:

    0:000> !DumpObj /d 0d8e822c
    [...]
    Content:     ,    
    

    What you did here is: list the values of enumSeperatorCharArray. It's not relevant to your enum definition. All enums have it.

    SOS IMHO doesn't have a way to list the enum definitions. You need for that.

    Here's the debugging session:

    0:006> .loadby sos clr
    0:006> .load D:\mylongpath\sosex.dll
    
    0:006> !dumpheap -type YourEnum
     Address       MT     Size
    02cf2480 01154dc4       12     
    
    Statistics:
          MT    Count    TotalSize Class Name
    01154dc4        1           12 DebuggingEnumDefinition.YourEnum
    Total 1 objects
    

    So there is one object, and it's possible to look at it like you did. At the very end of the output, you can see its decimal value, which is 65 and not very helpful.

    0:006> !DumpObj /d 02cf2480
    Name:        DebuggingEnumDefinition.YourEnum
    [...]
    61bf42a8  4000001        4         System.Int32  1 instance       65 value__
    

    With SOSEX' !mdt, you can list the enumeration constants:

    0:006> !mdt DebuggingEnumDefinition.YourEnum
    DebuggingEnumDefinition.YourEnum
        [s]enumSeperatorCharArray: char[]
            AppDomain 'DebuggingEnumDefinition.exe' (00c8dc18): <uninitialized>
        [s]enumSeperator: string
            AppDomain 'DebuggingEnumDefinition.exe' (00c8dc18): <Field def not loaded>
        value__: int
        [s]EnumVal1: DebuggingEnumDefinition.YourEnum
            AppDomain 'DebuggingEnumDefinition.exe' (00c8dc18): <Field def not loaded>
        [s]EnumVal2: DebuggingEnumDefinition.YourEnum
            AppDomain 'DebuggingEnumDefinition.exe' (00c8dc18): <Field def not loaded>
        [s]EnumVal3: DebuggingEnumDefinition.YourEnum
            AppDomain 'DebuggingEnumDefinition.exe' (00c8dc18): <Field def not loaded>
    

    Actually I have expected the numerical values as well.

    You can also use !mdt with the object's address, so you get its constant and hexadecimal value (0x41 == 65):

    0:006> !mdt 02cf2480
    0x41 (EnumVal3) (DebuggingEnumDefinition.YourEnum)
    

    By changing the process memory, you could probably generate a mapping of the enum values.

    The following generates a loop from 0 to 127:

    .for (r $t0=0; @$t0<0n128; r $t0 = @$t0+1) { }
    

    Inside the loop, you can change the enum's value (I forgot whether +4 is bitness dependent):

    ed 03402470+4 @$t0
    

    Next, you could use !mdt to analyze the modified value

    !mdt 03402470
    

    The full command is

    .for (r $t0=0; @$t0<0n128; r $t0 = @$t0+1) { ed 03402470+4 @$t0; !mdt 03402470}
    

    and the output will look like

    0x0 (EnumVal1) (DebuggingEnumDefinition.YourEnum)
    0x1 (EnumVal2) (DebuggingEnumDefinition.YourEnum)
    0x2 (DebuggingEnumDefinition.YourEnum)
    0x3 (DebuggingEnumDefinition.YourEnum)
    [...]
    0x7f (DebuggingEnumDefinition.YourEnum)
    

    Next, you probably to filter only the valid cases. Note that we can distinguish valid entries from invalid ones by containing ) (.

    This is where scripting in WinDbg gets a bit ugly...

    .echo Just a test
    

    displays something to show the principle.

    .shell -ci ".echo Just a test" findstr "Just"
    

    uses the DOS command findstr to filter lines that contain a specific word.

    Next, replace the .echo with the full command from before and replace "Just" by ) (. Because findstr is also a strange program, you actually need ).( because it would otherwise treat it as two separate search terms.

    .shell -ci ".for (r $t0=0; @$t0<0n128; r $t0 = @$t0+1) { ed 03402470+4 @$t0; !mdt 03402470}" findstr ").("
    

    And yippieh, the output is:

    0x0 (EnumVal1) (DebuggingEnumDefinition.YourEnum)
    0x1 (EnumVal2) (DebuggingEnumDefinition.YourEnum)
    0x41 (EnumVal3) (DebuggingEnumDefinition.YourEnum)
    .shell: Process exited
    

    What an adventure!


    The source code I used, just in case ...

    using System;
    using System.Collections;
    
    namespace DebuggingEnumDefinition
    {
        class Program
        {
            static void Main()
            {
                var somwehere = new ArrayList() { YourEnum.EnumVal3 };
                Console.WriteLine("There should be an enum on the heap now.");
                Console.ReadLine();
                Console.WriteLine(somwehere[0]); // Just fix the unused variable issue
            }
        }
    
        enum YourEnum
        {
            EnumVal1,
            EnumVal2,
            EnumVal3=65
        }
    }