리버싱을 위한 프로그램 실행 구조

컴퓨터의 기본 구성 요소 : CPU, Memory, HDD
- 프로그램을 실행할 수 있는 파일(exe)은 기본적으로 HDD에 저장됨.
PE(Portable Executable) 파일 = 윈도우의 실행파일
- Header와 Body로 구성됨
- Header : 프로그램을 실행하는 데 필요한 기본 정보 & 저장 위치를 알려주는 배치 정보 포함
- Body : Code & Data
- exe, dll, ocx 등 다양한 종류가 있음
PE 포멧 (Header + Body)로 구성된 실행 파일 클릭
→ OS에 있는 Loader는 PE Header 정보를 분석 → PE Body에 있는 코드와 데이터를 Memory에 배치

|
Code
|
프로그램 코드가 들어감
|
|
Data
|
정적 변수 & 전역 변수
|
|
Heap
|
동적 메모리 할당
|
|
Stack
|
함수 호출 시 사용되는 매개 변수 & 지역 변수
|
PE 코드가 메모리에 로딩될 때 Code 영역과 Data 영역에 자료가 들어감
→ 프로그램이 실행되면서 Stack 영역과 Heap 영역에 데이터가 쌓이게 됨.
C언어로 개발된 프로그램은 기본적으로 main()부터 실행
Entry Point = PE 파일 실행이 시작되는 주소
OS는 메모리에 있는 PE 파일을 실행하기 위해서 PE Header 정보에서 Entry Point를 찾아서 그곳부터 프로그램을 실행함.

프로그램 내의 명령어 실행 → CPU & Control Unit(CU) & Arithmetic/Logic Unit(ALU)
CU와 ALU가 프로그램을 실행하는데 필요한 Data는 모두 Registers → Data를 Registers로 복사하는 과정 필요
하나의 프로그램이 CPU를 사용하려면 자신에게 필요한 데이터로 Register의 내용을 전부 바꿔야함
여러 프로그램이 동시에 실행되기 때문에 번갈아 가면서 CPU를 사용함
Context Switching : CPU 사용이 끝나면 다른 프로그램에 CPU 사용 권한을 넘겨주는데, 자신이 사용하고 있던 모든 Registers를 Memory 영역으로 복사하는 것
Register
: CPU에서 사용하는 고속의 기억 장치
CPU는 연산을 수행하기 위해서 Memory에 있는 데이터를 CPU 내부의 Register로 가지고 온다.
연산 중간에도 Register에 데이터를 저장함.

|
RAX, EAX, AX
|
곱셈과 나눗셈 명령에서 사용, 함수의 반환값을 저장
|
|
RBX, EBX, BX
|
인덱스에 사용
|
|
RCX, ECX, CX
|
반복 명령어를 사용할 때 반복 카운터를 저장, 반복할 횟수를 저장
|
|
RDX, EDX,DX
|
EAX와 같이 사용, 부호 확장 명령
|
|
RSI, ESI, SI
|
데이터 복사하거나 조작시에 소스 데이터 주소 저장
|
|
RDI, EDI, DI
|
복사 작업 시 목적지의 주소가 저장, RSI/ESI 레지스터가 가리키는 주소의 데이터 복사
|
|
RBP, EBP, BP
|
하나의 스택 프레임의 시작 주소가 저장
|
|
RSP, ESP, SP
|
하나의 스택 프레임의 끝 지점 주소가 저장
|
|
RIP, EIP, IP
|
다음에 실행할 명령아가 저장된 메모리 주소가 저장, 현재 명령어를 모두 실행한 다음 레지스터에 저장된 주소에 있는 명령어가 저장됨. 실행전에 RIP,EIP 레지스터에는 다음에 실행해야 할 명령아가 있는 주소값이 저장된다.
|
Stack
: 메모리의 한 부분, LIFO(Last Input First Output) 방식으로 동작하는 특별한 자료구조

POP과 PUSH 두 가지 동작을 지원함
POP : 데이터를 Stack에서 꺼내는 동작
PUSH : 데이터를 Stack에 집어넣는 동작

- Stack에는 한 방향으로만 데이터가 쌓임
- PUSH를 하면 Stack의 (시작)주소는 4byte 감소하면서 데이터가 Stack으로 들어간다.
- POP을 하면 Stack에서 데이터가 꺼내지고 Stack의 (시작)주소는 4byte 증가한다.
- Stack은 시작 주소에서 주소가 작아지는 방향으로 데이터가 쌓인다.
- → Stack 구조 위에 있는 데이터가 시스템을 운영하는데 있어서 중요한 역할을 하기때문.
프로그램에서 Stack은 함수로 인자를 전달하고,
함수 내부에서 사용하는 지역 변수가 저장되는 공간을 제공하며,
함수이 종료될 때 되돌아갈 주소를 저장하는 역할
→ 함수를 호출할 때 정확하게 인자를 Stack으로 push하고,
함수가 종료될 때 복귀 주소를 Stack에서 pop함
Stack Frame
: 함수(서브루틴)이 가지는 자신만의 Stack 영역, 함수(서브루틴)에서 사용하는 데이터가 저장

