스택프레임
by St1tch스택프레임
EBP(베이스 포인터)레지스터를 사용하여 스택 내의 로컬 변수, 파라미터 ,복귀 주소에 접근하는 기법
ESP레지스터의 값은 프로그램 안에서 수시로 변경되기 때문에 스택에 저장된 변수, 파라미터에 접근하고자 할 때ESP 값을 기준으로 하면 프로그램을 만들기 힘들고, CPU가 정확한 위치를 참고할 때 어려움이 있다. 따라서 어떤 기준 시점(함수시작)의 ESP 값을 EBP 에 저장하고 이를 함수 내에서 유지해주면, ESP 값이 아무리 변하더라도 EBP를 기준으로 안전하게 해당 함수의 변수, 파라미터, 복귀 주소에 접근할 수 있다. 이것이 바로 EBP 레지스터의 베이스 포인터 역할이다.
스택프레임의 구조
PUSH EBP ;함수시작(현재 EBP를 스택에 저장)
MOV EBP, ESP ;현재의 ESP(스택포인터)를 EBP에 저장
... ;함수본체
MOV ESP, EBP ;ESP를 정리(함수 시작했을 때의 값으로 복원시킴)
POP EBP ;리턴되기 전에 저장해 놓았던 원래 EBP값으로 복원
RETN ;함수종료
실습
학습을 하기위해 C코드를 EXE로 만든후 디버거로 분석을 해보았다.
----------------------------------------------------
#include "stdio.h"
long add(long a, long b){
long x = a, y = b;
return (x + y);
}
int main(int argc, char* argv[]){
long a = 1, b = 2;
printf("%d\n", add(a, b));
return 0;
}
----------------------------------------------
디버거로 exe파일을 열면 아래의 그림처럼 나온다.
위의 스택프레임의 구조처럼 함수의 시작은
PUSH EBP
MOV EBP, ESP
로 하고 있는것을 확인할 수 있다.(00401020, 00401000)
함수의 로컬 변수는 스택에 저장된다. 그렇기 때문에 main함수든 add함수든 2개의 로컬 변수를 사용하기 때문에
SUB ESP, 8 이라는 명령어(00401023)를 통해 8BYTE의 공간을 먼저 확보한다음 함수를 진행한다.
함수는 파라미터들을 먼저 스택에 PUSH하고 그후 함수를 CALL하는 형태로 호출된다. 어셈블리어에서는 파라미터가 C언어의 입력 순서와는 반대로 스택에 저장된다. 즉, 여기서는 변수 b가 먼저 스택에 들어가고 변수a가 나중에 들어간다.
(00401034 ~ 0040103B)
함수가 되돌아오기 위한 복귀주소는 EBP+4에 저장된다.
EBP+8부터는 전달받은 파라미터가 저장된다. 파라미터가 2개면 EBP+8에 첫 번째, EBP+C에 두 번째 파라미터가 저장되어있다. 아래에 보면 ADD함수에서 EBP+4에는 ADD함수를 호출한 CALL다음 주소인 00401041가 들어있다.
EBP+8에는 첫번째 파라미터인 1이들어있고, EBP+C에는 두번째 파라미터인 2가 들어있다.
ADD 연산의 결과는 EAX에 저장한다. 보통 함수의 리턴값은 EAX에 저장한다.(00401015)
ADD함수가 끝나면 MAIN함수로 돌아와서 ADD ESP, 8을 해준다.
ADD함수에서 2개의 로컬변수를 할당받는데 쓰인 스택공간을 정리하는 것이다.(00401041)
PUSH EAX
PUSH 0040B384
CALL 00401067
ADD ESP, 8
(00401044 ~ 0040104F)
여기서는 리턴받은 EAX(더한 값)을 스택에 넣고, 문자열도 스택에 넣은후 printf()함수를 호출한다.
마찬가지로 함수가 끝난후에 ADD ESP, 8을 통해 스택을 정리해준다.
그후 main()함수의 리턴값 0을 세팅한다. 어셈블리어에서는 보통 XOR EAX, EAX를 통해 EAX를 0으로 만든다.
그리고 아래의 명령으로 함수가 종료된다.
MOV ESP, EBP
POP EBP
RETN
블로그의 정보
튜기's blogg(st1tch)
St1tch