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.