stack, prologue, leave, ret

임시 자원들을 관리하기 위하여 프로세스는 stack(쓰레드마다 stack이 생성된다.)이라는 메모리 영역을 할당받아 관리한다.
stack 자료구조는 후입선출법(LIFO)으로 나중에 들어온 데이터를 먼저 뽑아쓰는 구조로
stack 메모리는 stack자료구조를 push, pop과 esp, ebp로 구현한다.

esp는 스택의 최상단을 포인팅, ebp는 스택의 베이스를 포인팅하는 레지스터이고
push data는 stack 상단에 data를 넣고 esp를 증가시켜주고 pop reg로 esp가 가리키고 있는 data를 reg에 넣고 esp를 내려주는 명령어이다.

스택메모리에서 함수가 호출 될 때, 함수에서 빠져나올 때 마다 ebp, esp가 셋팅되는 것을 알아보자.

함수가 호출되고 지역변수를 잡은 후의 스택 메모리의 모습이다.(아래가 높은주소)




새로운 함수 호출

새로운 함수의 호출은 call func() 로 실행 될 수 있다. call func()는 push eip(RET backup) + jmp func() 와 같다. 
func() 함수가 끝난 후 돌아올 eip주소를 백업하고(RET),  func()의 기계어 코드가 있는 곳으로 eip를 이동시켜 func() 를 실행한다.

call func() 가 실행된 직후의 스택의 모습은 위의 그림에서 RET 값이 셋팅되고 esp 가 RET를 가리키고 있는 모습이다. ebp는 func()을 호출한 콜러의 ebp를 가리키고 있다.

이제 eip는 func()함수의 프롤로그를 수행 할 차례이다. 함수는 시작부분에 push ebp/mov ebp, esp라는 프롤로그를 갖고 있다.
프롤로그가 실행되면 push ebp로 (SFP)콜러의 ebp가 백업되고 ebp, esp가 SFP를 가리키게 된다. 이렇게 func()의 스택이 초기화 되어 func()는 스택을 독립적으로 사용할 수 있다.

변수의 전달

argv는 call을 하기 전에 스택에 argv를 push하여 func()로 전달하고 ebp+'x'로 값을 얻어쓴다.
local value는 func()의 프롤로그가 실행 된 후 sub esp, 'x'로 esp를 local value만큼 위로 올리고 esp + 'x'로 값을 얻어서 쓴다.

함수에서 콜러로 복귀

func() 함수가 모두 실행 된 후에는 func()를 호출한 콜러로 복귀를 해야한다. 스택 정리를 위해서 leave, ret을 실행 한다.
leave 는 mov esp, ebp/pop ebp로 esp를 ebp위치와 같게 내려 놓고 SFP(콜러의 ebp backup)를 ebp에 넣어 ebp를 이전 함수의 위치로 복귀 시킨다. pop명령어를 실행 하였기 
때문에 esp는 RET를 가리키고 있다. 

그 다음에는 ret(pop eip + jmp eip)가 실행된다. pop eip로 eip에는 RET(call func()의 바로 밑의 주소)이 들어가고 jmp eip로 eip는 func()가 호출된 다음의 코드를 가리키게 된다.
이렇게 되면 func()가 호출되기 이전의 스택모양과 똑같아진다!

0 개의 댓글:

댓글 쓰기