为什么Ldvirtftn无法验证?

时间:2012-03-31 20:30:42

标签: c# reflection.emit dynamicmethod

任何人都可以解释在使用匿名托管的动态方法时为什么我会在ldvirtftn上获得公共类的公共虚方法的无法验证的异常?我也设置了以下程序集级别属性:

[assembly: SecurityTransparent]
[assembly: SecurityRules(SecurityRuleSet.Level2,SkipVerificationInFullTrust=true)]

以下是示例代码:

public class Program
{
    public virtual void Foo() {}
    public static void Main(string[] args)
    {
        Action<ILGenerator> genfunc = il => il
            .newobj<Program>()
            .ldvirtftn(typeof(Program).GetMethod("Foo"))
            .ret();
        try
        {
            Console.WriteLine(CodeGen.CreateDelegate<Func<IntPtr>>(genfunc).Invoke());

        }
        catch (System.Security.VerificationException) { }
        Console.WriteLine(CodeGen.CreateDelegate<Func<IntPtr>>(genfunc,owner:typeof(Program)).Invoke());
    }
}

如果方法是拥有的,那么它不会抛出异常。

更奇怪的是,如果我像这样更改代码,那么两种方法都可以编译并运行而不会出现问题:

public class Program
{
    public virtual void Foo() {}
    public static void Main(string[] args)
    {
        Action<ILGenerator> genfunc = il => il
            .newobj<Program>()
            .dup()
            .ldvirtftn(typeof(Program).GetMethod("Foo"))
            .newobj<Action>(typeof(object),typeof(IntPtr))
            .ret();
        try
        {
            Console.WriteLine(CodeGen.CreateDelegate<Func<Action>>(genfunc).Invoke());
        }
        catch (System.Security.VerificationException) { }
        Console.WriteLine(CodeGen.CreateDelegate<Func<Action>>(genfunc,owner:typeof(Program)).Invoke());
    }
}

此代码是用反射库编写的。

CodeGen.CreateDelegate只使用type参数来确定动态方法的签名。这是方法::

    public static TDelegate CreateDelegate<TDelegate>(
        Action<ILGenerator> genfunc, string name = "", object target = null, Type owner = null, bool skipVisibility = false)
        where TDelegate : class
    {
        var invokeMethod = typeof(TDelegate).GetMethod("Invoke");
        var parameters = invokeMethod.GetParameters();
        var paramTypes = new Type[parameters.Length + 1];
        paramTypes[0] = typeof(object);
        parameters.Select(p => p.ParameterType).ToArray().CopyTo(paramTypes, 1);
        var method = owner != null ?
            new DynamicMethod(name, invokeMethod.ReturnType, paramTypes, owner, skipVisibility) :
            new DynamicMethod(name, invokeMethod.ReturnType, paramTypes, skipVisibility);
        genfunc(method.GetILGenerator());
        return method.CreateDelegate(typeof(TDelegate), target) as TDelegate;
    }

3 个答案:

答案 0 :(得分:4)

简短回答

您尝试发出的代码无法验证,匿名托管的动态方法永远不会包含无法验证的IL。由于与类型或模块关联的动态方法可能包含无法验证的IL(需要进行适当的安全检查),因此您的代码可以从这些动态方法中使用。

模式详情

尽管有MSDN的文档,但ldvirtftn没有将本机int加载到堆栈中;它加载一个方法指针。正如将对象引用视为本机int一样有效但无法验证,将方法指针视为本机int也是有效但无法验证的。最简单的方法是使用相同的IL指令在磁盘上创建一个程序集(例如,使用System.Reflection.Emit或ilasm)并在其上运行PEVerify。

我相信方法指针的唯一可验证用法是:

  • 使用dup; ldvirtftn; newobjldftn; newobj模式构建委托以创建兼容委托类型的新委托
  • 使用带有兼容参数的calli通过方法指针进行间接调用

这解释了为什么可以通过匿名托管的动态方法调用其他代码:您正在使用的委托创建模式是方法指针的少数可验证用法之一。

答案 1 :(得分:0)

ldvirtfn将本机int加载到堆栈中。我想你需要在返回之前先将它转换为IntPtr。

答案 2 :(得分:0)

奇怪的行为(IntPtr!= IntPtr):

//work normal
public static void F_Ldvirtftn_Action()
{
  Action<ILGenerator> genfunc = il =>
  {
    il.Emit(OpCodes.Newobj, typeof(Program).GetConstructor(Type.EmptyTypes));
    il.Emit(OpCodes.Dup);
    il.Emit(OpCodes.Ldvirtftn, typeof(Program).GetMethod("Foo2"));
    il.Emit(OpCodes.Newobj, typeof(Action).GetConstructor(new[] { typeof(object), typeof(IntPtr) }));
    il.Emit(OpCodes.Ret);
  };
  Console.WriteLine(CreateDelegate<Func<object>>(genfunc).Invoke());
}
// failed: VerificationException: Operation could destabilize the runtime
public static void F_IntPtr_Action()
{
  Action<ILGenerator> genfunc = il =>
  {
    il.Emit(OpCodes.Newobj, typeof(Program).GetConstructor(Type.EmptyTypes));
    il.Emit(OpCodes.Dup);
    il.Emit(OpCodes.Call, typeof(Program).GetMethod("Ptr"));
    il.Emit(OpCodes.Newobj, typeof(Action).GetConstructor(new[] { typeof(object), typeof(IntPtr) }));
    il.Emit(OpCodes.Ret);
  };
  Console.WriteLine(CreateDelegate<Func<object>>(genfunc).Invoke());
}
// failed: VerificationException: Operation could destabilize the runtime 
public static void F_Ldvirtftn_MyAction()
{
  Action<ILGenerator> genfunc = il =>
  {
    il.Emit(OpCodes.Newobj, typeof(Program).GetConstructor(Type.EmptyTypes));
    il.Emit(OpCodes.Dup);
    il.Emit(OpCodes.Ldvirtftn, typeof(Program).GetMethod("Foo2"));
    il.Emit(OpCodes.Newobj, typeof(MyAction).GetConstructor(new[] { typeof(object), typeof(IntPtr) }));
    il.Emit(OpCodes.Ret);
  };
  Console.WriteLine(CreateDelegate<Func<object>>(genfunc).Invoke());
}
//work normal
public static void F_IntPtr_MyAction()
{
  Action<ILGenerator> genfunc = il =>
  {
    il.Emit(OpCodes.Newobj, typeof(Program).GetConstructor(Type.EmptyTypes));
    il.Emit(OpCodes.Dup);
    il.Emit(OpCodes.Call, typeof(Program).GetMethod("Ptr"));
    il.Emit(OpCodes.Newobj, typeof(MyAction).GetConstructor(new[] { typeof(object), typeof(IntPtr) }));
    il.Emit(OpCodes.Ret);
  };
  Console.WriteLine(CreateDelegate<Func<object>>(genfunc).Invoke());
}

public static IntPtr Ptr(object z)
{
  return IntPtr.Zero;
}
public class MyAction
{
  public MyAction(object z, IntPtr adr) { }
}