2012-03-31 46 views
2

任何人都可以解釋何時使用匿名託管的動態方法爲什麼我得到一個公共類的公共虛擬方法ldvirtftn無法驗證的異常?我設置以下組件級別的屬性,以及:爲什麼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; 
    } 
+0

MSIL不只是託管代碼。您也可以將原始的本機C++代碼編譯爲IL。由C++/CLI編譯器完成。 Opcodes.Ldvirtfn是重要的代碼類型,它從v表中挖掘函數指針。當抖動驗證器被擊中時,原始地址IntPtr,而不是驗證的可能性。 – 2012-03-31 22:04:20

+0

的確如此,但爲什麼給該方法一個擁有類型會使驗證問題消失? – 2012-03-31 22:36:48

回答

4

簡短的回答

您正在嘗試發出的代碼是無法證實的,並且匿名運行動態方法不能含有無法證實IL 。由於與類型或模塊關聯的動態方法可能包含無法驗證的IL(受適當的安全檢查限制),因此您的代碼可用於這些動態方法。

模式細節

儘管MSDN文檔中,ldvirtftn不加載本地INT壓入堆棧;它加載一個方法指針。就像將對象引用視爲本地int一樣有效但不可驗證,將方法指針視爲本地int也是有效的,但無法驗證。最簡單的方法是使用相同的IL指令在磁盤上創建程序集(例如使用System.Reflection.Emit或ilasm)並在其上運行PEVerify。

我相信,方法指針的唯一可證實的用途是:

  • 構建使用dup; ldvirtftn; newobjldftn; newobj模式創建兼容的委託類型
  • 使用calli兼容參數的一個新委託的委託通過方法指針進行間接呼叫

這解釋了爲什麼您的其他代碼可以通過匿名託管的動態方法調用d:您使用的委託創建模式是方法指針的少數可驗證用途之一。

+0

PEVerify認爲calli始終是不可驗證的(這是有道理的)。 http://blogs.msdn.com/b/shawnfa/archive/2004/06/14/155478.aspx 很奇怪,但完全可信。 – 2012-04-03 00:22:14

+0

@MichaelB - 好點,我列出了ECMA規範涵蓋的規則,這與PEVerify實際執行的規則不同。 – kvb 2012-04-03 04:32:59

0

ldvirtfn將本地int加載到堆棧上。我想你需要在返回之前先將它轉換爲IntPtr。

+0

調用conv_i或conv_u不會使這隻小狗可行。你可能是對的,它是一個返回類型的問題。如果您返回uint的函數返回實際的int並且不先將其轉換,則還會出現驗證問題。 (例如ldc_m1 ret) – 2012-03-31 22:39:37

+0

'IntPtr's *是*本地整數。 – kvb 2012-04-02 21:35:40

+0

作爲緊急措施,您可以conv.i8並使用IntPtr(long)超載。 – usr 2012-04-02 22:18:39

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) { } 
}