2014-09-11 6 views
2

주소를 변경할 수있는 방법이 있습니까? OpCodes.Ret가 점프합니까? 일리노이 메서드는 C#에서 사용하는 호출 스택을 변경할 수 있습니까?OpCodes.Ret의 주소는 어디에 저장되어 있습니까? 그것을 바꿀 수 있습니까?

내가 아는 한 C++에서는 스택의 값에 액세스하고 리턴 주소를 변경할 수 있습니다. 일리노이에서 "InvalidProgramException"예외로 현재 메소드를 넘어서 스택에 액세스하려고 시도한 모든 것이 실패합니다.

내 예제 코드 :

public static void Test2() 
    { 
     var ma = typeof(Program).GetMethod("Test2"); 
     DynamicMethod meth = new DynamicMethod("", null, null, ma.Module, true); 
     ILGenerator il = meth.GetILGenerator(); 



     il.EmitWriteLine("Start to run the IL method"); 

     var t = il.DeclareLocal(typeof(int)); 



     //take the top of the stack (from the caller method) and put it into the variable t 
     //this seems to be not possible? 
     il.Emit(OpCodes.Stloc, t); 

     //print the value from the stack 
     il.EmitWriteLine(t); 

     //load the value back from the field onto the stack to allow everything to preceed normally 
     il.Emit(OpCodes.Ldloc, t); 

     //return 
     il.Emit(OpCodes.Ret); 

     Action a = (Action)meth.CreateDelegate(typeof(Action)); 

     a(); 
    } 
+1

누가 'ret'가 주소로 이동한다고 말합니까? opcode 사양은 "주소로 점프"에 대해서는 아무 것도 말하지 않습니다. 그 문제에 대해서도 C++ 스펙은 그렇지 않습니다. C++에서 그렇게 할 수 있다면 구현 정의 된 동작입니다. –

+0

예, 아마 컴파일러에 의존했습니다. C#에서는 이런 종류의 액세스가 불가능할 수도 있습니다. 나는 잘 모르겠다. 그러나 내부적으로 CLI는 메소드가 RET 명령에서 완료된 후에 어디로 가야 하는지를 저장해야합니다. 나는 실제 환경에서 사용해야하는 무언가가 아닌 해킹을 찾고있다. – Luz

+3

스택은 CLR이 소유 한 데이터 구조입니다. 스택을 사용하는 방법은 구현 세부 사항이므로 해당 데이터 구조를 사용하는 방법에 대해 아무런 가정을 할 수 없습니다. 예를 들어 반송 주소를 변경 한 다음 보안 스택 워크가 발생하거나 예외가 발생했다고 가정합니다. 올바르게 수행하려면 CLR에서 스택의 데이터 구조가 올바른 것이 필요할 수 있습니다.그것을 바꾸지 마십시오. 그것은 당신의 것이 아닙니다. –

답변

2

음은 OpCodes.Ret IL 명령은 정말 어떤 점프를하지 않습니다. 대신 IL 코드는 CLR에 의해 기본 기계 코드로 컴파일되고 이 실행됩니다. 일부 IL 코드에 대해 생성되는 머신 코드는 아키텍처 (x86, ARM, ...)에 따라 다르며 CLR의 구현 세부 사항입니다.

ret IL 명령어는 ret x86 명령어로 컴파일되지만, 예를 들어 전체 메소드가 인라인 될 수 있다고 가정하는 것이 좋습니다.

이렇게하려면 포인터를 사용하여 스택을 수정하십시오. 그 이유는 x86 ret 주소가 저장되기 때문입니다.하지만 이렇게하면 매우 위험합니다 (잘못된 메모리를 쉽게 수정할 수 있음). 쉽게 변경할 수있는 스택의 레이아웃에 의존). 내가 Ctrl 키를 사용하여 디버그 모드에서 내 컴퓨터에이 를 실행하면 + F5는, 그것은 인쇄

using System; 

static class Program 
{ 
    static void Main() 
    { 
     A(); 
    } 

    static void A() 
    { 
     Console.WriteLine("A before"); 
     B(); 
     Console.WriteLine("A after"); 
    } 

    static void B() 
    { 
     Console.WriteLine("B before"); 
     C(); 
     Console.WriteLine("B after"); 
    } 

    static unsafe void C() 
    { 
     int local; 

     int* localPointer = &local; 

     localPointer[2] = localPointer[4]; 
    } 
} 

:

A before 
B before 
A after 
A after 
예를 들어

다음 코드를 보면

이것은 호출 스택을 MainAB으로 변경 한 것을 보여줍니다. → C ~ MainAAC.

그러나 내가이 얼마나 깨지기 쉬운 보여 B 또는 C에 로컬 변수를 추가 할 때 F5를 사용하여 실행하거나, 릴리스 모드에서 실행할 때 작동을 멈 춥니 다.

그럼, 수행 할 수 있지만 (교육용 제외) 절대해서는 안됩니다.

+0

솔직히 말해서 나는 여기에도 교육적인 혜택이 없다고 생각합니다. 호출 스택/호출 규칙이 작동하는 방식을 알고 싶다면 관리 코드는 사용하기에 매우 잘못된 매체입니다. –

+0

좋은 대답 :) 그래서 일리노이는이 수준에서 조작을 허용하지 않지만 기계어 코드로 컴파일 한 후 C/C++와 비슷한 방식으로 작동합니다. 나는 그것이 교육이라고 생각한다. 적절한 프로그래밍을 위해서는 나쁜 예가 될 수 있지만 보안 관점에서 보면 매우 흥미 롭다. – Luz