티스토리 뷰

728x90
반응형

[Reversing] 스택프레임(Stack Frame)


스택프레임

- 새로운 함수가 호출 될 때 호출 후 원래의 위치로 돌아갈 정보를 담고 있습니다.

- ESP, EBP 레지스터를 사용해서 변수나 원복주소에 접근하는 방식입니다.

- 즉, 함수가 호출 될때 그 함수가 가지는 공간 구조입니다.

함수를 호출할 때 그 함수가 가지고 있는 공간으로 스택 프레임 구조는 함수 프롤로그 - 함수 원형 - 함수 에필로그로 구분할 수 있다. 


스택 프레임 예제

#include <stdio.h>


int main()

{

int x;

printf("정수입력 : ");

scanf_s("%d", &x);

printf("%d\n", x);

return 0;

}

[소스코드]


스택 프레임의 구조

[예제 스택프레임 구조]


[main() 함수의 스택 프레임]


x64 dbg

- 우선 main() 함수를 찾습니다.

- 4012A3 main( )함수 입니다.

- 이후 main() 함수가 끝이나고 돌아오는 주소를 보시면 4012A8 입니다.


main() 함수 시작 시 스택의 상태

- 현재의 EBP 18FF88, ESP 18FF44 입니다.

- 현재 ESP 18FF44에 4012A8 저장되어있습니다.(main함수 종료 후 리턴 주소)


401090 PUSH EBP

- main() 함수는 바로 스택 프레임을 생성한다고 합니다.

- 나중에 함수가 종료되기전에 이 값을 원복 시켜줍니다.

EBP 18FF88


401091 MOV EBP, ESP

- main() 함수가 끝날 때까지는 EBP는 변경되지 않습니다.(함수의 스택 프레임 생성)

EBP 18FF40

ESP 18FF40

- 18FF40 주소에는 18FF88(main() 함수의 EBP) 값 저장


~ 함수의 본체


4010D4 MOV ESP, EBP(main() 함수가 시작할 때의 초기값 원복)

4010D6 POP EBP(원래의 EBP 값 복원)

RETN

- 스택 프레임의 구조와 같이 마지막 위 명령로 스택 프레임은 해제 됩니다.

- main() 함수의 스택 프레임은 해제되면서 스택 프레임이 생성 되었을 때와 같은 모습(ESP, EBP)으로 돌아옵니다.

[MOV ESP, EBP / POP EBP / RETN 부분]


중간정리

 PUSH EBP

 MOV EBP, ESP

 ~ 함수의 본체

 MOV ESP, EBP

 POP EBP

 RETN

잠시 스택 프레임의 구조를 간략하게 설명하기 위해 작성한 부분입니다

이제 함수의 본체를 알아보기 위해 401091 MOV EBP, ESP 이후 명령부터 다시 시작하겠습니다.

현재 스택의 상태의 상태를 보면 18FF40 => 18FF88(main() 함수의 EBP), 18FF44 => 4012A8(main() 함수 리턴 주소) 입니다.


401093 SUB ESP, 8

- 변수(x) 값을 저장하기 위해 메모리 공간 확보하는 부분입니다.

- ESP에서 8을 빼서 공간을 확보한다라고 할수있습니다.

- 현재 ESP는 18FF38 입니다.

- 참고로 리버싱 핵심원리에서는 long 타입의 2개의 변수를 선언해서 4byte * 2(a, b) 총 8byte를 확보했습니다. 8byte 보다는 커야된다는거겠죠.

- int x; 선언한 변수는 이후 scanf() 함수에서 변수 x의 값을 입력 받아 저장이 될것 입니다.


401096 MOV EAX, DWORD PTR DS:[41E004]

40109B XOR EAX, EBP

40109D MOV DWORD PTR ss:[EBP-4], EAX

- 해당 명령을 한줄씩 보게 되면, 

DS:[41E004] 값을 EAX에 저장 => 

EBP(18FF40)를 EAX값과 XOR => 

XOR 한 EAX 값을 [EBP-4](18FF3C)에 저장(스택가드 값)


4010A0 PUSH PROJECT1.41C788

- 다음주소(18FF34)에 41C788 주소 저장

- 41C788에는 printf("정수입력 : ");  "정수입력 : " 문자열이 해당 부분이 있습니다.


4010A5 CALL project1.401020

- 해당 명령은 Pinrtf() 함수를 호출하는 부분입니다.

- printf() 함수에 대한 어셈블리는 구글링(비슷한 맥락)

[printf() 함수의 스택 프레임]


중간정리

소스코드로 보게되면 int x; , printf("정수입력 : "); 까지 진행했습니다.

401093 메모리 공간 확보, 변수 x에 관한 내용을 선언하였습니다.

401096 ~ 40109D 해당 부분은 스택 버퍼오버플로우를 탐지하기 위해 XOR을 통하여 스택가드가 적용되어있습니다.

4010A0, 4010A5 "정수입력 : " 문자열을 pritnf() 함수를 통하여 출력하였습니다.


4010AA LEA EAX, DWORD PTR SS:[EBP-8]

- LEA는 주소를 가져오는 뜻입니다. MOV는 해당 값을 가져오지만 LEX 주소입니다.

- [EBP-8]의 주소를 가져와 EAX에 저장하라는 뜻이 되겠습니다.(이유는 다음 PUSH EAX 에서)


4010AD PUSH EAX

- 생각해보면 EAX 18FF38은 이전 위에서 변수(x) 값을 저장하기 위해 공간을 8 확보한 주소이죠!

- EAX 18FF38을 다음주소에 넣어줍니다.


4010AE PUSH PROJECT1.41C794

- 41C794에는 scanf_s("%d", &x); 의 "%d" 해당 부분이 있습니다.


4010B3 CALL PROJECT1.401050

- 해당 명령은 Scanf() 함수를 호출하는 부분입니다.

- scanf() 함수에 대한 어셈블리는 구글링(비슷한 맥락)

[scanf() 함수의 스택 프레임]


- 해당 명령을 진행하면서 변수(x)의 값(저는 숫자 7)을 입력했습니다.

- 해당 변수(x) 값은 18FF38에 저장이 되었습니다.


중간정리

scanf() 함수까지 진행했습니다.

저는 7을 입력하여 18FF38에는 7이 저장되었습니다.


4010B8 PUSH DWORD PTR SS:[EBP-8]

- [EBP-8]은 변수(x) 값을 말하는 거죠!

- [EBP-8]에 있는 7을 다음주소에 넣어줍니다.


4010BB PUSH PROJECT1.41C798

- 41C798에는 printf("%d\n", x); 의 "%d\n" 해당 부분이 있습니다. 

- 현재까지 printf() 함수 2번, scanf() 함수를 사용했습니다.

- 여기서 한번 생각을 해보면 main() 함수의 EBP 값을 기준으로 엑세스를 진행한다는것 입니다.

- [EBP-8]에 변수(x) 값이며,  스택에는 반대로 저장이 된다는것 입니다.(당연한거죠 스택은 올래 그런거니 LIFO)


4010C0 CALL PROJECT1.401020

- 이전과 같은 printf() 함수 호출 부분입니다.


RETURN & 마무리

나머지 부분은 위에서 정리를 했었습니다.


Project1.exe

[파일]

728x90
반응형
댓글
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today