How to crash the .NET common language runtime (CLR) in pure .net
There is a similar question targeting the Java VM but I haven't found a question for .net (please close and mark as duplicate if I was missing something).
So - is it possible without nasty unmanaged interop? And with crashing I me开发者_如何学Pythonan a real "xxx.exe has stopped working" not a StackOverflow- or OutOfMemoryException.
I think it is not possible, except when hitting a bug in the VM itself.
Well...how would you define "pure .NET"? I played with CLR2/delegate/GCHandle/array when I read the "how to crash JVM" post, and came up with something like this:
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( );
}
}
}
It's only known to work on 32-bit Windows XP with CLR2 on x86; and also known not to work with Vista and Windows 7 and the like, where DEP+ASLR is on by default.
The point of fun about the code above is that it didn't explicitly use unsafe code (although GCHandle.Alloc(..., GCHandleType.Pinned) demands security privilege), yet it manages to fake an array into a delegate instance, and calls into the x86 machine code within the array. The code itself is pure C#, if you don't count the embedded x86 code as some "foreign language" ;-) Basically it makes use of internal implementation of CLR2's delegates on static methods, that a few private members of Delegate are actually internal pointers. I stuffed x86 code into an array, which is allocated on the managed heap. So in order for this to work, DEP must not be enabled, or we'll have to find some other way to get execution privilege on that memory page.
The x86 code is like this: (in pseudo-MASM syntax)
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
This is NOT a behavior specified by CLI, and won't work on other CLI implementations such as Mono. There are other ways to make the similar logic run on Mono though, already tried that on Ubuntu 9.04 w/ Mono 2.4 and worked.
I wrote a blog post about it here: http://rednaxelafx.javaeye.com/blog/461787
It's in Chinese, but there's plenty of code there which should explain what I did. Using the same trick, at the end of the blog post I showed a couple of examples how you could tweak the code above to make things go wrong, such as getting a SEHException.
I've done that just today. I was testing a setup of a larger .net project. There was missing an assembly containing some interfaces and the exe just stops working. No exception was caught by the runtime.
You can be sure that there a even more bugs in the runtime - just count the millions of lines of code ...
Without the need to use unsafe code or delegates (also if i must admit it is a very nice way to crash your CLR), you can use simple Marshal functions to force .NET to crash.
using System;
using System.Runtime.InteropServices;
namespace Crash
{
class Program
{
static void Main(string[] args)
{
IntPtr p = Marshal.AllocHGlobal(1);
for (int i = 0; i < 10000000; ++i)
{
p = new IntPtr(p.ToInt64() + 1);
Marshal.WriteByte(p, 0xFF);
}
}
}
}
Also this, using always GCHandle, will cause a memory access violation like error.
using System;
using System.Runtime.InteropServices;
namespace Crash
{
class Program
{
static void Main(string[] args)
{
GCHandle.FromIntPtr(new IntPtr(32323));
}
}
}
Oren Eini discovered a bug in the .net framework that caused an 'ExecutionEngineException' - basically a crash of the runtime.
You can read about it here (Microsoft Connect):
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=384781
And despite the 'closed' state of the bug - it's not fixed yet.
I've seen Java programs crash the JVM by loading a non-existent class and ignoring ClassNotFoundError
, then continue executing as if nothing happened. Perhaps you could do something similar when dynamically loading a .NET class.
You can compile this code with /clr:pure and have the linker force pure option as well.
However, it crashes with what looks like a runtime failure;
(1388.5e4): Access violation - code c0000005 (!!! second chance !!!) eax=8d00fea5 ebx=00000000 ecx=00253e50 edx=00253e50 esi=022ad3cc edi=00253e50 eip=6ae965c5 esp=0020edbc ebp=0020edc8 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b
efl=00010246 * WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\eb4e1e70734f6efb9c7de7ec5f452c9e\mscorlib.ni.dll mscorlib_ni+0x9365c5: 6ae965c5 ff10
call dword ptr [eax]
ds:002b:8d00fea5=????????
EVEN though you compile with /clr:pure or /clr:safe, and the image represents it self as such, it will only work in full trust due to the verifier catching this bug (compiler bug).
namespace Settings
{
public ref class RefType
{
public:
unsigned int I;
String^ S;
unsigned long L;
};
public ref class aUseTemplate
{
public:
void CallTemplate()
{
array<RefType^>^ refarr = gcnew array<RefType^>(20);
for(int i=0; i < 20; i++)
{
RefType^ rt = gcnew RefType();
rt->I = 0x42424242;
rt->L = 0x33333333;
refarr[i] = rt;
}
HasTemplate(refarr);
}
template<typename T> void HasTemplate(T input)
{
for each(T% x in input)
Console::WriteLine(x);
}
};
}
This is from the output of peverify, which parses the entire PE file, this error is not detected until the runtime calls into this method, as the verifier works with the JIT'er and is lazy about it's verification.
[IL]: Error: [C:\Users\files\Documents\Visual Studio 2010\Projects\testCli\bin\Release\PureSettings.dll : Settings.aUseTemplate::HasTemplate^>][off set 0x00000017][found ref 'Settings.RefType'][expected address of ref ] Unexpected type on the stack. 1 Error(s) Verifying PureSettings.dll
If you could bypass the verifier here, this would be a HUGE BUG in the CLR and give you code execution from an unprivilaged process.
Here's the MSIL;
IL_000d: bge.s IL_0021
IL_000f: ldloc.1
IL_0010: ldloc.0
IL_0011: ldelem.ref
IL_0012: castclass Settings.RefType
IL_0017: stloc.2
IL_0018: ldloc.2
IL_0019: ldind.ref
IL_001a: call void [mscorlib]System.Console::WriteLine(object)
IL_001f: br.s IL_0006
IL_0021: ret
The bug is at offset, IL_19.
If you are running under CAS or any other type-safe mode, this code will generate the famous "code may destabalize the runtime" exception.
I think I have found another way which doesn't involve unmanaged code. However, it uses PInvoke. It worked when I added MethodImpl(MethodImplOptions.Unmanaged). I tested it on .Net Core 2.1 and on .Net Framework 4.7.2. The former crashed during the VS debug immediately, for the latter the debugger broke on exception with the message that the System.TypeLoadException is thrown somewhere in internals. The message and error code are the same as the ones I've got from the @Salvatore Previti answer. I called the compiled app from the command line and get error exit code -532462766. The code is below:
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
[MethodImpl(MethodImplOptions.Unmanaged)]
[DllImport("user32.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern void Foo();
public static void Main(string[] args)
{
try
{
Foo();
}
catch (Exception e)
{
}
Console.ReadLine();
}
}
}
I know you can crash your entire PC with .NET. But that involves an endless loop and a RealTime process priority...
There is some c# code that while technically correct won't run as a valid .net program. It has something todo with interface overloading an empty method but I can't really remember.
精彩评论