2

谁能解释在使用匿名托管的动态方法时,为什么 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 只是使用类型参数来确定动态方法的签名。这是方法::

    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;
    }
4

3 回答 3

4

简短的回答

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

模式详情

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

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

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

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

于 2012-04-02T21:16:26.050 回答
0

ldvirtfn 将本机 int 加载到堆栈上。我认为您需要在返回之前先将其转换为 IntPtr 。

于 2012-03-31T20:48:26.733 回答
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) { }
}
于 2012-04-01T01:43:59.160 回答