미역줄기의 이모저모
기초 CS- Introduction 본문
드림핵 강의랑 많이 겹치지만.. 이건 정말 공부하고 쓰는 글이라 다듬어지지 않은 날 것의(?) 글이다.
1.1 x86 Memory layout

위의 그림은 x86 환경에서 하나의 프로세스가 가상 메모리에 로드되어 실행될 때의 메모리 구조이다. 최상위에 있는 커널 영역은 접근 못한다 (아님말고) 그 밑으로는 유저 영역의 코드 영역, 데이터 영역, bss 영역, 힙 영역, 스택 영역이다.
코드 영역
- 읽기 권한
- 실제 프로그램을 실행하는 기계어 명령들이 위치한다.
데이터 영역
- 전역 변수와 정적 변수가 위치
BSS 영역
- 초기화 되지 않은 전역변수와 정적 변수들이 위치한다
힙 영역
- 동적으로 할당되는 변수의 데이터가 위치
스택 영역
- 프로그램에서 사용되는 각종 정보(환경변수, 파라미터, 리턴 값 등)
- 함수 내부에서 선언한 지역변수 데이터
- 높은 주소에서 낮은 주소로
공유 라이브러이 영역
- 스택과 힙 영역 사이에 존재
- 공유 라이브러리들이 존재(.so)

1.2 Stack structure during function call
스택은 LIFO(Last In First Out) 구조이다. push와 pop 명령을 통해 스택에 값을 저장하고 다시 가져온다.

#include <stdio.h>
int Func(int num1, int num2, char num3)
{
int i = 1, j = 2;
return 0;
}
int main(int argc, char *argv[])
{
char buf1[20] = "Newheart_main";
Func(1, 2, 3);
return 0;
}
이 코드를 통해서 스택의 흐름을 살펴본다.
1. main 함수 호출 직전의 스택의 상황

- 프로그램 실행 시 처음부터 main이 호출되는 건 아님
- main 함수 호출 직전의 스택의 상황
- main 역시 __libc_start_main() 이라는 함수에 의해서 호출 되므로 main 함수의 인자 argc와 **argv를 파라미터로 설정 후 main 함수를 call 하게 된다.
2. 함수 프롤로그

- call은 실행 후 복귀할 주소를 스택에 저장한 후! 명령을 수행
- call 을 통해 호출 시에 ESP가 가리키던 스택의 주소에 복귀주소가 저장되고 자신을 호출한 Caller의 Frame Pointer를 스택에 저장
- 현재 ESP위치를 EBP 레지스터에 저장하고 이 주소를 현재 함수의 Frame Pointer로 사용
- return address: call 명령으로 호출된 함수 리턴 후 실행 될 주소
- frame Pointer: 지역변수 혹은 함수의 파라미터 값에 접근하기 위한 기준으로 사용
caller의 frame pointer를 스택에 저장하고 현재 함수의 frame pointer를 ebp 레지스터로 설정하는 작업을 함수 프롤로그라고 함
3. main 함수의 지역변수 할당
char buf1[20] = "Newheart_main";

- buf1[20]이라는 지역변수에는 "Newheart_main" 이라는 정적인 문자열이 저장
4. Func 함수 프롤로그
Func(1, 2, 3);

- func 함수가 호출되고 새로운 stack frame이 생성된다
- stack frame: 함수가 호출되어 복귀주소를 저장하는 것 부터 함수가 종료되어 리턴하는 과정까지의 스택 영역
- caller 함수인 main의 frame pointer의 주소를 스택에 저장 후 새로운 frame pointer를 생성
5. Func 함수의 지역변수 할당

6. Func 함수의 에필로그 과정을 통해 main 함수로 복귀

에필로그 수행 전에 add $0x10, $esp 와 같이 ESP 레지스터를 이동시켜 사용했던 지역변수 메모리 공간을 정리한 후에 에필로그를 수행

함수 에필로그 leave-ret은 instruction을 수행한다
현재 FP의 위치로 ESP를 이동하고 POP EBP를 통해 Caller함수의 FP로 복귀하고, POP EIP, JMP EIP를 통해 이전 함수의 흐름으로 복귀 할 수 있다 (EIP레지스터는 다음 실행할 Instruction의 주소를 저장한다)
7. Func 함수 종료 후에 다시 Main 함수의 흐름으로 복귀하여 0을 리턴하고 마찬가지로 main 함수의 에필로그 과정을 통해 main 함수를 종료한다.