C# в сочетании с MSIL — JIT Skip Verification

Я пытаюсь вызвать следующий метод MSIL:

.method public hidebysig static bool IsRuntimeType(class [mscorlib]System.Type 'type') cil managed {
    .maxstack 2
    ldarg.0
    isinst [mscorlib]System.RuntimeType
    ldnull
    cgt.un
    ret
} // end of method Program::IsRuntimeType

Однако это исключение возникает при попытке выполнить строку:

isinst [mscorlib]System.RuntimeType

'TypeAccessException', возникающее при вызове метода

Да, я знаю о JIT-проверке, но я пробовал много вещей, и они не работали, или, может быть, я просто делал их неправильно, я не уверен. Кроме того, я не мог найти много по этой теме.

Я пробовал следующее (в сочетании некоторых из них вместе):

  • Добавьте в метод атрибут [SecurityPermissionAttribute(SecurityAction.Demand, SkipVerification = true)] (также с SecurityAction.Assert)
  • Звонок new ReflectionPermission(ReflectionPermissionFlag.MemberAccess | ReflectionPermissionFlag.RestrictedMemberAccess).Demand(); (а также .Assert())
  • Звонок new SecurityPermission(SecurityPermissionFlag.AllFlags).Demand(); (а также .Assert())

Ни один из этих требовательных и утверждающих не стал исключением.

Чтобы уточнить, это всего лишь пример. Основная идея — заставить код работать и обойти JIT-проверку. Этот конкретный метод не может быть выполнен в C# без размышлений, и я хочу избежать его, потому что это очень дорого, но это не главное.

Есть ли способ заставить этот код выполняться без JIT, выбрасывающего TypeAccessException (например, когда вы вызываете динамический метод, который вы передали true параметру skipVerification его конструктору)?


person boaz23    schedule 26.04.2016    source источник
comment
Возможно, связано с тем, для чего предназначен этот метод: stackoverflow.com/questions/10183619/   -  person Jeroen Mostert    schedule 27.04.2016
comment
Это не то, о чем я прошу, это всего лишь один метод, который я хочу реализовать с кодом IL, который использует непубличные члены и типы. Я имею в виду больше вещей, таких как реализация моих собственных GetMethods и GetFields и т. д. на основе отражения .NET. Для этого вызова требуется System.RuntimeType.ListBuilder'1<T>.ToArray и некоторые другие непубличные вещи. Я просто хочу знать, как это сделать с помощью чистого IL-кода. Конечно, я могу использовать динамические методы или что-то в этом роде (это то, что я делаю прямо сейчас, и я не доволен этим), но это упускает из виду весь смысл. Я делаю это для себя, чтобы учиться и получать удовольствие.   -  person boaz23    schedule 27.04.2016
comment
Глупый вопрос: это можно сделать на C#, почему бы просто не написать этот метод? Редактировать: здесь я должен согласиться с @JeroenMostert.   -  person leppie    schedule 27.04.2016
comment
Пробовали ли вы добавить UnverifiedCode атрибут модуля?   -  person Brian Reichle    schedule 27.04.2016
comment
@BrianReichle, да, я только что попробовал, не работает   -  person boaz23    schedule 27.04.2016
comment
Здесь возникает путаница между пропущенными проверками проверки и пропущенными проверками видимости. Первый относится к статической предварительной проверке того, выполняет ли какой-либо код IL все поддающиеся проверке операции IL, тогда как второй относится к тому, будет ли JIT во время выполнения учитывать видимость членов кросс-типа при разрешении токенов метаданных, которые может содержать код, поэтому они совершенно не связаны.   -  person Glenn Slayden    schedule 17.12.2018


Ответы (2)


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

 static bool IsTypeRuntimeType(Type type)
 {
        return typeof(object).GetType() == type.GetType();
 }
person Hadi Brais    schedule 28.04.2016
comment
Как упомянул boaz23 в вопросе, есть некоторые перегрузки класса DynamicMethod, которые могут нарушать правила доступности. Также он заявляет, что код IL — это всего лишь пример, а не то, для чего ему нужен обходной путь. - person thehennyy; 28.04.2016
comment
@thehennyy ОП заявил в комментариях, что хочет использовать чистый IL. Код, который я показал, является чистым IL, если только он не имеет в виду не вызывать какие-либо методы, что невозможно без использования неуправляемых указателей. - person Hadi Brais; 28.04.2016
comment
@thehennyy DynamicMethod является частью API-интерфейсов отражения, которые по определению позволяют вам перечислять все типы и все члены, а также вызывать или использовать некоторые типы и члены в зависимости от предоставленных разрешений безопасности отражения. - person Hadi Brais; 28.04.2016
comment
@thehennyy точно, так как мне предоставить это разрешение коду IL? Я имею в виду, почему это возможно с использованием DynamicMethod, а не кода IL? - person boaz23; 02.05.2016
comment
@ boaz23 Потому что DynamicMethod предлагает несколько конструкторов, в которых последний параметр bool skipVisibility позволяет пропускать проверки видимости JIT, что, как я уже сказал, отличается от пропуска проверки. При написании или генерации кода IL статически вы можете пропустить проверку только при запуске как полностью доверенный код. Однако невозможно статически указать JIT-компилятору пропускать проверки видимости для какого-либо метода. Это просто не поддерживается. - person Hadi Brais; 03.05.2016

