Get Memory Address of .NET Object (C#)

后端 未结 5 2000
醉酒成梦
醉酒成梦 2021-01-02 03:05

I am trying to track down a bug in the mono runtime where a variable appears to be allocated to one valid object, and then is reassigned later to a bogus object, specificall

相关标签:
5条回答
  • 2021-01-02 03:33

    You can't get the address of a manged object in managed code, in general. If the object has a field like an int, you could take it's address with the fixed C# statement and then you'd have a pointer inside the object. For debugging purposes, you could make some assumptions and get the offset to the base pointer of the object (on 32 bit platforms the object header size on mono is 8 bytes, 16 bytes on 64 bit architectures, at this time).

    Your bug report claims you're using the Boehm collector, though, and that collector doesn't move objects in memory, the bug could be caused by some unrelated memory corruption, by the object being incorrectly freed or some other logic bug in the GC (I'm not sure the zero size you pointed out is relevant, since a managed object has at least the 8-16 byte header).

    0 讨论(0)
  • 2021-01-02 03:35

    My alternatives... Also @ This similar question

    #region AddressOf
    
        /// <summary>
        /// Provides the current address of the given object.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static System.IntPtr AddressOf(object obj)
        {
            if (obj == null) return System.IntPtr.Zero;
    
            System.TypedReference reference = __makeref(obj);
    
            System.TypedReference* pRef = &reference;
    
            return (System.IntPtr)pRef; //(&pRef)
        }
    
        /// <summary>
        /// Provides the current address of the given element
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static System.IntPtr AddressOf<T>(T t)
            //refember ReferenceTypes are references to the CLRHeader
            //where TOriginal : struct
        {
            System.TypedReference reference = __makeref(t);
    
            return *(System.IntPtr*)(&reference);
        }
    
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        static System.IntPtr AddressOfRef<T>(ref T t)
        //refember ReferenceTypes are references to the CLRHeader
        //where TOriginal : struct
        {
            System.TypedReference reference = __makeref(t);
    
            System.TypedReference* pRef = &reference;
    
            return (System.IntPtr)pRef; //(&pRef)
        }
    
        /// <summary>
        /// Returns the unmanaged address of the given array.
        /// </summary>
        /// <param name="array"></param>
        /// <returns><see cref="IntPtr.Zero"/> if null, otherwise the address of the array</returns>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static System.IntPtr AddressOfByteArray(byte[] array)
        {
            if (array == null) return System.IntPtr.Zero;
    
            fixed (byte* ptr = array)
                return (System.IntPtr)(ptr - 2 * sizeof(void*)); //Todo staticaly determine size of void?
        }
    
        #endregion
    
    0 讨论(0)
  • 2021-01-02 03:38

    Turns out this is not possible in .NET directly, but can be accomplished by altering the mono runtime code. To create a C# method that can read the memory address, make the following changes to the mono source code:

    Alter gc-internal.h to add

    gpointer    ves_icall_System_GCHandle_GetAddrOfObject (MonoObject *obj) MONO_INTERNAL;
    

    Alter gc.c to add:

    gpointer    ves_icall_System_GCHandle_GetAddrOfObject (MonoObject *obj) {
        return (char*)obj;
    }
    

    Alter GCHandle.cs to add:

    MethodImplAttribute(MethodImplOptions.InternalCall)]
    private extern static IntPtr GetAddrOfObject(object obj);
    
    public static IntPtr AddrOfObject(object o)
    {
        IntPtr res = GetAddrOfObject(o);
        return res;
    }
    

    Alter icall-def.h to add

    ICALL(GCH_6, "GetAddrOfObject", ves_icall_System_GCHandle_GetAddrOfObject)
    

    Note that these must be in order, so add it above the GetAddrOfPinnedObject line Rebuild

    Finally, call it from C#

    for (int i = 0; i < 100; i++) {
        object o = new object ();
        var ptr = GCHandle.AddrOfObject (o);
        Console.WriteLine ("Address: " + ptr.ToInt64().ToString ("x"));
    }
    
    0 讨论(0)
  • 2021-01-02 03:48

    There is a quick way to view the memory address allocated to a variable is:

    Code

    string s1 = "Hello World";
    GCHandle gch = GCHandle.Alloc(s1, GCHandleType.Pinned);
    IntPtr pObj = gch.AddrOfPinnedObject();
    Console.WriteLine($"Memory address:{pObj.ToString()}");
    

    Output

    Memory address:45687608
    

    Explanation

    The method GCHandle.AddrOfPinnedObject retrieves the address of an object in a Pinned handle.

    Disassembly

    You can view EVERY memory address allocated to each method and variable you should analize the JIT-compiled code with the Disassembly window in Visual Studio.

    Enable the Disassembly by selecting Enable address-level debugging, under Tools > Options > Debugging > General.

    Set the a brake point at the beginning of the application and start the debug. Once the application hit the brake-point open the Disassembly window by selecting Debug > Windows > Disassembly.

    --- C:\Users\Ivan Porta\source\repos\ConsoleApp1\Program.cs --------------------
            {
    0066084A  in          al,dx  
    0066084B  push        edi  
    0066084C  push        esi  
    0066084D  push        ebx  
    0066084E  sub         esp,4Ch  
    00660851  lea         edi,[ebp-58h]  
    00660854  mov         ecx,13h  
    00660859  xor         eax,eax  
    0066085B  rep stos    dword ptr es:[edi]  
    0066085D  cmp         dword ptr ds:[5842F0h],0  
    00660864  je          0066086B  
    00660866  call        744CFAD0  
    0066086B  xor         edx,edx  
    0066086D  mov         dword ptr [ebp-3Ch],edx  
    00660870  xor         edx,edx  
    00660872  mov         dword ptr [ebp-48h],edx  
    00660875  xor         edx,edx  
    00660877  mov         dword ptr [ebp-44h],edx  
    0066087A  xor         edx,edx  
    0066087C  mov         dword ptr [ebp-40h],edx  
    0066087F  nop  
                Sealed sealedClass = new Sealed();
    00660880  mov         ecx,584E1Ch  
    00660885  call        005730F4  
    0066088A  mov         dword ptr [ebp-4Ch],eax  
    0066088D  mov         ecx,dword ptr [ebp-4Ch]  
    00660890  call        00660468  
    00660895  mov         eax,dword ptr [ebp-4Ch]  
    00660898  mov         dword ptr [ebp-3Ch],eax  
                sealedClass.DoStuff();
    0066089B  mov         ecx,dword ptr [ebp-3Ch]  
    0066089E  cmp         dword ptr [ecx],ecx  
    006608A0  call        00660460  
    006608A5  nop  
                Derived derivedClass = new Derived();
    006608A6  mov         ecx,584F3Ch  
    006608AB  call        005730F4  
    006608B0  mov         dword ptr [ebp-50h],eax  
    006608B3  mov         ecx,dword ptr [ebp-50h]  
    006608B6  call        006604A8  
    006608BB  mov         eax,dword ptr [ebp-50h]  
    006608BE  mov         dword ptr [ebp-40h],eax  
                derivedClass.DoStuff();
    006608C1  mov         ecx,dword ptr [ebp-40h]  
    006608C4  mov         eax,dword ptr [ecx]  
    006608C6  mov         eax,dword ptr [eax+28h]  
    006608C9  call        dword ptr [eax+10h]  
    006608CC  nop  
                Base BaseClass = new Base();
    006608CD  mov         ecx,584EC0h  
    006608D2  call        005730F4  
    006608D7  mov         dword ptr [ebp-54h],eax  
    006608DA  mov         ecx,dword ptr [ebp-54h]  
    006608DD  call        00660490  
    006608E2  mov         eax,dword ptr [ebp-54h]  
    006608E5  mov         dword ptr [ebp-44h],eax  
                BaseClass.DoStuff();
    006608E8  mov         ecx,dword ptr [ebp-44h]  
    006608EB  mov         eax,dword ptr [ecx]  
    006608ED  mov         eax,dword ptr [eax+28h]  
    006608F0  call        dword ptr [eax+10h]  
    006608F3  nop  
            }
    0066091A  nop  
    0066091B  lea         esp,[ebp-0Ch]  
    0066091E  pop         ebx  
    0066091F  pop         esi  
    00660920  pop         edi  
    00660921  pop         ebp  
    
    00660922  ret  
    
    0 讨论(0)
  • 2021-01-02 03:50

    You should be able to use the GCHandle construct to accomplish this.

    GCHandle objHandle = GCHandle.Alloc(obj,GCHandleType.WeakTrackResurrection);
    int address = GCHandle.ToIntPtr(objHandle).ToInt32(); 
    

    Where 'obj' is the object whose address you're trying to get.

    0 讨论(0)
提交回复
热议问题