ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 7 스택 프레임
    리버싱 엔지니어링/리버싱 핵심 원리 2022. 3. 13. 23:36

    (흐름상 5->6보다 5->7이 이해에 좋은 것 같아 업로드 순서 수정)

    7.1 스택 프레임

    • ESP(스택 포인터)가 아닌 EBP(베이스 포인터) 레지스터를 사용하여 스택 내의 로컬 변수, 파라미터, 복귀 주소에 접근하는 기법
    • ESP 값은 프로그램 내에서 수시로 변경되기 때문에 스택에 저장된 변수, 파라미터에 접근하기 어렵고 CPU가 정확한 위치를 참고하기 어렵다
    • ESP 값을 EBP에 저장하고 함수 내에서 유지하면, 안전하게 해당 함수의 변수, 파라미터, 복귀 주소에 접근할 수 있다.
    • 스택 프레임의 구조
    PUSH EBP        ; 함수 시작(기존의 값을 스택에 저장)
    MOV EBP, ESP    ; 현재의 ESP를 EBP에 저장
    ...
    MOV ESP, EBP    ; ESP 정리(함수 시작했을 때 값으로 복원)
    POP EBP         ; 저장해 놓았던 원래 EBP 값으로 복원
    RETN            ; 함수 종료
    
    • 최신 컴파일러는 최적화 옵션을 가지고 있어 간단한 함수의 경우 스택 프레임을 생성하지 않음

     

    7.2 stackframe.exe

    7.2.1 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;
    }
    

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

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

    1) main() 함수 살펴보기 (main 함수 찾기 : 2장 참고)

    • main() 함수에 BP 설치 후 실행

    2) 함수 시작 시 스택 상태

    • ESP = 19FF2C, EBP =19FF70
    • ESP에 저장된 값 4012A8은 main() 함수의 실행이 끝난 후 돌아갈 리턴 주소

    3) PUSH EBP

    • 의미 : EBP 값을 스택에 집어 넣어라
    • 이전에 가지고 있던 값을 스택에 백업해두기 위한 용도

    4) MOV EBP,ESP

    • 의미 : ESP의 값을 EBP로 옮겨라
    • 명령 후 EBP는 현재 ESP와 같은 값을 가지게 됨
    • main() 함수가 종료될 때까지는 EBP 값 고정됨

    5) EBP 위치 확인 (스택 창 마우스 우측 - Address - Relative to EBP)

    • 현재 EBP, ESP 값이 동일함
    • 19FF28 주소에는 19FF70(EBP 초기 값)이라는 값이 저장되어있음

    7.2.3 로컬 변수 세팅

    long a = 1, b = 2;
    

    1) 메모리 공간 확보

    • 의미 : ESP에서 8을 빼라
      • 8을 빼는 이유? ‘a’와 ‘b’는 long 타입이므로 각각 4바이트 크기를 가짐. 총 8바이트의 메모리 공간을 확보하기 위해

    2) 값 저장

    • 의미 : [EBP-8]에는 1을 넣고, [EBP-4]에는 2를 넣어라
    • 실행 후 스택 상태

    7.2.4 add() 함수 파라미터 입력 및 add() 함수 호출

        printf("%d\\n", add(a, b));
    

    1) 함수 호출 과정

    • [EBP-4] = b, [EBP-8] = a
    • 입력 순서와 반대로 스택에 저장됨
      • 변수 b가 먼저 들어가고 변수 a가 나중에 들어감

    2) add() 함수 복귀주소

    • 함수로 들어가기 전 CPU는 무조건 해당 함수가 종료될 때 복귀할 주소(return address)를 스택에 저장해야함
    • 4010BC 주소의 CALL 명령어 실행 후 스택

    • 4010C1은 add() 함수의 복귀 주소
    • 4010C1은 add() 함수를 호출한 4010BC 다음 명령어

    7.2.5 add() 함수 시작 & 스택 프레임 생성

    long add(long a, long b)
    {
    

    1) 스택 프레임 생성

    • add() 함수가 시작되면 자신만의 스택프레임 생성

    • main() 함수와 동일한 코드 (7.2.2)
    • 원래 EBP 값(main() 함수의 base pointer)을 저장하고 현재의 ESP 값을 EBP에 입력

    7.2.6 add() 함수의 로컬 변수(x, y) 세팅

    long x = a, y = b;
    
    • 로컬 변수 x, y에 파라미터 a, b 대입

    1) 로컬 변수 x, y 스택 메모리 영역(8바이트) 확보

    2) 로컬 변수에 파라미터 값 대입

    • 스택

    7.2.7 ADD 연산

    return (x + y);
    

    1) add 연산

    • 401092 명령 : 변수 x 값을 EAX에 넣음
    • 401095 : EAX에 변수 y값을 더함
      • ADD는 덧셈 연산 명령어
    • 함수가 리턴하기 직전에 EAX에 넣은 값은 그대로 리턴 값이 된다

    7.2.8 add() 함수의 스택 프레임 해제 & 함수 종료(리턴)

    return (x + y);
    }
    

    1) 스택 프레임 해제 및 복원

    • EBP 값을 ESP에 대입
    • add() 함수 시작할 때의 ESP값을 EBP에 넣어두었다가 함수가 종료될 때 ESP를 원래대로 복원

    EBP값 복원

    • 스택
      • 4010C1은 add() 함수의 복귀 주소

    2) 복귀 주소로 리턴

    • RETN 명령어 실행 시 스택에 저장된 복귀 주소로 리턴
    • 스택 : (7.2.4 파라미터 입력 후와 동일)

    add() 함수 리턴 후

    7.2.9 add() 함수 파라미터 제거(스택 정리)

    • ESP에 8을 더하는 이유?
      • 함수에게 넘겨준 파라미터 a, b를 제거하기 위해
    • 스택

    7.2.10 printf() 함수 호출

    printf("%d\\n", add(a, b));
    

    1) 프린트 함수 호출 코드

    • EAX에는 add() 함수 리턴 값이 저장되어 있음
    • printf() 함수의 파라미터는 8비트, 따라서 ADD ESP, 8 명령으로 함수 파라미터 정리

    7.2.11 리턴 값 세팅

    return 0;
    

    • XOR : 같은 값끼리 XOR하면 0됨
    • MOV EAX, 0 명령어보다 실행 속도가 빨라 사용함

    7.2.12 스택 프레임 해제 & main() 함수 종료

    return 0;
    }
    

    • add() 함수와 마찬가지로 스택프레임 해제 후 리턴 (7.2.8)

    '리버싱 엔지니어링 > 리버싱 핵심 원리' 카테고리의 다른 글

    8. abex’ crackme #2  (0) 2022.03.13
    6 abex’ crackme #1 분석  (0) 2022.03.13
    5 스택  (0) 2022.02.06
    2 Hello World! 리버싱(2) - 문자열 패치  (0) 2022.02.06
    4 IA-32 Register 기본 설명  (0) 2022.02.06

    댓글

Designed by Tistory.