Вы можете загрузить токен, чтобы получить RuntimeTypeHandle, а затем вызвать Type.GetTypeFromHandle.

После игры с этим newobj <ctor> через то же исключение безопасности, что и выше. Однако мне удалось использовать Activator.CreateInstance

Здесь работает MSIL (обратите внимание, что сначала выполняется проверка типа (отвечая на исходный вопрос), а затем пример того, как работать, создавать и возвращать private struct ListBuilder<T> :

  .method public static object  IsRuntimeType(class [mscorlib]System.Type 'type') cil managed
  {
    // Code size       48 (0x30)
    .maxstack  5
    IL_0000:  ldarg.0
    IL_0001:  ldtoken    [mscorlib]System.RuntimeType
    IL_0006:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_000b:  call       bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type,
                                                                 class [mscorlib]System.Type)
    IL_0010:  pop
    IL_0011:  ldtoken    valuetype [mscorlib]System.RuntimeType/ListBuilder`1<string>
    IL_0016:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_001b:  ldc.i4.1
    IL_001c:  newarr     [mscorlib]System.Object
    IL_0021:  dup
    IL_0022:  ldc.i4.0
    IL_0023:  ldc.i4.0
    IL_0024:  box        [mscorlib]System.Int32
    IL_0029:  stelem.ref
    IL_002a:  call       object [mscorlib]System.Activator::CreateInstance(class [mscorlib]System.Type,
                                                                           object[])
    IL_002f:  ret
  } // end of method Dyn::IsRuntimeType

Вот CSharp, используемый для создания динамической библиотеки DLL и тестирования приведенного выше кода, из которого указанный выше MSIL был извлечен с помощью ILDASM.exe.

    var asmName = new AssemblyName("MsilDyn");
    AppDomain domain = AppDomain.CurrentDomain;

    AssemblyBuilder wrapperAssembly =
        domain.DefineDynamicAssembly(asmName,
            AssemblyBuilderAccess.RunAndSave);

    var assemblyPath = asmName.Name + ".dll";

    ModuleBuilder wrapperModule =
        wrapperAssembly.DefineDynamicModule(asmName.Name,
           assemblyPath);

    // Define a type to contain the method.
    TypeBuilder typeBuilder =
        wrapperModule.DefineType("Dyn", TypeAttributes.Public);

    MethodAttributes atts = MethodAttributes.Public | MethodAttributes.Static;
    MethodBuilder methodBuilder =
     typeBuilder.DefineMethod($"IsRuntimeType",
                                atts,
                                typeof(object),
                                new[] { typeof(Type) });
    methodBuilder.DefineParameter(1, ParameterAttributes.None, "type");

    ILGenerator il = methodBuilder.GetILGenerator();

    var assem = typeof(string).Assembly;
    var t = assem.GetType("System.RuntimeType");
    var nestedList = t.GetMembers();

    var resolveType = typeof(Type).GetMethod("GetType", new[] { typeof(string) });//., BindingFlags.Static | BindingFlags.Public);
    var opEqual = typeof(Type).GetMethod("op_Equality");
    var getTypeHandle = typeof(Type).GetMethod("GetTypeFromHandle");

    var runtimeType = Type.GetType("System.RuntimeType");
    var listBuilderType = (TypeInfo)runtimeType.GetMember("ListBuilder`1",
        BindingFlags.Public | BindingFlags.NonPublic)[0];



    var ListBuilderOfStringType = listBuilderType.MakeGenericType(new[] { typeof(string) });

    // From C#
    /*
    var ctor = listBuilderType.GetConstructor(new[] { typeof(int) });
    var instance = Activator.CreateInstance(ListBuilderOfStringType, new object[] { 0 });
    */

    var listBuilderCtorArgs = new[] { typeof(Type), typeof(object[]) };
    var ctor = typeof(Activator).GetMethod("CreateInstance", listBuilderCtorArgs);


    // Generate an MSIL example of working with the RuntimeType for comparison
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldtoken, runtimeType);
    il.Emit(OpCodes.Call, getTypeHandle);
    il.Emit(OpCodes.Call, opEqual);
    il.Emit(OpCodes.Pop);

    // Generate an MSIL of creating RuntimeType.ListBuilder<string>
    il.Emit(OpCodes.Ldtoken, ListBuilderOfStringType);
    il.Emit(OpCodes.Call, getTypeHandle);
    il.Emit(OpCodes.Ldc_I4_1);
    il.Emit(OpCodes.Newarr, typeof(object));
    il.Emit(OpCodes.Dup);
    il.Emit(OpCodes.Ldc_I4_0);
    il.Emit(OpCodes.Ldc_I4_0);
    il.Emit(OpCodes.Box, typeof(int));
    il.Emit(OpCodes.Stelem_Ref);
    il.Emit(OpCodes.Call, ctor);
    il.Emit(OpCodes.Ret);


    var result = typeBuilder.CreateType();
    wrapperAssembly.Save(assemblyPath);

    var method = result.GetMethod("IsRuntimeType", BindingFlags.Public | BindingFlags.Static);

    var stringType = typeof(string);
    var listBuilderOfStringInstance = method.Invoke(null, new[] { stringType });
person Alexander Higgins    schedule 23.04.2019