작성
·
401
0
스택 프레임 강의에서
1. 스택이 차는 순서가 궁금합니다.
OS가 할당해 주는 가장 큰 스택(함수의 변수들이 들어가는 스택과 구분하기 위해 가장 큰 스택이라고 표현했습니다)
은 high level -> low level 순서로 찬다고 하셨습니다.
그럼 스택 영역 -> 힙 영역 -> 데이터 영역 -> 코드 영역 순서대로 스택이 차게 됩니다.
여기서 궁금한 점이 컴파일 타임에 스택, 힙, 데이터, 코드 영역을 컴파일러가 판단해서 가장 큰 스택을 순서대로 채워줘야 할 것 같은데
그림에 보면 힙 영역은 런타임에 크기가 결정된다고 되어 있습니다. 이 부분이 궁금합니다.
스택 영역 채우고 힙영역 안 차고 데이터 영역 차고 코드 영역 차면 high-[스택 영역-빈 부분- 데이터 영역-코드 영역]-low
이렇게 되는데 이렇게 되면 중간에 빈 부분으로 남아있는 힙을 어떻게 채우는 건가요?
2. 어셈블리어 부분이 조금 헷갈립니다.
00F31710 push ebp
00F31711 mov ebp,esp
00F31713 sub esp,0CCh
F11로 함수부분 뜯어 볼 때 함수 부분의 돌아갈 주솔르 저장한다 하셨는데 이부분이 이해가 잘 가지 않습니다.
지금 esp가 가리키는 부분을 ebp에 저장해서 닻처럼 저장하고 00F31713 sub esp,0CCh 이 부분부터 esp가 돌아다니는 것은
이해가 됩니다.
하지만 00F31710 push ebp 이 부분을 왜 하는지 잘 모르겠습니다. ebp를 왜 스택에 넣어두는 건가요?
답변 3
2
OS가 할당해 주는 가장 큰 스택(함수의 변수들이 들어가는 스택과 구분하기 위해 가장 큰 스택이라고 표현했습니다)
은 high level -> low level 순서로 찬다고 하셨습니다.
그럼 스택 영역 -> 힙 영역 -> 데이터 영역 -> 코드 영역 순서대로 스택이 차게 됩니다.
->
잘못 이해하고 계십니다. [가장 큰 스택]이라는 것은 존재하지 않고 위에서 그림은
전체 메모리 지도를 그려주고 있는 것이고,
우리가 [스택 메모리]라 하는 것은 저 스택 영역을 얘기하는겁니다.
여기서 궁금한 점이 컴파일 타임에 스택, 힙, 데이터, 코드 영역을 컴파일러가 판단해서 가장 큰 스택을 순서대로 채워줘야 할 것 같은데 그림에 보면 힙 영역은 런타임에 크기가 결정된다고 되어 있습니다. 이 부분이 궁금합니다.
스택 데이터 코드는 컴파일 타임 (설정값)에 의해 설정되고,
힙 영역은 런타임에 동적 할당을 통해
(RAM이 버텨주는 만큼) 쭉 늘려서 사용할 수 있습니다.
위 그림은 참고용 지도에 불과하고
실제로 힙 영역 (파란색)은 전체 메모리의 99%의 비중으로 엄청 크게 사용한다고 보시면 됩니다.
스택 영역 채우고 힙영역 안 차고 데이터 영역 차고 코드 영역 차면 high-[스택 영역-빈 부분- 데이터 영역-코드 영역]-low 이렇게 되는데 이렇게 되면 중간에 빈 부분으로 남아있는 힙을 어떻게 채우는 건가요?
위 그림 (메모리 지도상)으로는 마치 고정된 크기를 할당받아 나눠 쓰는 것처럼 보이지만,
절대 그런 것이 아니고 힙 영역은 위에서 얘기한 대로 RAM이 버텨주는 만큼 증설해서 사용 가능합니다.
그리고 응용 프로그램에서 사용하는 메모리는 [가상 메모리]라고 합니다.
그림에서 나오는 저 영역을 '그대로' 활용하는게 아니라,
말 그대로 지도처럼 어디 어디를 활용할지를 정해주는 것에 가깝고
실제 가상 메모리와 매핑된 물리 메모리는 커널에서 따로 관리해주고 있습니다.
한마디로 그림상에서 연속된 공간을 채울 이유가 전~혀 없고
실제로 스택과 힙 메모리를 살펴보면 완전히 다른 공간에 위치해 있는 것도 알 수 있습니다.
지금 esp가 가리키는 부분을 ebp에 저장해서 닻처럼 저장하고
00F31713 sub esp,0CCh 이 부분부터 esp가 돌아다니는 것은 이해가 됩니다.
하지만 00F31710 push ebp 이 부분을 왜 하는지 잘 모르겠습니다. ebp를 왜 스택에 넣어두는 건가요?
해킹 쪽을 공부하면, 저 부분이 특히나 중요한데
쉽게 말해 함수마다 자신의 스택 프레임을 갖게 되고,
함수 내부에서 사용하는 로컬 변수들도 스택 위치 어딘가에 존재하게 됩니다.
그런데 스택 메모리는 어떤 함수를 어떤 순서로 호출했냐에 따라
사용하는 부분이 유동적으로 변합니다. (밀물 썰물과 같은..)
로컬 변수가 아닌 전역 변수였다면 고정 주소일텐데 말이죠
즉 어떤 함수에서 int a = 3;를 했다고 하면,
그 a라는 변수가 위치할 스택 주소는 상황에 따라 달라질 수 있다는 얘기고
항상 닻 (ebp)를 기준으로 ebp-12와 같은 상대 주소로 만들어지게 됩니다.
그래서 어떤 함수가 컴파일되어 바이너리 코드로 만들어지면,
코드 자체가 mov [ebp-12], 3와 같이 ebp 기준으로 뭔가를 하도록 만들어지게 됩니다.
여기서 문제는 함수에서도 또 다른 함수를 호출할 수 있기 때문에,
기존 함수에서 사용하던 [닻] 위치를 안전하게 보존해서
호출한 함수가 끝나면 다시 원래대로 복원시켜야 합니다.
결과적으로 함수마다 스택 프레임이 다르고,
자신이 사용하는 [닻]의 위치가 다르기 때문에 이를 스택에 잠시 보관해주고, 추후 복원시키는 것입니다.
0
0