스택에 대해 알아보자.
알고 있을진 모르겠지만 스택의 역할은 뭐..
로컬 변수 임시 저장, 함수 호출 시 파라미터 전달, 리턴 어드레스 저장 등이 있다.
구조는 후입선출이다. Last In First Out
가장 마지막에 넣은 녀석부터 다시 튀어나온다는 것.
(POP연산을 한다고해서 튀어나온다고 표현을 했는데
실제로는 그 값은 살아 있지만 후에 나올 esp가 pop연산을 만나면
자동으로 4바이트 아래로 내리기 때문에 튀어나오는 것 처럼 보이는거.)
스택은 메모리에 높은 주소 쌓이는 이유는 힙과 마주보며 쌓이면서 메모리를 좀더
효율적으로 사용할 수 있도록 하기 위해서 이렇게 설계되었다고 하는데
구체적인 이유가 2가지 정도 있다.
1. 높은 주소부터 거꾸로 스택을 채워나가면 스택영역이 커널영역에 침범할 일이 없어진다.
스택이 만약 작은 주소부터 할당되었다면 접근 불가 영역인 커널에 도달하려는 일이 생기겠지.
2. 힙과 스택이 중간에 공유라이브러리 영역을 두고 효율적으로 메모리 할당을 하기 위해서.
[ 힙→ 공유라이브러리 ←스택 ]
그럼 이제 실제 코드가 스택에 어떻게 쌓이는지 한번 알아보자.
예제는 Newheart의 smashing the stack이라는 문서를 참고하였다.
이런 예제코드가 있음.
프로그램에서 사용되는 환경변수, 함수파라미터 등이 쌓인다고 했다.
그럼 위 코드에서 나오는 것들 대부분이 스택에 쌓일 것이다.
일단 스택에 대해 공부하기 전에 esp, ebp 의 쓰임에 대해 알아야 한다.
esp는 프로그램이 실행되고 스택이 차곡차곡 쌓일 때 스택의 가장 최상부를 가르킨다. (주소상으론 가장 낮은 주소)
스택은 cpu에 의해 계속 push, pop 명령을 수행하므로 커널에서 스택의 크기를 계속 변경한다.
esp의 위치도 계속 바뀌겠지.
ebp는 설명하기 전에 스택 프레임이라는 개념을 알아야겠다.
최초에 함수가 메모리에 호출 되면 스택 프레임 (Stack Frame)이라고 하는 함수가 사용할 공간을 잡아준다.
이 공간은 ebp와 esp 사이의 공간이라고 생각하면 된다.
함수가 호출되면 일단 그 함수의 파라미터들이 먼저 스택에 쌓이고 그 후에 CALL명령이 떨어지게 된다.
함수가 다 끝나고 나면 그 다음 실행 되어야 할 부분이 있어야 할 것 아닌가.
그래서 스택에 RET (Return Address)가 파라미터 다음에 쌓이게 된다.
RET에는 현재 함수가 호출된 CALL 명령 다음에 실행할 부분의 주소가 담긴다.
굳이 따지자면 호출 이전 함수로 돌아가는 것.
그리고 난 후 이전 함수의 프레임 포인터 (FP라고 하겠다.)가 저장되어야 한다.
그래야 현재 함수가 끝나고, 이전 함수로 돌아갔을 때의 스택 프레임을 복원 할 수 있다.
게다가 현재 함수 사용 중에 이전 함수의 지역 변수 등을 참조할 경우에도 쓰인다.
이것을 SFP (Saved Frame Pointer) 라고 말한다. 쉽게 ebp에 저장된 위치가 SFP라고 보면 된다.
(함수가 사용하는 스택영역의 기준점. 스택에 쌓이는 것들은 ebp를 기준으로 위치를 파악한다.)
그리고 난 후 최초에는 스택에 쌓이 지역변수가 없으므로, esp는 SFP가 저장된 메모리의 주소를 갖고있다.
이 때 ebp에 esp의 값을 복사한다.
이 과정을 프롤로그(Prolog)라고 하는데, 바로 아래 그림이다.
반대로 함수가 끝나고 스택을 비우는 과정을 에필로그(Epilog) 라고 한다. 이건 좀 뒤에서 언급하겟음.
위에서 설명했다시피, Return Address는 call 명령으로 호출된 함수에서 리턴 후 실행될 주소이고,
SFP는 지금 호출된 함수를 불러낸 caller의 프레임포인터를 저장한 것이다.
지금 이까지 과정이 main함수의 프롤로그이다.
그럼 이제 선언한 지역변수와 함수 호출 시에 어떠한 일이 생기는지 보자
프롤로그에 대해 설명했고, 함수 호출 시 파라미터가 먼저 스택에 쌓이는거 언급했고,
프롤로그 후에 함수의 지역변수가 스택 메모리에 할당된다는 것 얘기 했으니
위에 나온 소스코드 통째로 그냥 스택에 올려보겠다.
CALL Func()까지 마치면 스택은 위와 같은 상황이 된다.
스택 할당에 대한 내용은 요쯤 하면 웬만큼 이해를 할 수 있다.
'IT 그리고 정보보안 > Knowledge base' 카테고리의 다른 글
바이트 오더링 (Byte Ordering) (0) | 2021.04.17 |
---|---|
함수 호출 규약 (Function Calling Convention) (0) | 2021.04.17 |
x86 메모리 구조 (0) | 2021.04.17 |
리버싱(Reversing) 그리고 올리디버거(Ollydbg) (0) | 2021.04.17 |
어셈블리 - REP 명령어 (0) | 2021.04.17 |