함수에 진입할 때 스택의 변화
main 함수 진입할 때 스택의 변화
처음부터 순서대로 하나씩 보겠습니다. 리틀 엔디언 기준으로 설명하겠습니다.
Dump of assembler code for function main:
0x08048665 <+0>: push ebp
0x08048666 <+1>: mov ebp,esp
0x08048668 <+3>: and esp,0xfffffff0
0x0804866b <+6>: sub esp,0x10
0x0804866e <+9>: mov DWORD PTR [esp],0x80487f0
0x08048675 <+16>: call 0x8048450 <puts@plt>
0x0804867a <+21>: call 0x8048609 <welcome>
0x0804867f <+26>: call 0x8048564 <login>
0x08048684 <+31>: mov DWORD PTR [esp],0x8048818
0x0804868b <+38>: call 0x8048450 <puts@plt>
0x08048690 <+43>: mov eax,0x0
0x08048695 <+48>: leave
0x08048696 <+49>: ret
End of assembler dump.
1
메인 함수에 처음 진입하면 push ebp
가 먼저 실행됩니다.
push는 스택에 값을 삽입하는 명령어이구요. ebp는 스택의 베이스 포인터 주소입니다.
이게 실행되는 이유는 갱신되지 않은 ebp 즉 이전에 호출한 함수(혹은 프로세스)로 되돌아 가기 위해서 입니다.
현재 스택의 모양은
메모리 주소 | 값 |
---|---|
main 함수 스택의 시작점 | 이전 함수(혹은 프로세스)의 ebp |
2
mov ebp, esp
ebp에 esp를 넣어줍니다.
esp는 방금 push를 하면서 가리킨 주소. 즉, main 함수 스택의 시작점 주소를 ebp에 넣습니다. 이로써 ebp가 새로운 함수의 스택을 가리키기 시작했습니다.
메모리 주소 | 값 |
---|---|
ebp ← esp | 이전 함수(혹은 프로세스)의 ebp |
3
and esp, 0xfffffff0
and 연산으로 esp의 주소(현재 esp는 ebp와 같다) 중 맨 뒷자리만 0으로 초기화한다.
예를 들어
esp : 0x08280cf4
라고 한다면
esp : 0x08280cf0
이 된다.
esp가 이동하였으므로
메모리 주소 | 값 |
---|---|
ebp - n(n은 ebp의 끝자리 16진수) ← esp | |
... | |
ebp | 이전 함수(혹은 프로세스)의 ebp |
4
sub esp, 0x10
esp에서 0x10을 뺀다.
이번에도 esp가 이동하게 된다.
메모리 주소 | 값 |
---|---|
ebp - n - 0x10 ← esp | |
... | |
ebp - n | |
... | |
ebp | 이전 함수(혹은 프로세스)의 ebp |
함수를 호출할 때
Dump of assembler code for function login:
0x08048564 <+0>: push ebp
0x08048565 <+1>: mov ebp,esp
0x08048567 <+3>: sub esp,0x28
0x0804856a <+6>: mov eax,0x8048770
0x0804856f <+11>: mov DWORD PTR [esp],eax
0x08048572 <+14>: call 0x8048420 <printf@plt>
0x08048577 <+19>: mov eax,0x8048783
0x0804857c <+24>: mov edx,DWORD PTR [ebp-0x10]
0x0804857f <+27>: mov DWORD PTR [esp+0x4],edx
0x08048583 <+31>: mov DWORD PTR [esp],eax
0x08048586 <+34>: call 0x80484a0 <__isoc99_scanf@plt>
...
처음에 함수가 호출되면 push ebp
가 발생합니다.