13

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 mean 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.

Community
  • 1
  • 1
Marc Wittke
  • 2,991
  • 2
  • 30
  • 45
  • 2
    The whole point of the .NET CLR is to abstract away from the computer, keeping stack overflows and other bugs inside of the CLR and not crashing your physical computer(even with a buggy OS) – Earlz Nov 11 '09 at 20:09
  • 1
    If such a thing is possible, doesn't it fall in the "bug in the VM itself" category, by definition? – Mehrdad Afshari Nov 11 '09 at 20:11
  • Yes.. unless you are talking about deleting important files and such from inside the CLR runtime – Earlz Nov 11 '09 at 20:13
  • 1
    I'm guessing you could do same nasty manipulation with raw pointers and information gained by pinning an object with `GCHandle`. – Sam Harwell Nov 11 '09 at 20:40
  • The GCHandle stuff sounds like an interesting way. But, does it pass the definition "pure .net without unmanaged interop" though? – Marc Wittke Nov 12 '09 at 07:20

9 Answers9

17

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.

RednaxelaFX
  • 221
  • 1
  • 4
  • I'm still unsure whether this is "pure .net" - the definition gets blurry. However, it's definitely a very nice idea +1! – Marc Wittke Nov 13 '09 at 05:27
4

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));
        }
    }
}
Salvatore Previti
  • 8,956
  • 31
  • 37
3

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 ...

Jan
  • 15,802
  • 5
  • 35
  • 59
2

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.

Adam
  • 4,159
  • 4
  • 32
  • 53
  • The bug report was closed because he found a workaround... so MS closed the report and never fixed the issue because no one else had the same problem. – Jon Seigel Nov 11 '09 at 20:16
  • ...and the problem itself stays in the dark without seeing the solution he provided. However, this would also be categorized as "bug in the CLR" – Marc Wittke Nov 12 '09 at 07:31
1

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.

David R Tribble
  • 11,918
  • 5
  • 42
  • 52
  • Since we cannot load classes in .net but just assemblies the situation is a little bit different. When we failed to load an assembly and swallowed the exception the only thing we could try is to find a class and/or method by reflection. But then the worst thing is another exception, either something related to reflection itself (e.g. TypeLoadException) or a NullReferenceException when some variable is expected to be not null after loading an assembly. To sum it up: I see no chance for this in .net. And if you're right the certain JVM is probably buggy on this. – Marc Wittke Nov 12 '09 at 07:27
  • It was a Sun JVM, but quite a few years ago, probably circa version 2.x. – David R Tribble Nov 12 '09 at 21:51
0

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();
        }
    }
}
SENya
  • 1,083
  • 11
  • 26
0

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.

RandomNickName42
  • 5,923
  • 1
  • 36
  • 35
-1

I know you can crash your entire PC with .NET. But that involves an endless loop and a RealTime process priority...

Vivelin
  • 770
  • 1
  • 10
  • 23
  • process priority - nice idea. But can we change the process priority from inside of the process? Or can we trigger another process, with higher priority than ours? well.... lets see... – Marc Wittke Nov 12 '09 at 07:33
  • Just tried it: a working process is endless looping and instantiating objects while it was started by another process using the ProcessPriorityClass.RealTime. Machine's performance is degraded, sure, but it does not crash. – Marc Wittke Nov 12 '09 at 07:43
  • You can set it using Process.GetCurrentProcess().ProcessPriority or something like that. But I've had mixed results. My own machine froze completely for example. I don't know if it matters but I always log in as admin. – Vivelin Nov 12 '09 at 16:13
  • ...and you'll need one endless loop per CPU core. – Roger Lipscombe May 25 '10 at 13:41
-1

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.

CodingBarfield
  • 3,392
  • 2
  • 27
  • 54