김승현

KISA Academy - 리버스 코드 엔지니어링 중급 (16~20강) 본문

교육/KisaGym

KISA Academy - 리버스 코드 엔지니어링 중급 (16~20강)

kshind 2024. 7. 12. 00:06

16강 [이론] 가상주소공간의 스택 메모리 - 스택 메모리 개요

IA32 메모리의 프로세스가 사용하는 가상주소공간에서 대표적으로 많이 사용되는 버퍼 메모리로,

스택이 있음

스택은 호출되는 함수들의 운용을 위해 존재함

 

스택

스택은 탑 포인터가 마지막으로 저장된 데이터를 가리키며, 낮은 주소 방향으로 자라는 "Full Descending Stack" 방식

또한 POP될 땐 후입선출(LIFO) 방식이다.

 

스택은 Top Pointer( ESP의 역할 )가 마지막으로 들어온 값을 가리키느냐 들어올 다음 영역을 가리키느냐에 따라 Full Stack, Empty Stack으로 나눌 수 있음

 

또 0에서 100번지 방향으로 가느냐 혹은 100에서 0번지로 가느냐에 따라

Ascending Stack 혹은 Descending Stack으로 나눌 수 있음

 

Intel32 아키텍쳐가 Full Descending Stack 방식이고 아래는 이 방식에 따라 설명

Full Descending Stack : 방금 PUSH된 객체를 가리키며 100번지에서 0번지로 자라는 스택 구조

 

 


17강 [이론] 가상주소공간의 스택 메모리 - IA32 스택 프레임

 

스택 프레임이란?

함수가 사용하는 고유한 영역

BOX로 존재하고 push되면 위로 쌓이는 느낌 EBP는 스택프레임의 바닥을 가리킴

 

예시)main에서 a라는 함수를 호출하면 스택에는 main의 스택 프레임 시작 지점 이후 main의 지역 변수, 데이터 등이 쌓이고 그 위 a 함수의 스택 프레임에 지역 변수 데이터 등이 쌓이고 ..... 함수가 끝나면 스택 프레임이 참조되지 않고 쓰레기 데이터로 바뀜

 

EBP는 현재 실행 중인 스택 프레임을 바닥을 가리키고 ESP 레지스터는 계속 오르락 내리락 하며 이동됨

 

함수 프롤로그

함수 시작 직후 스택 프레임을 오픈하는 코드

예)

push ebp  

mov ebp, esp

sub ebp, n(숫자)

 

현 스택 프레임의 바닥에는 보통 호출한 함수의 바닥부분이 백업이 됨(push ebp

ebp를 현 스택 프레임의 바닥을 가리키도록 호출(mov ebp, esp)

지역변수 등을 위한 공간 확보(sub esp, n)

 

함수 에필로그

함수 종료 직전 스택 프레임을 클로징하는 코드

 

mov esp, ebppop ebp

 

지금까지 사용한 지역변수 공간 무효화(mov esp, ebp)기존 ebp값(기존 스택프레임 혹은 그 함수의 바닥으로 ebp 보내기)(pop ebp)

 

함수의 프롤로그 이전 함수 스택 프레임의 바닥 백업 -> 이전 함수 스택 프레임의 EBP를 끌어올림 -> 지역 변수 공간 할당함수 에필로그현재 스택 프레임 바닥 끝까지 끌어내림 -> 이전의 스택프레임 바닥을 EBP에 대입한다 -> 대입

 

컴파일러

Stack Frame Pointer Omssion은 EBP레지스터를 스택의 프레임 포인터로 사용하지 않도록 하는 컴파일 방법

레지스터는 고급 인력인데 이걸 바닥 가리키는 데 사용하기엔 아깝기 때문에 나온 방법

 

스택에서 주소로 어떤 위치를 특정하는 방식이 아닌 EBP로부터 거리로 접근 [EBP - 8], [EBP - 4]

 

만약 EBP를 스택프레임의 바닥을 가리키는 방식 외로 사용한다면?

ESP 레지스터를 기준으로 함 [ESP + 4], [ESP + 8] 

 

ESP + N 방식으로 접근을 한다면 Stack Frame Omission 방식이 적용됐구나라고 생각하면 됨

 


18강 [실습] 가상주소공간의 스택 메모리 - IA32 스택 프레임 분석 실습

사용 툴 - IDA, xdbg

sample PE파일의 프롤로그 코드
위 사진은 에필로그 코드인데 leave 함수 하나로 에필로그 코드를 대체함mov esp, ebp와 pop ebp를 대체함

 

f7로 push ebp 실행시 ebp 값인 0061FF68이 push

 


19강 [이론] 가상주소공간의 스택 메모리 - 호출된 함수의 복귀주소

caller함수는 callee함수를 호출할 때 되돌아올 주소값(Return Address)를 스택에 백업함

Return Address는 스택에 백업됨

 

위 방식은 인텔 계열 아키텍처에서 자주 보이며 MIPS 및 ARM 아키텍처는 레지스터에 저장

 

함수는 프롤로그 코드가 실행되면서 스택프레임 젤 아래는 Saved Frame Pointer라는 caller함수의 바닥을 가리키는 값을 가짐 그 위엔 지역 변수를 위한 공간

 

leave 함수 이후 return이라는 코드가 실행됨

return이란 POP EIP라는 값과 같음 return 코드는 return address call한 이후의 다음의 코드를 가리키는 주소을 가짐

 

이 return address는 언제 정의가 되느냐?

-> call A()처럼 함수가 call될 때 정의 됨. call A() = push EIP, jmp A()

 


20강 [실습] 가상주소공간의 스택 메모리 - 호출된 함수의 복귀주소 분석 실습

사용 툴 - IDA, xdbg

 

주요 목적 : Return Address의 생성 과정 및 참조 방법 탐구, 실행의 흐름의 변화 양상 확인

 

메인 함수의 스택프레임이 있다면 SFP가 있고 그 위에 지역변수 저장 영역이 있음

 

Saved Frame Pointer는 main함수를 호출한 스택프레임의 바닥인 tmainCRTStrartup()의 바닥을 가리킴

 

sum 함수를 호출하게 되면 다음 스택에 다음 주소인 00401638을 push한다. 그 다음 sum 함수의 프롤로그 코드가 실행되며sum함수의 Stack Frame이 빌드됨.

해당 프레임의 젤 아래엔 SFP, 즉 main함수의 바닥을 가리키는 게 저장

 

main 함수의 return address

sum함수가 실행되기 전 스택의 모습으로 sum함수 다음 주소인 0x00401638이 스택 젤 위에 push되어 있는 것을 볼 수 있음 해당 주소는 sum함수의 Return Address임.

 

main함수의 return operator가 실행되기 직전의 모습으로 main함수를 호출한 tmainCRTStrartup()를 가리키는 주소를 볼 수있음

바로 이전 사진에서 f7키를 누를시 스택 최상단에 있던 0040138b위치로 이동된 것을 볼 수 있음.