2015-11-19 63 views
4

如何在C#中找到指向託管的類的原始指針,並且希望它在內存中的原始大小?顯然,這是CLR所不允許的 - 更準確地說,嚴格禁止,因爲託管類的非託管表示永遠不應該出於穩定和安全的原因 - 所以我正在尋找黑客。我不在尋找序列化 - 我實際上需要一個託管類的轉儲,因爲它在原始內存中表示。如何獲取原始內存指針到託管類?

更確切地說,我在下面的例子中尋找類似功能getObjectPtr

IntPtr getObjectPtr(Object managedClass) {...} 

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

提前感謝!

編輯: 我終於找到了解決辦法我自己,而且,回來的時候將它張貼作爲一個答案,是由的所謂快速量完全驚訝已經張貼的答案...感謝所有你的!這非常快,而且完全出乎意料。

最接近我的解決方案是@ thehennyy的一個,但我沒有發佈它,因爲@Chino提出了更好的一個(抱歉,我錯誤地認爲它是錯誤的,我忘記解除引用指針再次)。它不需要代碼是不安全的,多一點容忍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,現在我是一個有點無能。根據this文章,類中的前2個地址應該是指向SyncBlock和RTTI結構的指針,因此第一個字段的地址必須偏移2個字[32位系統中的8個字節,64位系統中的16個字節]從一開始就。我的是64位;但是,正如您在輸出中看到的那樣,顯然第一個字段的對象地址的原始偏移量僅爲4個字節,這沒有任何意義。

我已經問這個作爲separate question也許我應該問這是一個單獨的問題,但有可能是我的解決方案有錯誤。

+0

簡短聲明:我通過google搜索找到任何東西,因爲搜索結果通常是指像「C++指針」和「教程C#不安全傻瓜」,我我沒有在自己的C#中找到合適的方法(包括查看元帥課程)。我很確定這個問題以前應該已經問過了,但是對於「原始C#指針」,「C#指向類的指針」,「指向類內存的原始指針」和「原始指向託管類的指針」,SO沒有得到任何結果。 – DeFazer

+0

一般來說,你不能這樣做,因爲在你的傾銷中間GC可以將你的對象移動到其他位置。 –

+0

也許,你可以使用'var h = GCHandle.Alloc(obj); var address =(IntPtr)h;'。 –

回答

3

嘿這是你想要什麼?:

GCHandle gcHandle = GCHandle.Alloc(yourObject,GCHandleType.WeakTrackResurrection); 
IntPtr thePointer = GCHandle.ToIntPtr(gcHandle); 
+0

您的答案甚至不需要代碼爲'不安全',因爲以前接受的代碼!對不起,我一開始沒有注意到它。當我第一次測試你的解決方案時,我認爲這是錯誤的,但是當我決定寫評論時,我已經跑了幾次測試來確定確切的錯誤,並且突然意識到我忘記了再次解除引用它:'GCHandle gcHandle = GCHandle.Alloc(test,GCHandleType.WeakTrackResurrection); IntPtr thePointer = Marshal.ReadIntPtr(GCHandle.ToIntPtr(gcHandle)); Marshal.Copy(thePointer,cls,0,16); WriteAsHexArray(cls);' 謝謝!它更快更好! – DeFazer

2

您可以編寫一個泄漏對象地址的小IL功能。

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); 

來源是:IllidanS4/SharpUtils /UnsafeTools.cs

+0

謝謝!它很整潔,但是IL Emiter的這種使用卻導致了一些後果。第一次嘗試時,我不得不啓用不安全的代碼,否則在運行代理時拋出「運行時可能處於不穩定狀態」異常。我不記得它的確切名稱,也不能複製它,因爲現在代碼可以順利運行,不會出現「不安全」和任何異常情況。也許它使用存儲在緩存中某個地方的編譯函數,也許它確實接受這個DynamicMethod是一個安全和驗證...有很多選項,但事實是這個解決方案是不穩定的。 – DeFazer

+0

@DeFazer由於O和*之間的轉換,此方法無法驗證。嘗試使用我的庫中的版本,它應該繞過可驗證性檢查。 – IllidanS4