2012-03-31 1 views
2

익명으로 호스팅 된 동적 메서드를 사용할 때 설명 할 수있는 사람이 공개 클래스의 공개 가상 메서드에 대해 ldvirtftn에 의해 확인할 수없는 예외가 발생하는 이유는 무엇입니까? 메소드가 소유 한 경우, 예외를 throw하지 않습니다Ldvirtftn을 확인할 수없는 이유는 무엇입니까?

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()); 
    } 
} 

:

여기
[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>() 
      .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

MSIL은 관리되는 코드를위한 것이 아닙니다. 원시 원시 C++ 코드를 IL로 컴파일 할 수도 있습니다. C++/CLI 컴파일러가 수행했습니다. Opcodes.Ldvirtfn이 중요한 코드의 종류는 v 테이블에서 함수 포인터를 파 낸다.지터 검증기에 충돌이 발생하면 원시 주소 인 IntPtr이 검증의 가설이 아닌 가능합니다. –

+0

사실, 그렇다면이 메소드에 소유 유형을 지정하면 검증 문제가 사라지게되는 이유는 무엇입니까? –

답변

4

짧은 대답은

당신이 방출하려는 코드는 확인 불가능한이며, 익명 동적 방법을 확인 불가능한 IL을 포함 할 수 없다 호스팅 . 유형 또는 모듈과 관련된 동적 메서드에는 검증 할 수없는 IL (적절한 보안 검사가 적용될 수 있음)이 포함될 수 있으므로 해당 동적 메서드에서 코드를 사용할 수 있습니다. MSDN의 설명서에도 불구하고

모드 세부

ldvirtftn는 스택에 기본 INT를로드하지 않습니다; 메소드 포인터를로드합니다. 네이티브 int로서의 객체 참조를 처리하는 것이 유효하지만 검증 할 수없는 것과 같이, 메소드 포인터를 네이티브 int로서 취급하는 것도 유효 합니다만, 검증 할 수 없습니다. 이를 확인하는 가장 쉬운 방법은 동일한 IL 명령어 (예 : System.Reflection.Emit 또는 ilasm 사용)로 디스크에 어셈블리를 만들고 PEVerify를 실행하는 것입니다.

나는 방법 포인터의 유일한 검증 용도가 있다고 생각 :

  • 는 호환 인수 calli를 사용하여 호환 대리자 형식
  • 의 새로운 대리자를 만들 수 dup; ldvirtftn; newobj 또는 ldftn; newobj 패턴을 사용하여 대리자를 구축 메서드 포인터를 통해 간접 호출하기

이렇게하면 익명으로 호스팅되는 동적 메토를 통해 다른 코드를 호출 할 수있는 이유가 설명됩니다 d : 사용중인 델리게이트 생성 패턴은 메소드 포인터의 검증 가능한 사용법 중 하나입니다.

+0

PEVerify는 항상 calli를 확인할 수없는 것으로 간주합니다. http://blogs.msdn.com/b/shawnfa/archive/2004/06/14/155478.aspx 아주 이상하지만 완전히 믿을 수 있습니다. –

+0

@MichaelB - 좋은 점은 ECMA 사양이 다루는 규칙을 나열했는데 PEVerify가 실제로 구현 한 것과 같지 않습니다. – kvb

0

ldvirtfn이 스택에 기본 int를로드합니다. 나는 당신이 IntPtr로 돌아 오기 전에 그것을 변환 할 필요가 있다고 생각한다.

+0

conv_i 또는 conv_u를 호출해도이 강아지를 실행할 수 없습니다. 당신은 반환 유형 문제가 맞을 수도 있습니다. 확인 문제는 uint를 반환하는 함수가 실제 int를 반환하고 먼저 변환하지 않으면 표시됩니다. (예 : ldc_m1 ret) –

+0

'IntPtr's *은 (는) 네이티브 int입니다. – kvb

+0

응급 조치로 conv.i8을 사용하고 IntPtr (긴) 과부하를 사용할 수 있습니다. – usr

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