【问题标题】:Why is Ldvirtftn unverifiable?为什么 Ldvirtftn 无法验证?
【发布时间】:2012-03-31 20:30:42
【问题描述】:

在使用匿名托管的动态方法时,任何人都可以解释为什么我在公共类上的公共虚拟方法中得到 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;
    }

【问题讨论】:

  • MSIL 不仅适用于托管代码。您也可以将原始本机 C++ 代码编译为 IL。由 C++/CLI 编译器完成。 Opcodes.Ldvirtfn 很重要的那种代码,它从 v-table 中挖掘出一个函数指针。当抖动验证器被击中时,可以使用原始地址 IntPtr,而不是大量验证。
  • 是的,但是为什么给方法一个拥有类型会使验证问题消失呢?

标签: c# reflection.emit dynamicmethod


【解决方案1】:

简答

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

模式详情

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

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

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

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

【讨论】:

【解决方案2】:

ldvirtfn 将本机 int 加载到堆栈上。我认为您需要先将其转换为 IntPtr,然后再返回。

【讨论】:

  • 调用 conv_i 或 conv_u 并不能使这只小狗工作。您可能是对的,这是一个返回类型问题。如果返回 uint 的函数返回实际的 int 并且不先转换它,也会出现验证问题。 (例如 ldc_m1 ret)
  • 作为紧急措施,您可以 conv.i8 并使用 IntPtr(long) 重载。
【解决方案3】:

奇怪的行为(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-03-01
    • 2019-04-27
    • 1970-01-01
    • 1970-01-01
    • 2020-10-29
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 2017-05-01
    相关资源
    最近更新 更多