我没有在 64 位转储中这样做,只有 32 位转储,但希望这会对您有所帮助。
(我意识到这对你来说可能为时已晚,但希望它会对其他人有所帮助。)
根据我的经验,从 COM 对象到 .NET 对象的运行方式如下。因此,从 .NET 对象到 COM 对象是一种逆向过程。我使用的是真实转储中的示例,但经过审查和重命名。
从 COM 到 .NET 对象。
您的 COM 对象中包含一个字段,可能如下所示:
+0x30 m_iTestObject : ATL:CComPtr<ITestObject> { 0e645150 }
这是间接指向 clr!Unknown_QueryInterface 吗?如果不是,那就不是 CCW:
0:000> dds poi(0e645150) L1
0631c090 07466260 clr!Unknown_QueryInterface
好的,这是一个逆时针。我们回到原始地址 0e645150,此时我们必须检查一些偏移量,因为它们似乎在 .NET 运行时之间有所不同。到目前为止,我发现的三个偏移量是 -0x0c、-0x10 和 -0x14。显然,在 64 位下偏移量可能会有所不同,但您可以看到尝试不同的偏移量然后针对结果运行 !mdt 应该很容易:
0:000> dds poi(0e645150-0x0c) L1
2506a4c0 00000000
0:000> dds poi(0e645150-0x10) L1
01785bd8 08acc744
0:000> dds poi(0e645150-0x14) L1
00000000 ????????
我们现在可以对每个结果运行 !mdt (sosex) 或 !do。碰巧的是,其中只有一个实际上给了我们一个有效地址,即 08acc744:
0:000> !mdt 08acc744
08acc744 (TestAssembly.TestObject)
requests:08accba8 (System.Collections.Generic.List`1[[TestAssembly.TestRequest, TestAssembly]])
notifySink:08aee138 (TestAssembly.SinkWrapper)
回溯到指向这个 .NET 对象的 COM 对象:
我们从 08acc744 的同一个 .NET 对象开始并检查它的引用。 (您已经使用 !gcroot 来查找 RefCounted 句柄):
0:000> !refs 08acc744
Objects referenced by 08ACC744
NONE
Objects that reference 08ACC744 (TestAssembly.TestObject):
RefCounted Handle at 01785BD8, AppDomain=0456F158
好的,所以我们有了句柄。这是什么意思?
0:000> s -d 0x00000000 L?0xffffffff 01785bd8
0e645140 01785bd8 2506a4c0 0630ec60 00000000 .[x....%`.0.....
所以这个句柄是从 0e645140 指向的。我们知道,下一级不是直接指向这个地址,而是指向从这里开始的一个偏移量。我们能找到任何指向这些偏移地址的东西吗?
0:000> s -d 0x00000000 L?0xffffffff 0e645140+0x0c
0:000> s -d 0x00000000 L?0xffffffff 0e645140+0x10
27f90d68 0e645150 25d100e8 27fab0f8 00000000 PQd....%...'....
0:000> s -d 0x00000000 L?0xffffffff 0e645140+0x14
我们在 27f90d68 找到了一个指针,我们希望它是 COM 对象中的一个字段。让我们转储该地址周围的内存:
0:000> dds 27f90d68-0x40
27f90d28 0537f8b8
27f90d2c 00000000
27f90d30 559965bd
27f90d34 88000000
27f90d38 15ffe604 TestCore2!ATL::CComObject<CTestContainer>::`vftable'
27f90d3c 15ffe5e4 TestCore2!ATL::CComObject<CTestContainer>::`vftable'
27f90d40 00000002
27f90d44 0000000e
27f90d48 ffffffff
27f90d4c 0000000f
27f90d50 00000010
27f90d54 1dc721d8
27f90d58 00000000
27f90d5c 27d6ef80
27f90d60 262b1ec0
27f90d64 00000001
27f90d68 0e645150
27f90d6c 25d100e8
27f90d70 27fab0f8
27f90d74 00000000
27f90d78 559965b4
我们可以看到一个带有 vftable 的对象,该对象的起始地址稍早于我们的地址,因此希望我们的地址是该对象内部的一个字段。 (请注意,对象很可能在 vftable 条目之间存在间隙,ATL 接收器指针之类的东西可以驻留在其中,但碰巧这是对象的开始)。
0:000> dt 27f90d38 TestCore2!ATL::CComObject<CTestContainer>
+0x000 __VFN_table : 0x15ffe604
=16010f38 _tih : ATL::CComTypeInfoHolder
+0x004 __VFN_table : 0x15ffe5e4
=16010f58 _tih : ATL::CComTypeInfoHolder
+0x008 m_dwRef : 0n2
+0x008 m_pOuterUnknown : 0x00000002 IUnknown
+0x00c m_nStateOrdinal : 0n14
+0x010 m_nKeyOrdinal : 0n-1
+0x014 m_nErrorCodeOrdinal : 0n15
+0x018 m_nErrorTextOrdinal : 0n16
...
+0x30 m_iTestObject : ATL:CComPtr<ITestObject> { 0e645150 }
这样我们就一直从 .NET 对象回到指向它的 COM 对象。
希望对你有用。