함수가 호출될 때 Stack Frame 생성
함수가 동작을 종료하고 복귀 주소로 돌아갈 때, Stack Frame 소멸
- 함수 호출 시 필요한 인자를 먼저 Stack으로 push → 인자1,인자2
- 함수를 실행하기 직전 다시 돌아올 주소인 복귀 주소를 Stack으로 push → Return Address
- Stack Frame 시작
- 이전 함수가 사용한 RBP/EBP Register 내용을 백업
- RBP/EBP Register가 백업된 위치의 Stack 주소를 push
Frame Pointer = Stack Frame에서 데이터 참조를 위한 기준 주소
Stack에 있는 데이터를 접근할 때 Frame Pointer(RBP/EBP)를 사용하는 이유
: RSP/ESP는 프로그램이 시작되면서 계속 변함. 변하지 않는 RBP/EBP는 변하지 않음.
PE 파일
PE 파일 기본 개념

기계어는 파일로 만들어지는데, exe나 dll 같은 확장자로 만들어짐
→ 이러한 파일들이 모두 PE 파일
PE 파일은 PE 포맷에 따라서 만들어야함.
PE 포맷 : 윈도우에서 만든 프로그램이 실행되기 위해 준수해야 하는 일종의 규칙
PE 파일 종류
|
실행 계열
|
exe, scr
|
|
라이브러리 계열
|
dll, ocx, cpl, drv
|
|
드라이버 계열
|
sys, vxd
|
|
오브젝트 파일 계열
|
dbj
|

PE (Portable Executable) : 윈도우 운영체제에서 사용하는 실행 파일 형식
PE 파일은 PE Header / Section Header / Section Data로 구성됨
( PE Header + Section Header → PE Header라고 통칭하기도 함)
모든 PE 파일은 공통으로 PE Header를 가지고 있다.
Section Header 부분은 PE 파일마다 가지는 내용과 형태가 다름
PE 파일은 최소 1개 이상의 Section Header를 가지고 있음.
Section Data 부분 또한 PE 파일마다 가지는 내용과 형태가 다르며
PE 파일은 최소 1개 이상의 Section Data를 가진다.
|
IMAGE_DOS_HEADER
|
DOS 운영체제가 윈도우용 PE 파일을 실행했을 때 적절한 오류 메세지를 보여줌. 실제 윈도우용 PE Header 위치를 가리키는 역할
|
|
MS_DOS Stub Program
|
DOS 운영체제에서 윈도우용 PE 파일을 실행했을 때 보여줄 오류 메세지를 저장하고 있음
|
|
IMAGE_NT_HEADER
|
4byte Signature, 2개의 IMAGE_FILE_HEADER, IMAGE_OPTIONAL_HEADER 구조체로 구성되어 있음. IMAGE_OPTIONAL_HEADER 구조체 뒤에는 중요한 역할을 담당하는 16개의 IMAGE_DATA_DIRECTORY 구조체가 따라옴.
|
|
IMAGE_OPTIONAL_HEADER
|
PE 구조에서 핵심적인 역할
|
IMAGE_OPTIONAL_HEADER
|
Image Base
|
메모리에 PE 파일이 저장되는 시작주소. PE 파일에서 확인할 수 있는 RVA(Relative Virtual Address)는 메모리에 로딩되면서 Image Base 값과 합산된 주소(VA: Virtual Address)에 저장된다.
|
|
Address of Entry Point
|
프로그램 실행에 대한 제어권이 커널 영역에서 코드 영역으로 처음 넘어오는 주소인 Entry Point를 가리키다
|
|
Base of Code
|
코드 영역이 시작되는 주소 (RVA)를 가리킨다. 디버거로 프로그램을 열었을 때 코드 영역의 맨 윗부분 주소에 해당
|
주소 지정 방법
PE 파일 분석 시 찾는 데이터가 어디에 위치해 있는지는 헤더에 있는 정보를 가지고 찾을 수 있다.
→ 이때 사용하는 값이 주소
주소 형식
|
pFile
|
PE 파일 내부에서의 오프셋(파일), PE 파일이 물리적으로 하드디스크에 저장되었을 때 의미 있는 값
|
|
RVA
|
메모리에 로드됐을 때, 기준값에서 얼마나 떨어져 있는지를 나타내는 상대 위치 (메모리)
|
|
VA
|
가상 메모리상에 저장되는 실제 주소(메모리)
|
DLL (Dynamic Linking Library) : 프로그램에서 필요한 라이브러리를 사용 시점에 동적으로 연결
→ IAT 정보 : DLL을 사용할 수 있도록 지원하는 정보
IAT
실행 파일 (PE)안에 어떤 라이브러리의 어떤 함수를 쓰는지 기록해 놓은 정보
Roader는 PE 파일을 메모리로 로딩할 때
IAT에 기록된 API 이름을 참조해서 / 실제 주소를 찾아 / IAT 안에 API를 가리키는 주소를 적어 놓는다.
코드에서 라이브러리를 참조하는 부분은 IAT 내부에 있는 함수 주소를 사용함.
'Reversing > Introduction to reversing' 카테고리의 다른 글
| abex crackme 5 (0) | 2025.05.05 |
|---|---|
| abex crackme 4 (0) | 2025.05.05 |
| abex crackme 3 (1) | 2025.05.05 |
| abex crackme 2 (0) | 2025.05.05 |
| abex crackme 1 (2) | 2025.05.05 |