반응형

다음과 같은 소스로 실습을 진행할 것이다.

정수인자 a,b,c,d를 가진 test_function을 선언했고, 함수의 지역변수로  flag와 10개의 문자를 가진 buffer를 선언했다. 이 변수들의 메모리는 '스택 세그먼트'에 있고, 함수의 기계어 코드는 텍스트 세그먼트에 저장되어 있다.

디스어셈해보면 main은 다음과 같다.

<main>

test_function은 다음과 같다

main() 함수는 0x08048357 부터 시작한다. sub esp,eax(0x08048365) 부분 까지를 우리는 '프롤로그'라 부르며, 스택 프레임을 구성한다

프로그램이 실행되면 test_function()을 호출하는 main()함수가 호출된다.test_function()이 main() 함수에서 호출될 때 다음과 같이 스택의 시작점을 만들기 위해 많은 값이 스택에 푸시된다. test_function()이 호출되면 함수의 인자가 역순으로 스택에 푸시가되므로(FILO구조) 우리는 소스에서 1,2,3,4 순으로 넣엇지만 4,3,2,1순으로 푸시된다. 이 값들은 각기 함수의 d,c,b,a에 대응된다.


어셈블리 명령이 실행되면 복귀 주소가 스택에 푸시되고, 실행 흐름이 test_function()의 시작점인 0x08048344로 점프한다. 


call 명령은 복귀 주소를 스택에 저장하고 EIP를 test_function()의 시작점으로 점프한다. 그래서 test_function()의 프로시저 프롤로그 명령은 스택프레임을 구성하는 것을 끝마치고 , 여기서 EBP의 현재값은 스택에 푸시된다. 이 값을 우리는 앞에 설명한 'SFP'라 한다. 이것은 나중에 EBP를 원상태로 복귀하는데 사용한다.

프레임 포인터는 함수의 지역변수(flag, buffer)를 참조하는데 사용한다.

프로시저 프롤로그가 끝난뒤의 스택 프레임의 모습은 다음과 같다.

 buffer

 flag

 SFP

ret 

a

b

c

d


이제 그러면 GDB를 사용해서 스택에 구성된 스택 프레임을 살펴보도록 하자.

main()에서 test_function()을 호출하기 전과 test_function() 시작 부분에 중지점을 설정할 것이다. 프로그램을 실행하면 레지스터의 ESP, EBP, EIP를 살펴볼 수 있는 곳에서 실행이 멈춘다.

<1번 break>

이 중지점은 test_function() 스택 프레임이 생기기 바로전에 있다. 이것은 이 스택 프레임의 바닥이 ESP의 현재값인 0xbffffa30이라는 것을 의미한다. 다음 중지점은 test_function() 프로시저 프롤로그 바로 다음이라서, 계속하면 스택 프레임이 구성된다. 아래의 결과는 두 번째 중지점에서 비슷한 정보를 보여준다. 지역변수(현재 소스에서는 flag, buffer)는 프레임 포인터(EBP)에서 상대적 위치로 참조된다.

스택에 스택프레임이 구성됬다. 함수의 네 개 인자가 스택프레임의 바닥에 있고, 복귀주소는 바로 그위에 있다(0x804838b) 또 그위에는 앞의 스택 프레임인 EBp(0xbffffa48)이 있고, 나머지 주소는 지역 변수 flag와 buffer 용이다. EBP와의 상대적 주소를 계산에 스택프레임에서의 정확한 위치를 알 수 있다.


함수의 실행이 끝나면 전체 스택 프레임이 스택에서 제거되고 EIP는 함수 호출 전에 하던 작업을 계속 실행할 수 있게 복귀주소로 설정된다. 호출된 함수에서 또 다른 함수가 호출되면 추가적인 스택 프레임이 생성되는 과정이 반복 된다.


(1) 이론 에서 설명했다 싶이 힙과 스택은 모두 크기가 동적으로 변하며 서로를 마주보는 방향으로 증가한다. 이런 구조는 메모리 공간 낭비를 최소화 하며, 힙이 작을 때는 스택이 커질수도 있고, 힙이 클때는 스택이 작아 질 수 있는 구조를 가진다.


반응형
,