windows-0x02
0x02 STACK
What is the STACK?
STACK은 Main 함수를 포함한 각 함수들이 생성하는 지역변수, 결과값 그리고 함수가 필요로 하는 인자들을 저장할 수 있는 메모리 영역입니다. 이 영역은 LIFO(Last In, First Out)이라는 데이터 구조를 가지고 있으며, CPU에 의해 관리되고 최적화되는 영역이기 때문에 데이터를 읽고 쓰는 속도가 매우 빠릅니다. 또한, 어떤 함수가 변수를 선언하면 그 새로운 변수는 스택에 생성되고(push), 함수가 종료될 때 함수가 생성한 모든 변수, 값등은 자동으로 해제됩니다.
How does the STACK work?
STACK을 설명할 때 가장 먼저 떠오르는 개념이 바로 LIFO입니다. LIFO는 Last In First Out의 앞 글자를 딴 용어이고 한글로는 후입선출이라고 합니다. 설명을 하자면 ‘마지막에 들어온 것이 먼저 나간다’이죠. 아래 그림을 보면서 설명을 이어나가겠습니다.
STACK은 위 그림처럼 한 쪽이 막혀 있고 입구가 하나인 구조를 가지고 있습니다.
앞에서 제가 마지막에 들어온 것이 먼저 나간다라고 했는데, 들어오는 동작. 값을 STACK에 저장하는 동작을 push라고 합니다.
반대로 나가는 동작. 값을 STACK에서 빼내는 동작을 pop이라고 합니다. 이것이 STACK의 기본 구조이며 동작입니다.
그럼, 다음 코드를 가지고 STACK 동작 및 용어들을 조금 더 살펴보도록 하겠습니다.
#include <stdio.h>
void plus(int input) {
int output = input+2;
printf("Result : %d\n", output);
}
int main(int argc, char* argv[]) {
int a = 4;
plus(a);
return 0;
}
위 코드는 입력 값에다 2를 더해서 출력해주는 간단한 내용입니다. 아래 [그림 1-1]은 plus 함수가 동작하고 있는 특정 순간의 STACK 상황을 표현한 내용입니다.
STACK은 STACK Frame들이 모여서 만들어진다고도 표현합니다. 풀어서 설명을 좀 해보자면, 각 함수들마다 주어진 동작을 위한 공간이 주어지는데 이런 공간 하나 하나를 STACK Frame이라고 할 수 있겠습니다. 이 공간들이 모여 STACK을 구성하는 것이죠.
위 그림의 오른편을 보면, Main Function STACK Frame이라고 적힌 것을 보실 수 있습니다. 그 아래로 Plus Function STACK Frame이라고 적혀 있죠.
위 그림처럼, 하나의 함수가 동작하는 중에 다른 함수가 동작되어지는 상황에서는 새롭게 실행되는 함수를 위한 공간을 마련합니다. 그리고나서 함수가 종료되면 마련해놓았던 공간을 해제합니다.
STACK은 Heap과는 다르게 여러분들이 직접 할당, 해제할 필요없이 자동으로 진행이 됩니다.
마지막으로, 함수가 실행되어 동작하는 과정에서 STACK에 값을 저장하거나, STACK의 값을 참조하는 등의 행동을 할 때는 Offset을 가지고 합니다.
그런데 이 Offset이라는 게 어떤 지점까지의 거리니까 기준점이 있어야겠죠? 그 기준점으로 주로 활용되는 것이 EBP와 ESP입니다.
새로운 함수를 위해 Frame을 구성하면 제일 먼저하는 동작. 함수의 프롤로그 과정이죠.
push ebp
mov ebp,esp
새로운 함수를 위해 Frame을 구성하면 제일 먼저 프롤로그 과정을 통해 기준점을 생성합니다. 이 기준점을 생성할 때는 아무 값이나 설정하는 것이 아니라 이전 STACK Frame의 기준점을 가지고 옵니다.
그래서 STACK에 그림으로 표기할 때, EBP(Extended Base Pointer)라는 용어와 SFP(Saved Frame Pointer)라는 용어도 같이 사용하죠.
이전 STACK의 기준점을 가지고와서 현재 STACK Frame의 기준으로 쓰다가 함수가 종료되는 시점. 에필로그 과정에서 다시 되돌려 줍니다.
mov esp, ebp
pop ebp
pop eip
이 과정을 통해 EBP를 원래대로 돌려줄 뿐만 아니라, 이전 Frame 안의 다음 주소로 이동하게 됩니다. Offset의 기준을 ESP로 잡더라도 EBP를 새로운 STACK Frame에 구성하는 행위는 변함없습니다.
아래는 지금까지 설명한 내용들을 정리한 내용들입니다.
-
SFP
-Saved Frame Pointer는 현재 STACK Frame의 지역 변수들을 위한 기준점 역할을 합니다.
-예전 STACK Frame이 가지고 있던 EBP가 현재 STACK Frame의 EBP(Extended Base Pointer)에 저장됩니다.
-
함수 호출
-함수가 호출되면, 새로운 STACK Frame이 생성됩니다.
-함수 동작에 필요한 인자들을 STACK에 저장합니다.
-현재 EBP와 Return Address가 저장됩니다.
-ESP는 새로운 STACK Frame에 맞추어 변경됩니다.
-
함수 종료
-함수 사용에 필요했던 STACK Frame이 제거됩니다.
-EBP가 이전에 저장했뒀던 값으로 되돌려지고, Return Address로 코드의 흐름이 바뀝니다.
-ESP는 되돌아온 STACK Frame에 맞추어 변경됩니다.
STACK에 대한 이야기는 이쯤해두고, 다음 연재부터는 본격적으로 Stack based overflow를 다루어보겠습니다.