如何在純 .net 中使 .NET 公共語言執行時 (CLR) 崩潰
有一個針對Java VM的類似問題,但我沒有找到 .net 的問題(如果我遺漏了什麼,請關閉並標記為重複)。
那麼 - 沒有討厭的非託管互操作可能嗎?崩潰是指真正的“xxx.exe 已停止工作”,而不是 StackOverflow 或 OutOfMemoryException。
我認為這是不可能的,除非遇到 VM 本身的錯誤。
好吧…您如何定義“純 .NET”?當我閱讀“如何使 JVM 崩潰”文章時,我使用了 CLR2/delegate/GCHandle/array,並想出了這樣的東西:
using System; using System.Reflection; using System.Runtime.InteropServices; namespace TestCLR2Crash { static void Main( string[ ] args ) { // declare a delegate that refers to a static method, // in this case it's a static method generated from the // anonymous delegate. Action action = delegate( ) { }; // "generate" code into an array of uint var fakeDelegate = new uint[ ] { // dummy values 0x00000000, 0x00000000, // fake _methodPtrAux 0x00000000, // native code/string 0x6AEC8B55, 0x2FD9B8F5, 0xD0FF7C81, 0x006A006A, 0x00E81F6A, 0x83000000, 0x50102404, 0x81CC5DBA, 0x8BD2FF7C, 0x47C35DE5, 0x74656572, 0x73676E69, 0x6F726620, 0x6567206D, 0x6172656E, 0x20646574, 0x65646F63, 0x00000A21 }; // fill in the fake _methodPtrAux, // make it point to the code region in fakeDelegate var handle = GCHandle.Alloc( fakeDelegate, GCHandleType.Pinned ); var addr = handle.AddrOfPinnedObject( ); const int sizeOfUInt32 = sizeof( uint ); // 4 const int indexOfCode = 3; fakeDelegate[ 2 ] = Convert.ToUInt32( addr.ToInt32( ) + sizeOfUInt32 * indexOfCode ); var targetInfo = typeof( Action ) .GetField( "_target", BindingFlags.NonPublic | BindingFlags.Instance ); targetInfo.SetValue( action, fakeDelegate ); action( ); // Greetings from generated code! Console.WriteLine( "Greetings from managed code!" ); handle.Free( ); } } }它只知道在 x86 上使用 CLR2 的 32 位 Windows XP 上工作;並且還已知不適用於 Vista 和 Windows 7 等,預設情況下 DEP+ASLR 處於啟用狀態。
上面程式碼的有趣之處在於它沒有明確使用不安全程式碼(儘管 GCHandle.Alloc(…, GCHandleType.Pinned) 需要安全權限),但它設法將數組偽裝成委託實例,並且呼叫數組中的 x86 機器程式碼。程式碼本身是純 C#,如果您不將嵌入式 x86 程式碼算作某種“外語”;-) 基本上它利用 CLR2 代理在靜態方法上的內部實現,即 Delegate 的一些私有成員實際上是內部的指針。我將 x86 程式碼填充到一個數組中,該數組分配在託管堆上。所以為了讓它工作,DEP不能被啟用,否則我們必須找到其他方法來獲得該記憶體頁面的執行權限。
x86 程式碼是這樣的:(在偽 MASM 語法中)
55 push ebp 8BEC mov ebp,esp 6A F5 push -0B ; /DevType = STD_OUTPUT_HANDLE B8 D92F817C mov eax,KERNEL32.GetStdHandle ; | FFD0 call eax ; \GetStdHandle 6A 00 push 0 ; /pReserved = NULL 6A 00 push 0 ; |pWritten = NULL 6A 1F push 1F ; |CharsToWrite = 1F (31.) E8 00000000 call <&next_instruction> ; | 830424 10 add dword ptr ss:[esp],10 ; |Buffer 50 push eax ; |hConsole BA 5DCC817C mov edx,KERNEL32.WriteConsoleA ; | FFD2 call edx ; \WriteConsoleA 8BE5 mov esp,ebp 5D pop ebp C3 ret這不是 CLI 指定的行為,並且不適用於其他 CLI 實現,例如 Mono。不過,還有其他方法可以讓類似的邏輯在 Mono 上執行,已經在 Ubuntu 9.04 w/Mono 2.4 上嘗試過並且有效。
我在這裡寫了一篇關於它的部落格文章:http ://rednaxelafx.javaeye.com/blog/461787
它是中文的,但是那裡有很多程式碼可以解釋我的所作所為。使用相同的技巧,在部落格文章的末尾,我展示了幾個範例,您可以如何調整上面的程式碼以使事情出錯,例如獲得 SEHException。