스택프레임
: 함수가 사용하는 스택의 영역으로 호출된 함수의 지역변수, 매개변수, 반환 주소 등이 있다
함수 호출마다 새로운 스택 프레임 생성과 제거의 과정이 이루어짐
- EBP: 함수의 스택 프레임 시작주소 /Base Pointer → 고정
- ESP: 함수의 스택 프레임 끝 주소 /Stack Pointer → 변함
main 함수가 실행되면 main 함수의 스택 프레임, 다음에 A()가 실행되면 A함수의 스택 프레임이 생성되는 구조, 함수 실행 끝나면 프레임도 무효화됨(삭제는 X)
프로세스 흐름이 main에서 A함수로 넘어가면 EBP 프로세스 역시 A 함수의 시작 부분으로 이동
스택 프레임 생성과 종료
함수가 실행된 직후 함수에 의해 스택 프레임이 생성됨
함수 시작 직후 스택 프레임을 시작하는 코드를 ‘함수 프롤로그’라고 함
→ 새로운 함수가 실행/스택 프레임이 생성된다는 의미
함수 종료 직전 스택 프레임을 클로징 하는 코드를 ‘함수 에필로그’라고 함
→ 함수가 종료되기 직전 함수에 의해 소멸됨
프롤로그 에필로그 예시
int add(int a, int b) {
int sum = a + b;
return sum;
}
정수 a와 b를 더해서 반환하는 매우 간단한 C언어 코드인데
이게 어셈블리언어에서 어떻게 이루어지고 프롤로그와 에필로그 구조를 살펴보자
add:
push ebp ; (프롤로그) 이전 함수의 베이스 포인터를 스택에 저장
mov ebp, esp ; (프롤로그) 현재 스택 포인터를 베이스 포인터로 설정
sub esp, 16 ; (프롤로그) 지역 변수 공간 할당 (이 예시에서는 16바이트)
mov eax, [ebp+8] ; 첫 번째 인자(a)를 eax 레지스터에 로드
add eax, [ebp+12] ; 두 번째 인자(b)를 eax에 더함
mov [ebp-4], eax ; 결과를 지역 변수 sum에 저장
mov eax, [ebp-4] ; sum의 값을 eax로 이동 (반환할 값 준비)
mov esp, ebp ; (에필로그) 스택 포인터를 베이스 포인터로 복구
pop ebp ; (에필로그) 이전 베이스 포인터를 복구
ret ; (에필로그) 함수 호출 위치로 리턴
프롤로그에서는 이전 함수의 베이스 포인터(ebp)를 스택에 저장하고, 현재 스택 포인터를 베이스 포인터로 설정한 후, 지역 변수를 저장할 스택 공간을 할당
에필로그에서는 스택 포인터를 베이스 포인터로 복구하고, 이전 함수의 베이스 포인터를 스택에서 꺼내 복구해 리턴 주소로 점프하여 함수 호출 직후의 코드로 돌아간다
Stack Frame Pointer Omission
- EBP 베이스 포인터 레지스터를 스택의 프레임포인터로 사용하지 않고 생략하는 컴파일방법
- 일반적으로 함수가 호출되면 EBP가 스택 프레임 설정의 기준이 되는데 이를 유지하는데 필요한 명령어와 리소스 손실이 있음
- 그래서 EBP 없이 ESP만으로 매개/지역 변수를 추적하는 방식을 도입
- EBP 레지스터를 다른용도로 사용할 수 있기 때문에 프로그램의 성능향상과 메모리, CPU 절약에 도움을 줌
- 대부분의 컴파일러에서 최적화 옵션형태로 제공 Visual Studio (/O2, /FPO), GCC(--fomit-frame-pointer)
- 컴파일러는 런타임에 사용되는 주소들을 미리계산해야 하므로 컴파일단계에서 부담이 커짐
- 베이스포인터가 없으면 변수 위치를 추적하기 어렵고 스택프레임개념이 없어지므로 분석자 입장에서 디버깅이 어려워짐
'리버싱' 카테고리의 다른 글
가상주소공간의 스택메모리 - 호출된 함수의 복귀주소와 분석 (0) | 2025.03.03 |
---|---|
리버싱 :: 가상주소공간 VAS 스택메모리 (0) | 2025.02.28 |
IA32 어셈블리 주요 명령어 이론과 실습 (0) | 2025.02.25 |
CPU 레지스터와 주요 명령어 - IA32 주요 레지스터 (0) | 2025.02.24 |
PE 파일의 자료구조 임포트 테이블 분석 실습 (0) | 2025.02.24 |