Stack Frame
# include <stdio.h> void A(int arg1, int arg2){ B(arg1, arg2); } void B(int arg3, int arg4){ printf("%d, %d", arg3, arg4); } void main(){ int a = 1; int b = 2; A(a, b); }
코드는 Calling Convention 에서 사용한 코드를 가져왔다.
컴파일 하고 info stack 으로 스택프레임을 확인 해보면 아래와 같이 나온다.
사진은 Calling Convention 에서 x86 으로 실행했던거라 주소가 x64랑 다르지만 스택 프레임은 같기 때문에 그냥 썼다.
함수 프롤로그 & 에필로그
이 스택프레임을 만들고 제거하는 것은 함수 프롤로그, 에필로그이다.
이게 스택프레임을 만들어주는 함수 프롤로그이다.
모든 함수의 시작에 이 프롤로그가 들어간다.
이건 함수 에필로그이다.
프롤로그와 에필로그를 진행하면 어떻게 되는지 알아보고 왜 사용하는지 알아보자.
일단 B 함수의 0번째 라인에 멈춰 있다.
이떄의 스택 상황은 다음과 같다.
push rbp로 기존 rbp 가 스택에 삽입된다.
이 rbp는 이전 스택 프레임의 rbp 이다.
rsp 가 가리키는 곳에 rbp가 들어갔다.
이를 SFP(Stack Frame Pointer)라고 부른다.
근데 여기서 sfp 밑에 있는 값의 정체가 무엇인지 궁금할 것이다.
Call은
push rip
jmp Operand
이기 때문에 call 할 때 rip가 스택에 삽입된다.
SFP 밑에 있는 값은 이때 삽입된 rip 로 이후 볼 에필로그의 ret 에서 실행된다.
이 다음 위 명령을 실행하면 다음과 같이 된다.
여기까지가 프롤로그 이다.
에필로그는 이렇게 만든 스택 프레임을 정리해주는 것으로 앞에서 했던 과정을 반대로 한다.
leave 직전의 스택이다.
leave 는
mov rbp, rsp
pop rbp
로 실행을 하면 다음과 같아진다.
mov rbp, rsp 로 rsp는 rbp가 가리키던 곳을 가리키고 있고
pop rbp로 sfp 가 rbp에 pop 되고 rsp는 pop 되었으므로 8바이트 아래를 가리킨다.
ret 명령어는
pop rip
jmp rip
로 rsp가 가리키는 ret를 pop 해서 rip 에 넣고 jmp 시킨다.
이 과정이 함수 프롤로그, 에필로그 이다.
이 글은 옵시디언을 이용해서 작성되었습니다.
'TOOR' 카테고리의 다른 글
[TOOR] 5.2 NXbit (0) | 2023.09.19 |
---|---|
[TOOR] 5.1. BOF (내 안의 버퍼가 넘친다!) (0) | 2023.09.19 |
[TOOR] 4.Handray (0) | 2023.09.19 |
[TOOR] 2.Calling Convention (0) | 2023.09.19 |
[TOOR] 1.Memory Structure (0) | 2023.09.19 |