How to get a raw memory pointer to a managed class

2019-04-09 22:42发布

How do I find a raw pointer to a managed class in C#, and, hopefully, it's raw size in memory? Obviously, this is not allowed by CLR - more precisely, strictly prohibited, as unmanaged representation of managed classes should never, ever be worked with for both stability and safe reasons - so I'm looking for a hack. I'm not looking for serializing - I do actually need a dump of managed class as it is represented in raw memory.

More precisely, I'm looking for something like function getObjectPtr in the following example:

IntPtr getObjectPtr(Object managedClass) {...}

void main() {
    var test=new TestClass();
    IntPtr* ptr_to_test=getObjectPtr(test);
    Console.WriteLine(ptr_to_test.ToString());
}

Thanks in advance!

EDIT: I've finally found a solution by myself, and, when came back to post it as an answer, was totally surprised by the amount of so-quickly already posted answers... Thanks to all of you! This was very quick and totally unexpected.

The closest to mine solution was @thehennyy's one, but I'm not posting it since @Chino proposed far better one (sorry I've mistaken it to be wrong at first, I've just forgot to dereference the pointer again). It does not require a code to be unsafe and a bit more tolerates GC:

class Program
{
    // Here is the function in case anyone needs it.
    // Note, though, it does not preserve the handle while you work with
    // pointer, so it is less reliable than the code in Main():
    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();

        GCHandle gcHandle = GCHandle.Alloc(test, GCHandleType.WeakTrackResurrection);
        IntPtr thePointer = Marshal.ReadIntPtr(GCHandle.ToIntPtr(gcHandle));
        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!
*/

Hovewer, now I'm a bit clueless. According to the this article, first 2 addresses in the class should be pointers to SyncBlock and RTTI structure, 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. Mine is 64-bit; however, as you can see in the output, it is obvious that first field's raw offset from the object's address is only 4 bytes, which doesn't make any sense.

I've asked this as a separate question. Maybe I should ask this as a separate question, but it is possible that there is an error in my solution.

2条回答
女痞
2楼-- · 2019-04-09 23:27

You can write a small IL function that leaks an objects address.

var o = new object();

var d = new DynamicMethod("GetPtr", typeof(IntPtr), new Type[] {typeof(object)}, Assembly.GetExecutingAssembly().ManifestModule);
var il = d.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ret);

var address = (IntPtr)d.Invoke(null, new object[] {o});
Console.WriteLine(address);

The source is: IllidanS4 / SharpUtils / UnsafeTools.cs

查看更多
手持菜刀,她持情操
3楼-- · 2019-04-09 23:28

Hey is this what you want?:

GCHandle gcHandle = GCHandle.Alloc(yourObject,GCHandleType.WeakTrackResurrection);
IntPtr thePointer = GCHandle.ToIntPtr(gcHandle);  
查看更多
登录 后发表回答