2014-10-11 2 views
17

C/C++에서 C#과 같은 IL 코드 배열을 실행할 수있는 방법이 있습니까?C#에서 .NET IL 코드 실행

메서드를 작성하여 IL 코드로 변환하고 난독 화하여 바이트 배열에 저장하고 마지막으로 배열 내용을 해독하고 IL 코드를 실행하려고합니다.

private static int MyMethod(string A, int B) 
    { 
    locals: int local_0, 
      bool local_1 

    /* 0000025C 00    */ nop 
    /* 0000025D 03    */ ldarg_1 // int B 
    /* 0000025E 1F 0A   */ ldc_i4_s 10 
    /* 00000260 58    */ add 
    /* 00000261 10 01   */ starg_s arg_1 // int B 
    /* 00000263 02    */ ldarg_0 // string A 
    /* 00000264 72 01 00 00 70 */ ldstr "A" 
    /* 00000269 6F 11 00 00 0A */ callvirt System.String::Equals(string) // returns bool 
    /* 0000026E 16    */ ldc_i4_0 
    /* 0000026F FE 01   */ ceq 
    /* 00000271 0B    */ stloc_1 // bool local_1 
    /* 00000272 07    */ ldloc_1 // bool local_1 
    /* 00000273 2D 03   */ brtrue_s loc_28 
    /* 00000275 16    */ ldc_i4_0 
    /* 00000276 10 01   */ starg_s arg_1 // int B 
loc_28: 
    /* 00000278 03    */ ldarg_1 // int B 
    /* 00000279 0A    */ stloc_0 // int local_0 
    /* 0000027A 2B 00   */ br_s loc_32 
loc_32: 
    /* 0000027C 06    */ ldloc_0 // int local_0 
    /* 0000027D 2A    */ ret 
    } 

그리고 마지막으로이 바이트 배열은 다음과 같습니다 : 이제

static int MyMethod(string A, int B) 
{ 
    B += 10; 
    if (A.Equals("A")) 
     B = 0; 
    return B; 
} 

내가 IL 코드로 변환 : 예를 들어

이 내 C# 코드입니다

private byte[] ilcode = 
{ 
    0x00, 0x03, 0x1F, 0x0A, 0x58, 0x10, 0x01, 0x02, 0x72, 0x01, 0x00, 0x00, 0x70, 0x6F, 0x11, 0x00, 0x0, 0x0A, 0x16, 
    0xFE, 0x01, 0x0B, 0x07, 0x2D, 0x03, 0x16, 0x10, 0x01, 0x03, 0x0A, 0x2B, 0x00, 0x06, 0x2A 
}; 
+2

예상대로 IL을 실행할 수있는 방법은 없지만 리플렉션을 사용하여 동적으로 모든 메소드를로드하고 호출 할 수 있습니다. – Thangadurai

+3

당신의 가장 큰 문제는 메타 데이터 토큰이라고 생각합니다. 예를 들어'string.Equals()'(코드에서'11 00 00 0A')에 대한 토큰이 항상 같지는 않을 것입니다. – svick

+1

@svick 메타 데이터 토큰은 모듈 수준에서 저장되어있는 것처럼 보이기 때문에 큰 문제가됩니다 (적어도 빠른 테스트에서 볼 수 있습니다). 문자열 리터럴과 같은 경우에도 마찬가지입니다. –

답변

21

당신 System.Reflection.Emit 네임 스페이스의 인프라를 사용해야합니다. 특히 MethodBuilder.CreateMethodBody에 대한 docs은 MSIL 명령어를 나타내는 바이트 배열을 사용합니다. 거기에 전체 예제가 있지만, 아래는 짧은 사용법입니다. 여기에서는 동적 메서드에 대한 대리자도 만듭니다.

나는이 단지 문서에서 호출되는 매우 제한된 방식에서 지원되는지주의 할 것이다는 :

이 현재 완벽하게 지원되지 않습니다. 사용자는 토큰 픽스 업 및 예외 처리기의 위치를 ​​제공 할 수 없습니다.

유형, 메소드, 문자열 리터럴 등을 참조하기 위해 IL에서 사용 된 메타 데이터 토큰이 모듈 수준에서 해결된다는 것이 문제입니다. 따라서 일리노이는 하나의 모듈에있는 메소드에서 임의의 원시 IL을 가져 와서 다른 모듈의 다른 메소드에 드롭 할 수 없다는 점에서 완전히 이식성이 아닙니다. 새 모듈에서 적절한 메타 데이터 토큰이 필요합니다. 그러나 IL에 메타 데이터 토큰이 없다는 것을 알게되면이를 수행 할 수는 있지만 이렇게하면 할 수있는 일이 심각하게 제한됩니다. (HT : svick, 사이먼 스벤손)

class Program 
{ 
    static void Main(string[] args) 
    { 
     // opcodes for pushing two arguments to the stack, adding, and returning the result. 
     byte[] ilcodes = { 0x02, 0x03, 0x58, 0x2A }; 
     var method = CreateFromILBytes(ilcodes); 
     Console.WriteLine(method(2, 3)); 
    } 

    private static Func<int, int, int> CreateFromILBytes(byte[] bytes) 
    { 
     var asmName = new AssemblyName("DynamicAssembly"); 
     var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave); 
     var module = asmBuilder.DefineDynamicModule("DynamicModule"); 
     var typeBuilder = module.DefineType("DynamicType"); 
     var method = typeBuilder.DefineMethod("DynamicMethod", 
      MethodAttributes.Public | MethodAttributes.Static, 
      typeof(int), 
      new[] { typeof(int), typeof(int) }); 
     method.CreateMethodBody(bytes, bytes.Length); 
     var type = typeBuilder.CreateType(); 
     return (Func<int, int, int>)type.GetMethod("DynamicMethod").CreateDelegate(typeof(Func<int, int, int>)); 
    } 
} 

참고 RunAndSave 옵션의 사용. 그러면 동적 어셈블리가 디스크에 임시 위치에 저장됩니다. RunAndCollect을 사용하는 것이 더 바람직 할 수 있습니다.이 경우 메모리에만 어셈블리가 생성되고 나중에 모든 참조가 제거되면 수집 할 수 있습니다. 그러나 이른바 소장품 조립품에 대한주의 사항은 here입니다.

+0

잘 알고 있습니다. 나는 지금 더 많은 책을 읽기 시작해야한다! – Thangadurai

+1

opcodes ('ldarg.0','ldarg.1','add','ret')가 메타 데이터 토큰을 사용하지 않기 때문에이 예제 (바이트 배열)가 작동합니다. – sisve

+0

@SimonSvensson 맞습니다. 저는 이것을 제 대답으로 확장했습니다. –