스택 프레임(1)

밍기적·2023년 1월 5일

리버스엔지니어링

목록 보기
2/6

스택 프레임이란?


ESP(스택 포인터)가 아닌 EBP(베이스 포인터) 레지스터를 사용하여 스택 내의 로컬 변수, 파라미터, 복귀 주소에 접근하는 기법을 말합니다

ESP 레지스터의 값은 프로그램 안에서 수시로 변경되기 때문에 스택에 저장된 변수, 파라미터에 접근하고자 할 때 ESP 값을 기준으로 하면 프로그램을 만들기 어렵고, CPU가 정확한 참고할 때 어려움이 있습니다

어떤 기준 시점(함수 시작)의 ESP 값을 EBP에 저장하고 이를 함수 내에서 유지해주면, ESP 값이 아무리 변하더라도 EBP를 기준(BASE)으로 안전하게 해당 함수의 변수, 파라미터, 복귀 주소에 접근할 수 있습니다

스택 프레임의 구조


PUSH EBP 		; 함수 시작(EBP를 사용하기 전에 기존의 값을 스택에 저장)
MOV EBP, ESP 	; 현재의 ESP(스택포인터0를 EBP에 저장

...				; 함수 본체
				; 여기서는 ESP값이 변경되어도 EBP값은 변하지 않기 때문에 
                ; 안전하게 로컬 변수와 파라미터를 액세스 할 수 있음
                
                
MOV ESP, EBP	; ESP를 정리(함수 시작했을 때의 값으로 복원시킴)
POP	EBP			; 리턴되기 전에 저장해 놓았던 원래 EBP 값으로 복원
RETN			; 함수 종료

실습 예제


StackFrame.cpp 파일을 가지고 진행했습니다


#include "stdio.h"

long add(long a, long b)
{
    long x = a, y = b;
    return (x + y);
}

int main(int argc, char* argv[])
{
    long a = 1, b = 2;
    
    printf("%d\n", add(a, b));

    return 0;
}

해당 소스코드를 가지고 실행파일로 만들어 디버깅을 시작합니다

main 함수의 주소인 401020에 BP(Break Point)를 설치 후 실행을 했습니다

이 때 스택의 상태는 아래와 같습니다

현재 ESP 값은 0019FF74이며 여기에는 main() 함수가 끝난 후 리턴 할 주소인 769900F9가 저장되어 있습니다


main() 함수 시작과 스택 프레임 생성

int main(int argc, char* argv[])
{

시작과 동시에 스택 프레임을 생성합니다

먼저 EBP가 베이스 포인터의 역할을 하도록 이전에 가지고 있던 값을 스택에 백업해두기 위한 용도로 PUSH를 합니다

  • 나중에 main()함수가 종료되기 전에 이 값을 회복시켜 줌

두 번째로 ESP의 값을 EBP로 옮깁니다

명령 실행 후 ESP의 값은 EBP와 같도록 설정되며 main() 함수가 끝나기 전까지 EBP 값은 고정됩니다

  • 스택에 저장된 함수 파라미터와 로컬 변수들은 EBP를 통해 접근하겠다는 것

EBP 설정이 완료되었습니다

long a = 1, b = 2;

이제 main() 함수 내 로컬 변수(a, b)를 위한 공간을 만들고 값을 입력합니다


먼저 ESP 값에서 8을 빼는 과정을 진행합니다

현재 ESP 값은 0019FF70입니다

  • 8을 빼는 이유는 로컬 변수를 저장하기위해 long 타입(4 바이트) 2개의 변수 크기를 할당하는 과정입니다

두 변수에게 필요한 메모리 공간을 확보(예약)한 것입니다

EBP 값은 main() 함수 내에서 고정이므로 이를 기준으로 삼아서 로컬 변수에 접근할 수 있습니다

MOV DWORD PTR SS:[EBP-4], 1

  • 간단하게 적으면 [EBP-4] = local 'a' 입니다

실행 후 스택의 모양을 확인해보면 1, 2 순으로 저장된 것을 확인할 수 있습니다

0개의 댓글