Подтвердить что ты не робот

Как свернуть среду выполнения обычного языка .NET(CLR) в чистом виде .net

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

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

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

4b9b3361

Ответ 1

Ну... как бы вы определили "чистый .NET"? Я играл с CLR2/delegate/GCHandle/array, когда читал сообщение о том, как сбой 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 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 в массиве. Сам код является чистым С#, если вы не считаете внедренный код 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.

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

Вы можете скомпилировать этот код с помощью /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
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'ером и ленив об этом.

[IL]: Ошибка: [C:\Users\файлы\Documents\Визуальное студия 2010\Projects\testCli\Bin\Release\PureSettings.dll: Settings.aUseTemplate:: HasTemplate ^ > ] [off set 0x00000017] [found ref '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 или любым другим безопасным типом, этот код генерирует исключение известного "кода может дестабилизировать время выполнения".

Ответ 7

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

Ответ 8

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