非托管内存中托管类标头的布局和大小是多少? [英] What are layout and size of a managed class's header in unmanaged memory?

查看:31
本文介绍了非托管内存中托管类标头的布局和大小是多少?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近,在这个问题,我问过如何在 C# 中获取类的原始内存地址(这是一个粗略的不可靠的黑客和不好的做法,除非你真的需要它,否则不要使用它).我成功了,但随后出现了一个问题:根据 这篇文章,类原始内存表示中的前2个字应该是指向SyncBlock和RTTI结构的指针,因此第一个字段的地址必须偏移2个字 [8 字节在 32 位系统中,16 字节在 64 位系统中] 从一开始.但是,当我从对象位置的内存中转储第一个字节时,第一个字段相对于对象地址的原始偏移量只有 1 个 32 位字(4 个字节),这不会对两种类型的系统都没有任何意义.从我链接的问题:

class 程序{//这里是函数.//我建议查看原始问题的解决方案,因为它是//更可靠.静态 IntPtr getPointerToObject(Object unmanagedObject){GCHandle gcHandle = GCHandle.Alloc(unmanagedObject, GCHandleType.WeakTrackResurrection);IntPtr thePointer = Marshal.ReadIntPtr(GCHandle.ToIntPtr(gcHandle));gcHandle.Free();返回指针;}类测试类{uint a = 0xDEADBEEF;}static void Main(string[] args){字节[] cls = 新字节[16];var test = new TestClass();var thePointer = getPointerToObject(test);Marshal.Copy(thePointer, cls, 0, 16);//转储前16个字节...Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(thePointer.ToInt32())));Console.WriteLine(BitConverter.ToString(cls));Console.ReadLine();gcHandle.Free();}}/* 示例输出(你的应该不同):40-23-CA-024C-38-04-01-EF-BE-AD-DE-00-00-00-80-B4-21-50-73该字段的值为EF-BE-AD-DE",0xDEADBEEF,因为它存储在内存中.是的,我们找到了!*/

为什么会这样?也许我只是把地址弄错了,但是怎么做以及为什么?如果我没有,那又有什么问题呢?也许,如果那篇文章是错误的,我只是误解了托管类标题的样子?或者它可能没有那个 Lock 指针——但是为什么以及如何可能呢?..

(这些显然只是几个可能的选项,虽然我仍然会仔细检查我能预测的每个选项,但胡乱猜测无法在时间和准确性上与正确答案进行比较.)

解决方案

@HansPassant 出色地指出,所讨论对象的指针指向了第二个结构,即方法表.现在,出于性能原因,这完全有意义,因为方法表(RTTI 结构)的使用频率远高于 SyncRoot 结构,因此,SyncRoot 结构仍然位于它之前的负索引 -1 处.他明确表示他不想发布这个答案,所以我自己发布了,但功劳仍归于他.

但我想提醒的是,这是一个肮脏的不可靠 hack,可能会使系统不稳定:

<块引用>

除了固定问题之外,其他令人讨厌的问题是不知道对象有多长以及字段是如何排列的.

您应该改用调试器,除非您了解所有后果,准确了解您要做什么并且确实需要这样做 - 使用这种肮脏且不可靠的方式.>

Recently, in this question, I've asked how to get a raw memory address of class in C# (it is a crude unreliable hack and a bad practice, don't use it unless you really need it). I've succeeded, but then a problem arose: according to this article, first 2 words in the class raw memory representation should be pointers to SyncBlock and RTTI structures, and therefore the first field's address must be offset by 2 words [8 bytes in 32-bit systems, 16 bytes in 64-bit systems] from the beginning. However, when I dump first bytes from memory at the object location, first field's raw offset from the object's address is only 1 32-bit word (4 bytes), which doesn't make any sense for both types of systems. From the question I've linked:

class Program
{
    // Here is the function.
    // I suggest looking at the original question's solution, as it is 
    // more reliable.
    static IntPtr getPointerToObject(Object unmanagedObject)
    {
        GCHandle gcHandle = GCHandle.Alloc(unmanagedObject, GCHandleType.WeakTrackResurrection);
        IntPtr thePointer = Marshal.ReadIntPtr(GCHandle.ToIntPtr(gcHandle));
        gcHandle.Free();
        return thePointer;
    }
    class TestClass
    {
        uint a = 0xDEADBEEF;
    }
    static void Main(string[] args)
    {
        byte[] cls = new byte[16];

        var test = new TestClass();

        var thePointer = getPointerToObject(test);
        Marshal.Copy(thePointer, cls, 0, 16); //Dump first 16 bytes...
        Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(thePointer.ToInt32())));
        Console.WriteLine(BitConverter.ToString(cls));

        Console.ReadLine();

        gcHandle.Free();
    }
}
/* Example output (yours should be different):
40-23-CA-02
4C-38-04-01-EF-BE-AD-DE-00-00-00-80-B4-21-50-73

That field's value is "EF-BE-AD-DE", 0xDEADBEEF as it is stored in memory. Yay, we found it!
*/

Why is so? Maybe I've just got the address wrong, but how and why? And if I didn't, what could be wrong anyway? Maybe, if that article is wrong, I simply misunderstood what managed class header looks like? Or maybe it doesn't have that Lock pointer - but why and how is this possible?..

(These are, obviously, only a few possible options, and, while I'm still going to carefully check each one I can predict, wild guessing cannot compare in both time and accuracy to a correct answer.)

解决方案

@HansPassant brilliantly pointed out that the pointer for the object in question points to the second structure, the method table. Now that totally makes sense for performance reasons, as the method table (RTTI structure) is used far more often than the SyncRoot structure, which, therefore, is still located right before it at the negative index -1. He made it clear that he doesn't want to post this answer so I'm posting it myself, but the credit still goes to him.

But I would like to remind that this is a dirty unreliable hack, possibly making the system unstable:

Beyond the pinning problem, other nasty issues are not having any idea how long the object is and how the fields are arranged.

You should use the debugger instead, unless you understand all the consequences, understand exactly what you are trying to do and really need to do it - using this, dirty and unreliable, way.

这篇关于非托管内存中托管类标头的布局和大小是多少?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