任何人都可以解释在使用匿名托管的动态方法时为什么我会在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;
}
答案 0 :(得分:4)
简短回答
您尝试发出的代码无法验证,匿名托管的动态方法永远不会包含无法验证的IL。由于与类型或模块关联的动态方法可能包含无法验证的IL(需要进行适当的安全检查),因此您的代码可以从这些动态方法中使用。
模式详情
尽管有MSDN的文档,但ldvirtftn
没有将本机int加载到堆栈中;它加载一个方法指针。正如将对象引用视为本机int一样有效但无法验证,将方法指针视为本机int也是有效但无法验证的。最简单的方法是使用相同的IL指令在磁盘上创建一个程序集(例如,使用System.Reflection.Emit或ilasm)并在其上运行PEVerify。
我相信方法指针的唯一可验证用法是:
dup; ldvirtftn; newobj
或ldftn; 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) { }
}