C# combined with MSIL - JIT Skip Verification
Asked Answered
S

3

8

I'm trying to call the following MSIL method:

.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

However, this exception occurs when trying to execute the line:

isinst [mscorlib]System.RuntimeType

'TypeAccessException' that is occured when calling the method

Yes, I'm aware of JIT verification, but I've tried many things and they didn't work or maybe I just did them wrong, I"m not sure. Also, I couldn't find much about this subject.

I have tried the following (in combination of some of them together):

  • Add a [SecurityPermissionAttribute(SecurityAction.Demand, SkipVerification = true)] attribute to the method (also with SecurityAction.Assert)
  • Calling new ReflectionPermission(ReflectionPermissionFlag.MemberAccess | ReflectionPermissionFlag.RestrictedMemberAccess).Demand(); (and also .Assert())
  • Calling new SecurityPermission(SecurityPermissionFlag.AllFlags).Demand(); (and also .Assert())

None of these demanding and asserting threw an exception.

To clarify, this is just an example. The main idea is making the code work and bypassing JIT's verification. This particular method cannot be done in C# without reflection and I want to avoid it because it is very costly, but that's not the point.

Is there any way I can make this code execute without the JIT throwing a TypeAccessException (like when you invoke a dynamic method that you passed true to the skipVerification parameter to it's constructor)?

Squashy answered 26/4, 2016 at 19:22 Comment(6)
Possibly related depending on what this method is for: #10184119Meet
That is not what I'm asking, this is just one method I want to implement with IL code that uses non-public members and types. I have more things in mind, like implementing my own GetMethods and GetFields and so on based on the reflection of .NET. This calling requires System.RuntimeType.ListBuilder'1<T>.ToArray and some other non-public stuff. I just want to know how to do this with pure IL code. Sure I can use dynamic methods or something (that's what i'm doing right now and I'm not happy with it), but that's missing the entire point. I'm doing it for myself, to learn and to have fun.Squashy
Silly question: This can be done in C#, why not just write this method? Edit: I have to agree with @JeroenMostert here.Phototransistor
Did you try adding the UnverifiableCode attribute to the module?Huggermugger
@BrianReichle, Yes, I've tried it just now, it doesn't workSquashy
There's a confusion here between "skip verification" checks vs. "skip visibility" checks. The former refers to statically pre-inspecting whether some IL code performs all "verifiable" IL operations, whereas the latter refers to whether the runtime JIT will respect cross-type member visibility when resolving metadata tokens that code may contain, so they're quite unrelated.Rennarennane
A
1

After six years, I am not sure if @boaz23 is still using stackoverflow. But I had the same problem and I found a way just now.

The key is using IgnoresAccessChecksToAttribute to skip JIT visibility check, as we want to access non-public types.

Because I am using .netstandard instead of .net framework, so I replaced mscorlib with System.Private.CoreLib in the il code. Here it is.

.assembly extern System.Private.CoreLib {}

.class public abstract auto ansi sealed beforefieldinit System.TypeHelper
{
    .method public hidebysig static bool IsRuntimeType(class [mscorlib]System.Type 'type') cil managed {
        .maxstack 2
        ldarg.0
        isinst [System.Private.CoreLib]System.RuntimeType
        ldnull
        cgt.un
        ret
    }
}

First, add this IgnoresAccessChecksToAttribute to your project.

namespace System.Runtime.CompilerServices
{
    /// <summary>
    /// Allows the current assembly to access the internal types of a specified assembly that are ordinarily invisible.
    /// </summary>
    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
    public sealed class IgnoresAccessChecksToAttribute : Attribute
    {

        /// <summary>
        /// Initializes a new instance of the <see cref="IgnoresAccessChecksToAttribute" /> class with the name of the specified assembly.
        /// </summary>
        /// <param name="assemblyName">The name of the specified assembly.</param>
        public IgnoresAccessChecksToAttribute(string assemblyName)
        {
            AssemblyName = assemblyName;
        }

        /// <summary>
        /// Gets the name of the specified assembly whose access checks against the current assembly are ignored .
        /// </summary>
        /// <value>A string that represents the name of the specified assembly.</value>
        public string AssemblyName { get; }
    }
}

Second, add [assembly: IgnoresAccessChecksTo("System.Private.CoreLib")] to your project where your .il file is located.

Finally, I can use the method like this:

[Fact]
public void IsRuntimeType_Test()
{
    Assert.True(TypeHelper.IsRuntimeType(typeof(object)));
}

And the method will be invoked successfully.

Ann answered 17/5, 2022 at 5:58 Comment(0)
I
0

Irrespective of whether verification is enabled or not, you cannot violate type accessibility or member visibility even if you used CIL directly. This is a matter of correctness of CIL code, not just type-safety. This is the correct way to check whether a given type object is an instance of RuntimeType.

 static bool IsTypeRuntimeType(Type type)
 {
        return typeof(object).GetType() == type.GetType();
 }
Inweave answered 28/4, 2016 at 11:44 Comment(5)
As boaz23 has mentioned in the question, there are some overloads of the DynamicMethod class that can violate the accessibility rules. Also he states that the IL code is just an example and not a thing he needs a workaround for.Agateware
@Agateware The OP stated in the comments he wants to use pure IL. The code I've shown is pure IL unless he means not calling any methods which is not possible without using unmanaged pointers.Inweave
@Agateware DynamicMethod is part of the reflection APIs, which by definition enable you to enumerate all types and all members and invoke or use some types and members depending on the reflection security permissions that have been granted.Inweave
@Agateware exactly, so how do I grant these permission to the IL code? I mean, how come it's possible using DynamicMethod and not IL code?Squashy
@Squashy Because DynamicMethod offers several constructors in which the last parameter bool skipVisibility enables you to skip JIT visibility checks, which is, as I said, different from skipping verification. When writing or emitting IL code statically, you can only skip verification when running as fully trusted code. However, there is no way to tell the JIT compiler statically to skip visibility checks on some method. It's just not supported.Inweave
U
0

You can load a token to get RuntimeTypeHandle and then call Type.GetTypeFromHandle.

After playing around with this newobj <ctor> through the same security exception as above. However, I was successful using Activator.CreateInstance

Here's working MSIL (note there is a type check first (addressing the original question) and then an example of how to work and create and return the 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

Here's the CSharp used to create the dynamic DLL and test the code above, from which the MSIL above was extracted using 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 });
Unhook answered 23/4, 2019 at 2:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.