如何在纯.net中崩溃.NET公共语言运行库(CLR)

时间:2009-11-11 20:03:08

标签: .net security crash clr

有针对Java VM的类似问题,但我没有找到.net的问题(如果我遗漏了某些内容,请关闭并标记为重复)。

那么 - 没有讨厌的非托管互操作可能吗?崩溃我的意思是真正的“xxx.exe已经停止工作”而不是StackOverflow-或OutOfMemoryException。

我认为这是不可能的,除非遇到VM本身的错误。

9 个答案:

答案 0 :(得分:16)

嗯......你怎么定义“纯.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的静态方法委托的内部实现,委托的一些私有成员实际上是内部指针。我将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。

答案 1 :(得分:3)

我今天就这样做了。我正在测试一个更大的.net项目的设置。缺少包含一些接口的程序集,exe只是停止工作。运行时没有捕获异常。

您可以确定运行时中存在更多错误 - 只计算数百万行代码......

答案 2 :(得分:2)

Oren Eini在.net框架中发现了一个导致“ExecutionEngineException”的错误 - 基本上是运行时崩溃。

您可以在此处阅读(Microsoft Connect):

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=384781

尽管臭虫的“关闭”状态 - 它还没有修复。

答案 3 :(得分:2)

无需使用不安全的代码或委托(如果我必须承认它是一种非常好的方法来崩溃你的CLR),你可以使用简单的Marshal函数来强制.NET崩溃。

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

此外,使用always GCHandle会导致内存访问冲突,例如错误。

using System;
using System.Runtime.InteropServices;

namespace Crash
{
    class Program
    {
        static void Main(string[] args)
        {
            GCHandle.FromIntPtr(new IntPtr(32323));
        }
    }
}

答案 4 :(得分:1)

我看到Java程序通过加载一个不存在的类并忽略ClassNotFoundError来崩溃JVM,然后继续执行,好像什么都没发生一样。也许你可以在动态加载.NET类时做类似的事情。

答案 5 :(得分:0)

您可以使用/ clr:pure编译此代码,并使用链接器强制纯选项。

然而,它看起来像运行时故障一样崩溃;

  

(1388.5e4):访问冲突 - 代码   c0000005(!!!第二次机会!!!)   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    * 警告:无法验证校验和   C:\ WINDOWS \装配\ NativeImages_v4.0.30319_32 \ mscorlib程序\ eb4e1e70734f6efb9c7de7ec5f452c9e \ mscorlib.ni.dll   mscorlib_ni + 0x9365c5:6ae965c5 ff10
  叫dword ptr [eax]
  DS:002B:8d00fea5 = ????????

即使您使用/ clr:pure或/ clr:safe进行编译,并且图像自身也是如此,但由于验证程序捕获此错误(编译器错误),它只能完全信任。

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

这是来自peverify的输出,它解析整个PE文件,直到运行时调用此方法才检测到此错误,因为验证程序与JIT'er一起工作并且对它的验证很懒惰。

  

[IL]:错误:   [C:\用户\ FILES \文档\ Visual   工作室   2010 \项目\ TESTCLI \ BIN \发布\ PureSettings.dll   :   Settings.aUseTemplate :: HasTemplate ^&gt;] [off set 0x00000017] [找到参考   'Settings.RefType'] [预期地址   of ref]堆栈上的意外类型。   1错误验证PureSettings.dll

如果您可以在此处绕过验证程序,这将是CLR中的巨大错误,并为您提供来自无法处理的进程的代码执行。

这是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

错误是抵消,IL_19。

如果您在CAS或任何其他类型安全模式下运行,此代码将生成着名的“代码可能会使运行时不稳定”异常。

答案 6 :(得分:0)

我认为我找到了另一种不涉及非托管代码的方法。但是,它使用PInvoke。当我添加MethodImpl(MethodImplOptions.Unmanaged)时,它起作用了。 我在.Net Core 2.1和.Net Framework 4.7.2上进行了测试。前者在VS调试期间立即崩溃,而后者在调试器发生异常时出现异常,消息是System.TypeLoadException抛出在内部某个地方。消息和错误代码与我从@Salvatore Previti答案得到的消息和错误代码相同。 我从命令行调用了已编译的应用程序,并获取错误退出代码-532462766。 代码如下:

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

答案 7 :(得分:-1)

我知道你可以用.NET崩溃你的整个PC。但这涉及无限循环和实时进程优先级......

答案 8 :(得分:-1)

有一些c#代码虽然技术上正确但不会作为有效的.net程序运行。它有一些东西,界面重载一个空方法,但我真的不记得了。