반응형

StackFrame구성 - Stack Overflow

 

CallStack에 대한 이해를 돕고자 간단한 Quiz로 포스팅을 시작하겠습니다.

아래 와 같은 코드가 있습니다.

void Dummy()

{

           printf("In Dummy Function()\n");

 

           exit(0);

}

 

void SomFunction()

{

           BYTE Buf[4];

 

           printf("In SomFunction()\n");

            

           return ;

}

 

int wmain(int argc, _TCHAR* argv[])

{

           printf("Before call SomFunction()\n");

 

           SomFunction();

          

           printf("After call SomFunction()\n");

          

           return 0;

}

 

asm 코드를 쓰지 않고 SomFunction()을 수정하여 아래와 같은 결과를 출력한 후, 프로그램이 종료되도록 수정할 수 있을까요?

 

. 감이 오지 않는 분들은 아래의 글을 먼저 읽고 오시기 바랍니다.

[디버깅을 위한 기초지식 #3] CallStack - 프로시저 호출에 따른 스택의 구성

 

이제 어느 정도 방향을 잡으셨나요?

 

아래와 같은 방법을 사용하면 위의 문제를 해결할 수 있을 것 같습니다.

1.     Return Address가 저장되어 있는 Stack상의 Address를 구한다.

A.     asm 코드를 사용하지 않으려면, 해당 주소를 구하기 위해 로컬변수 Buf address를 통해 구해야 할 것 같습니다.

2.     1에서 구한 Address Dummy() 함수의 Address Overwrite하게 되면, SomeFunction이 모든 처리를 마치고 리턴할 때 Dummy()가 실행되게 됩니다..

생각보다 복잡하지는 않군요.

 

그럼 한단계씩 따라가 보겠습니다.

1.     SomeFunction이 복귀할 ReturnAddress가 저장된 Stack상의 주소 구하기

A.     스택 구성에 대한 링크된 포스트의 그림에 따르면 첫번째 로컬 변수의 위치는 ebp-0x4이므로, szBuf의 주소+0x4(szBuf사이즈) ebp의 주소가 됩니다.

B.     Rerturn Address의 주소는 ebp+0x4이므로, [ Buf+0x4/*ebp*/+4/*ebp+4*/ ]로 계산이 되겠습니다.

void SomFunction()

{

           BYTE Buf[8];

           DWORD *pDw = (DWORD *)(Buf+0x8/*ebp*/+4/*ebp+4*/);

 

           *pDw = (DWORD)Dummy;

 

           printf("In SomFunction()\n");

            

           return ;

}


 

2. 그럼 이제 구해진 주소에 Dummy()함수의 주소를 write해주기만 하면 되겠군요

*pDw = (DWORD)Dummy;



혹시 위의 코드를 기반으로 혹은 코드를 작성해서 따라해 보신분…?

머야? 안돼잖아~ 하신분도 있으리라 생각됩니다.

 

저도 테스트를 하다 보니 CallStack내에서의 첫번째 로컬 변수의 주소가 항상 ebp – 0x4의 주소에 위치하지는 않더군요.

 

위의 테스트 코드는 아래의 환경하에서 정상 동작했습니다.

n  VC++ 2008, Release모드, optimize off

 

n  Optimize(최적화)옵션을 켜게 되면, SomFunction()함수의 코드가 wmain()함수 안에 삽입되어 버리거나, stack frame이 생략되는 등 실제 작성된 코드와는 많은 부분이 달라질 수 있습니다.
실제로 아래의 코드는 [ release mode: 최적화-속도 최대화 ]를 설정하여 컴파일한 코드로 SomFunction()의 코드가 wmain()함수 안쪽에 박혀(?)있습니다.


 

n  또한 링크된 포스트의 그림에서 설명한 대로라면 SomeFunction()안의 szBuf변수의 사이즈를 8로 잡을 경우, 첫 번째 로컬변수의 주소가 ebp-0x8로 할당될것으로 생각했으나, 실제로는  ebp-0xc위치로 할당되었습니다.(어떠한 다른 규칙이 있는 것인지 제가 잘못 이해한것인지? 답을 아시는 분은 좀 알려주세요~)


 

별 활용성은 없는 코드이 겠으나, 위의 내용을 완벽히 이해하시면 CallStack BackTrace라던지 함수 호출 흐름 분석에 꽤 도움이 되지 않을까 생각합니다.


반응형
반응형


이 그림을 이해하는 것만으로도 (물론 대부분의 경우 디버거가 해주겠지만) 디버거의 도움을 받지 않더라도 함수 호출관계, 파라미터, 변수 등에 대한 많은 부가 정보를 읽을 수 있습니다.

프로그램 경력이 미천할 때( 지금도 마찬가지 입니다만…) 드라이버 개발자 분께서 어셈코드를 보고, 파라미터가 몇 개인 함수고,,, 인자가 어쩌고~ 하시면서 분석하는 과정을 경이롭게 지켜보았던 적이 있었는데… calling convention stack frame의 구성에 대한 이해가 수반된다면 얼추 흉내는 낼 수 있을거 같습니다. .

 
2011/07/25 - [디버깅] - Stack Frame구성 - Stack Overflow

2011/07/18 - [디버깅] - [디버깅을 위한 기초지식 #4] Calling Convention

반응형

+ Recent posts