지금까지 배운걸 간단히 정리하면 다음과 같다
(세그먼트4는 세그먼트 n이라고 보셈.. 숫자를 잘못 입력했다는..)
8086메모리는 4기가 메모리 기준으로 2기가 커널 영역과 2기가 2기가 유저 영역이 존재한다.
유저 영역은 세그먼트 라는 단위로 프로세스들이 들어가는데 CPU는 이 세그먼트를 병렬적으로 처리를 한다.
하나의 세그먼트는 오른쪽과 같이 여러개의 구역으로 나뉘어 진다. 그렇다면 이제는 스택에 코드가 어떤식으로 쌓이는지 살펴보겠음
스택은 다 알거라 생각하고 진행하겠다!!!!!
위 코드는 설명을 돕기 위해 걍 아무렇게 나 짠 코드다.
스택에는 지역변수, 함수들의 매개변수, 등이 들어간다고 했다.
보통 코드를 보면 대략적인 흐름은 다음과 같다.
메인 함수에는 test라는 지역변수가 선언되어 있고 SUM이라는 함수가 매개변수 1,2를 가지고 있다.
SUM 함수안에는 c라는 지역변수가 3의 정수 값으로 초기화 되어 있다.
사실 이제는 코드 영역에 들어가는 어셈블리어를 잘 알아야 이해가 쉽다.
나도 완벽하게 아는 것은 아니지만 공부를 하면서 내가 이해한 선에서 설명을 하겠다.
일단 esp, ebp를 알아보자.
esp는 스택에 데이터가 계속 쌓일 때 스택의 가장 높은 곳을 가리키게 된다. push를 하면 TOP 부분이 계속 높아지는데 이 TOP 부분을 가리키고 있는 것이 바로 esp이다.
주소 상으로는 스택은 높은 주소에서 낮은 주소로 쌓이니까 현재 스택의 가장 낮은 주소를 가리키고 있다
push, pop을 통해 esp가 위아래로 왔다갔다 하면서 스택의 크기를 변경하게 된다
ebp는 함수가 호출되면 스택 프레임이 형성이 되는데 이 스택프레임의 시작 공간을 가리킨다.
스택프레임이 머냐면 그냥 코드상에서 함수가 호출 될때마다 "너는 스택의 여기서부터 여기까지 공간을 쓰렴!" 하면서 할당되는 공간을 뜻한다
처음에는 메인함수가 호출되므로 메인 함수에 대한 스택 프레임이 형성되고 메인 함수 안에서 또다른 함수가 호출되면 그 함수에 대한 스택 프레임이 형성되고 또 그 함수안에서 어떤 함수가 호출되면 또 그 함수의 스택 프레임이 형성되는 식으로 진행된다.
그럼 현 시점의 스택프레임에서 함수가 진행되다가 역할을 다하여 리턴이 되면 그 전의 함수에서 호출했던 시점으로 다시 돌아가야하지 않겠는가?
따라서 스택프레임이 형성될 때에는 이전 함수의 위치로 다시 돌아갈 수 있게끔 ret 라는 주소가 저장된다. 이는 좀있다 다시 설명하겠고 스택프레임에 대해서 그림으로 표현하면 다음과 같다.
검은색 색깔 부분이 현재 소스코드 부분임. 그리고 오른쪽 상단 부분이 현재 설명하고자 하는 영역이다. 일단 코드에는 메인함수와 SUM이라는 함수가 존재한다.(SUME이거 SUM임... 사진 바꾸기 귀찮아서..)
1. 함수 호출 시
그럼 우선 스택에는 메인함수에 대한 스택 프레임이 형성된다.
함수가 호출되는 시점에는 이미 그 함수에 대한 매개변수가 선언이 되어 있어야 한다.
그러면 스택프레임이 형성되는 시점에는 이미 매개변수에 대한 정의가 있어야 한다는 소리이다.
현재 메인 함수에서는 매개변수가 없기 때문에 그냥 스택 프레임이 바로 형성되고 ret, sfp, 변수들이 스택에 들어간다.
ret, sfp, test가 스택프레임에 들어가고 다시 SUM 함수가 호출이 된다.
SUM함수에는 매개변수 1,2가 있기 때문에 SUM함수에 대한 스택프레임이 형성되기 전에 1,2에 대한 정보가 스택에 들어가 있는걸 볼수 있다.
(함수 호출 규약은 여러가지가 있는데 가장 대표적인것이 cdecl 임. 이것은 따로 알아보시길)
그러면 SUM 함수는 이 매개변수를 가지고 스택프레임을 형성한다.
SUM함수 역시 ret, sfp 가 스택 프레임에 들어가고 c 변수가 들어간다.
*정리* 함수가 호출되때 진행되는 위의 초반 과정을 프롤로그라 한다.
프롤로그
push ebp
mov ebp,esp
노란색 부분이 프롤로그 부분
2. 함수 반환 시
이제 함수가 일을 다하고 반환을 하는 시점을 살펴보쟈
함수가 호출될 때 마다 스택 프레임이 형성된다고 했는데 이렇게 계속 진행이 되다가 함수가 일을 끝냈으면 해당 함수에 대한 스택프레임을 싹다 지우게 된다.
일을 다했는데 잔여물을 남길 필요가 없지 않겠는가?
따라서 ret, sfp가 여기서 사용된다.
ret는 함수가 다 끝나고 나면 호출된 CALL 명령 다음에 실행할 부분의 주소가 담긴다.
sfp는 이전 함수의 베이스 포인터의 주소가 저장되어 있다.
함수가 호출될 때를 프롤로그라 했는데 함수가 종료할 때는 에필로그가 수행된다.
*에필로그* leaveret
leave : mov esp, ebp 와 pop ebp를 합친 의미
ret : pop eip 와 jmp eip를 합친 의미
mov esp, ebp는 esp를 ebp에 복사한다는 뜻이므로 esp가 위로 올라와 ebp와 같은 공간을 가리킴
pop ebp는 현재 esp가 가리키는 값을 빼서 ebp에 저장한다는 뜻이다. 이게 먼소리냐면 현재 esp가 이전 함수의 베이스 포인트를 가리키고 있으니까 그 값을 빼서 ebp에 넣는다는 소리이다.
그렇게 되면 SUM함수 이전에 메인한수의 스택프레임의 SFP의 값을 저장되어 있는 공간을 esp가 가리키고 있으므로 해당 값을 ebp에 넣으면 ebp는 위로 쭉 올라고 esp는 pop을 했기 때문에 한칸 위로 올라간다.
(pop ebp를 통해 현재 ebp가 메인 함수 스택 프레임 쪽으로 올라간다는걸 저렇게 오른쪽으로 표시한거임.) 이렇게 SUM함수의 스택프레임이 정리가 된다.
스택 프레임이 정리되는 과정
이렇게 SUM함수의 스택프레임이 정리가 되고 esp는 SUM함수의 매개변수를 가리키고 있다.
이 부분도 정리를 해줘야 하지 않겠는가?
따라서 메인 함수의 어셈블리어 부분을 보면 add esp 0x10 이 부분이 있는데 이게 바로 SUM함수에서 사용되었던 매개변수 부분을 정리해주는 부분이다.
아까 함수호출 규약을 말했는데 cdelc에서는 함수를 호출한 부분에서 호출한 함수에 해당하는 스택의 내용을 정리한다
따라서 메인함수의 add esp 0x10을 통해 매개변수부분을 정리해준다.
매개변수가 두개라서 8바이트만큼만 정리해주면 된다고 생각했는데 그 사이에 먼가 더 시스템내에 어떤 값이 들어 간거 같다.
최종적으로 다음과 같은 그림이 된다.
메인 함수도 위와 같은 과정으로 종료가 되고 프로그램이 아마 종료될 것이다.
3. 결론
스택은 위와 같이 진행이 된다. 정리를 하기전 나름 그래도 잘 알고 있다고 생각했는데 직접 정리를 하다보니 헷갈리는 부분도 굉장히 많았다. 역시 사람을 삽질을 해야 공부가 더 되는 거 같다.
이제 다음 시간 부터 ftz의 문제 풀이를 해볼 생각이다.
그럼 안녕