Dot-Net

如何在純 .net 中使 .NET 公共語言執行時 (CLR) 崩潰

  • February 14, 2019

有一個針對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。

引用自:https://stackoverflow.com/questions/1717701