如何使用 SOS 和 Windbg 从内存转储中列出枚举的值?
How do I list the values of an enum from a memory dump using SOS and Windbg?
我有一个已将 windbg 附加到的小型转储。小型转储来自 IIS 上的 .NET 4.6.1 ASP.NET 站点 运行。我想获得我的枚举的定义,但每当我获得 Class 的 MethodTable 时,我只会得到以下内容。
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\root3e164d6bec4f\assembly\dl3\bdb2a421[=10=]4bd941_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\root3e164d6bec4f\assembly\dl3\bdb2a421[=10=]4bd941_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__
我查看了 appdomains 中的值,但它们只是逗号。
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
我需要做什么来获取枚举是如何从堆上的对象定义的?
编辑1:
如果这有所不同,我可以访问 PDB。
简单的事情优先:
0:000> !DumpObj /d 0d8e822c
[...]
Content: ,
您在这里所做的是:列出 enumSeperatorCharArray
的值。它与您的枚举定义无关。所有枚举都有它。
恕我直言,SOS 无法列出枚举定义。为此,您需要 sosex。
这是调试会话:
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
所以只有一个物体,可以像您一样观察它。在输出的最后,您可以看到它的十进制值,即 65,这不是很有帮助。
0:006> !DumpObj /d 02cf2480
Name: DebuggingEnumDefinition.YourEnum
[...]
61bf42a8 4000001 4 System.Int32 1 instance 65 value__
用SOSEX'!mdt
,可以列出枚举常量:
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>
其实我也预料到了数值。
您还可以将 !mdt
与对象的地址一起使用,这样您就可以得到它的常量和十六进制值 (0x41 == 65):
0:006> !mdt 02cf2480
0x41 (EnumVal3) (DebuggingEnumDefinition.YourEnum)
通过更改进程内存,您可能可以生成枚举值的映射。
下面生成一个从 0 到 127 的循环:
.for (r $t0=0; @$t0<0n128; r $t0 = @$t0+1) { }
在循环内部,您可以更改枚举的值(我忘了 +4 是否依赖于位数):
ed 03402470+4 @$t0
接下来可以使用!mdt
分析修改后的值
!mdt 03402470
完整的命令是
.for (r $t0=0; @$t0<0n128; r $t0 = @$t0+1) { ed 03402470+4 @$t0; !mdt 03402470}
并且输出看起来像
0x0 (EnumVal1) (DebuggingEnumDefinition.YourEnum)
0x1 (EnumVal2) (DebuggingEnumDefinition.YourEnum)
0x2 (DebuggingEnumDefinition.YourEnum)
0x3 (DebuggingEnumDefinition.YourEnum)
[...]
0x7f (DebuggingEnumDefinition.YourEnum)
接下来,您可能只过滤有效案例。请注意,我们可以通过包含 ) (
.
来区分有效条目和无效条目
这就是 WinDbg 中的脚本变得有点丑陋的地方...
.echo Just a test
展示一些东西来说明原理。
.shell -ci ".echo Just a test" findstr "Just"
使用 DOS 命令 findstr
过滤包含特定单词的行。
接下来,将 .echo
替换为之前的完整命令,并将 "Just" 替换为 ) (
。因为 findstr
也是一个奇怪的程序,所以您实际上需要 ).(
,否则它会将其视为两个单独的搜索词。
.shell -ci ".for (r $t0=0; @$t0<0n128; r $t0 = @$t0+1) { ed 03402470+4 @$t0; !mdt 03402470}" findstr ").("
yippieh,输出是:
0x0 (EnumVal1) (DebuggingEnumDefinition.YourEnum)
0x1 (EnumVal2) (DebuggingEnumDefinition.YourEnum)
0x41 (EnumVal3) (DebuggingEnumDefinition.YourEnum)
.shell: Process exited
多么冒险!
我用的源码,以防万一...
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
}
}
我有一个已将 windbg 附加到的小型转储。小型转储来自 IIS 上的 .NET 4.6.1 ASP.NET 站点 运行。我想获得我的枚举的定义,但每当我获得 Class 的 MethodTable 时,我只会得到以下内容。
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\root3e164d6bec4f\assembly\dl3\bdb2a421[=10=]4bd941_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\root3e164d6bec4f\assembly\dl3\bdb2a421[=10=]4bd941_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__
我查看了 appdomains 中的值,但它们只是逗号。
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
我需要做什么来获取枚举是如何从堆上的对象定义的?
编辑1: 如果这有所不同,我可以访问 PDB。
简单的事情优先:
0:000> !DumpObj /d 0d8e822c
[...]
Content: ,
您在这里所做的是:列出 enumSeperatorCharArray
的值。它与您的枚举定义无关。所有枚举都有它。
恕我直言,SOS 无法列出枚举定义。为此,您需要 sosex。
这是调试会话:
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
所以只有一个物体,可以像您一样观察它。在输出的最后,您可以看到它的十进制值,即 65,这不是很有帮助。
0:006> !DumpObj /d 02cf2480
Name: DebuggingEnumDefinition.YourEnum
[...]
61bf42a8 4000001 4 System.Int32 1 instance 65 value__
用SOSEX'!mdt
,可以列出枚举常量:
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>
其实我也预料到了数值。
您还可以将 !mdt
与对象的地址一起使用,这样您就可以得到它的常量和十六进制值 (0x41 == 65):
0:006> !mdt 02cf2480
0x41 (EnumVal3) (DebuggingEnumDefinition.YourEnum)
通过更改进程内存,您可能可以生成枚举值的映射。
下面生成一个从 0 到 127 的循环:
.for (r $t0=0; @$t0<0n128; r $t0 = @$t0+1) { }
在循环内部,您可以更改枚举的值(我忘了 +4 是否依赖于位数):
ed 03402470+4 @$t0
接下来可以使用!mdt
分析修改后的值
!mdt 03402470
完整的命令是
.for (r $t0=0; @$t0<0n128; r $t0 = @$t0+1) { ed 03402470+4 @$t0; !mdt 03402470}
并且输出看起来像
0x0 (EnumVal1) (DebuggingEnumDefinition.YourEnum)
0x1 (EnumVal2) (DebuggingEnumDefinition.YourEnum)
0x2 (DebuggingEnumDefinition.YourEnum)
0x3 (DebuggingEnumDefinition.YourEnum)
[...]
0x7f (DebuggingEnumDefinition.YourEnum)
接下来,您可能只过滤有效案例。请注意,我们可以通过包含 ) (
.
这就是 WinDbg 中的脚本变得有点丑陋的地方...
.echo Just a test
展示一些东西来说明原理。
.shell -ci ".echo Just a test" findstr "Just"
使用 DOS 命令 findstr
过滤包含特定单词的行。
接下来,将 .echo
替换为之前的完整命令,并将 "Just" 替换为 ) (
。因为 findstr
也是一个奇怪的程序,所以您实际上需要 ).(
,否则它会将其视为两个单独的搜索词。
.shell -ci ".for (r $t0=0; @$t0<0n128; r $t0 = @$t0+1) { ed 03402470+4 @$t0; !mdt 03402470}" findstr ").("
yippieh,输出是:
0x0 (EnumVal1) (DebuggingEnumDefinition.YourEnum)
0x1 (EnumVal2) (DebuggingEnumDefinition.YourEnum)
0x41 (EnumVal3) (DebuggingEnumDefinition.YourEnum)
.shell: Process exited
多么冒险!
我用的源码,以防万一...
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
}
}