Как вывести из строя общеязыковую среду .NET в среде .net - PullRequest
13 голосов
/ 11 ноября 2009

Существует аналогичный вопрос, касающийся Java VM , но я не нашел вопрос для .net (закройте и пометьте как дубликат, если я что-то упустил).

Итак, возможно ли это без неприятного неуправляемого взаимодействия? И при сбое я имею в виду настоящий «xxx.exe перестал работать», а не StackOverflow- или OutOfMemoryException.

Я думаю, что это невозможно, за исключением случаев ошибки в самой виртуальной машине.

Ответы [ 9 ]

16 голосов
/ 12 ноября 2009

Ну ... как бы вы определили "чистый .NET"? Я поиграл с CLR2 / делегат / GCHandle / массив, когда я прочитал пост "как разбить JVM" и придумал что-то вроде этого:

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

Известно, что он работает только на 32-битной Windows XP с CLR2 на x86; а также известно, что он не работает с 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 с Mono 2.4 и работали.

Я написал в блоге об этом здесь: http://rednaxelafx.javaeye.com/blog/461787

Это на китайском языке, но там есть много кода, который должен объяснить, что я сделал. Используя тот же трюк, в конце поста в блоге я показал несколько примеров того, как можно настроить приведенный выше код, чтобы заставить его работать неправильно, например, получить исключение SEHException.

3 голосов
/ 11 ноября 2009

Я сделал это только сегодня. Я тестировал настройку более крупного проекта .net. Там отсутствовала сборка, содержащая некоторые интерфейсы, и exe просто перестает работать. Во время выполнения не было обнаружено исключений.

Вы можете быть уверены, что во время выполнения будет еще больше ошибок - просто посчитайте миллионы строк кода ...

2 голосов
/ 29 октября 2011

Без необходимости использовать небезопасный код или делегаты (также, если я должен признать, что это очень хороший способ сбить вашу 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);
            }
        }
    }
}

Также это, используя всегда GCHandle, приведет к нарушению доступа к памяти, как ошибка.

using System;
using System.Runtime.InteropServices;

namespace Crash
{
    class Program
    {
        static void Main(string[] args)
        {
            GCHandle.FromIntPtr(new IntPtr(32323));
        }
    }
}
2 голосов
/ 11 ноября 2009

Орен Эйни обнаружил ошибку в .net framework, которая вызвала 'ExecutionEngineException' - по сути, сбой среды выполнения.

Вы можете прочитать об этом здесь (Microsoft Connect):

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

И, несмотря на «закрытое» состояние ошибки - она ​​еще не исправлена.

1 голос
/ 11 ноября 2009

Я видел, как Java-программы аварийно завершают работу JVM, загружая несуществующий класс и игнорируя ClassNotFoundError, а затем продолжают работу, как будто ничего не произошло. Возможно, вы могли бы сделать нечто подобное при динамической загрузке класса .NET.

0 голосов
/ 14 февраля 2019

Я думаю, что нашел другой способ, который не связан с неуправляемым кодом. Тем не менее, он использует 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();
        }
    }
}
0 голосов
/ 30 апреля 2011

Вы можете скомпилировать этот код с помощью / clr: pure и также использовать опцию linker force 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 фс = 0053 гс = 002b
EFL = 00010246 * ВНИМАНИЕ: Невозможно проверить контрольную сумму для C: \ Windows \ сборка \ NativeImages_v4.0.30319_32 \ mscorlib \ eb4e1e70734f6efb9c7de7ec5f452c9e \ mscorlib.ni.dll mscorlib_ni + 0x9365c5: 6ae965c5 ff10
Call 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: \ Users \ файлы \ Documents \ Визуальное студия 2010 \ Projects \ testCli \ Bin \ Release \ PureSettings.dll : Settings.aUseTemplate :: HasTemplate ^>] [отключено 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 или любом другом безопасном режиме, этот код сгенерирует известное исключение «код может дестабилизировать среду выполнения».

0 голосов
/ 28 апреля 2011

Существует некоторый код на c #, который, будучи технически правильным, не будет работать как действительная программа .net В нем есть что-то с интерфейсом, перегружающим пустой метод, но я не могу вспомнить.

0 голосов
/ 11 ноября 2009

Я знаю, что вы можете разбить весь ваш компьютер с помощью .NET. Но это включает в себя бесконечный цикл и приоритет процесса RealTime ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...