PE파일이란?
Window에서 사용되는 파일 형식으로 exe, dll, sys 등
운영체제에 의해 메모리에 로드되고 실행되는데 필요한 주요 데이터들이 포함되어 있다
가상 메모리에 매핑된 후에도 실행 전후 레이아웃이 거의 변하지 않는 특성
PE파일의 구조
PE파일은 헤더와 여러 섹션들로 이루어져 있고
모든 헤더들은 구조체 Struct 자료구조로 되어있음
헤더에는
- DOS Header
- DOS stub
- PE Header(Image_NT_Header)
- Section Table
섹션에는
- .data: 전역변수 저장
- .rdata: 읽기 전용 데이터 섹션
- .rsrc: 아이콘, 이미지 등의 리소스
- .bss: 초기화되지 않은 전역변수
- .text: 컴파일된 기계어 코드가 있음
등의 다양한 섹션이 있고 개수에 제한은 없다고 한다
각 헤더와 섹션들은 밑에서 자세히 알아보자
IMAGE_DOS_HEADER
DOS 파일의 하위 호환성을 위한 헤더로, PE 파일의 처음 64비트에 해당한다.
처음 2바이트에 MZ문자를 통해서 윈도우 운영체제가 PE 파일인 것을 인식할 수 있다.
IMAGE_DOS_HEADER의 처음과 마지막 부분에는 각각 e_magic, e_lfanew이 있음
- e_magic: MZ가 아니면 로더가 PE파일로 인식 X
- e_lfanew: NT Header구조체의 위치를 오프셋으로 가리킴
IMAGE_DOS_STUB
Image_DOS_Header의 바로 다음에 위치해 있음
DOS 환경에서 실행되는 코드와 !This program cannot be run in DOS mode라는 메시지가 포함되어 있다.
크기가 일정하지 않으며 실행에 필수적이지 않다
IMAGE_NT_HEADERS
NT_HEADERS
: DOS 헤더의 e_lfanew 오프셋 값으로 시작주소를 알 수 있다. NT_HEADERS내에 하위 3가지가 있다.
- Signature : NT_HEADERS의 처음 4바이트에 해당함
- FILE_HEADER: NumberOfSections, Machine, TimeDateStamp, Characteristics 등의 필드가 있다
- OPTIONAL_HEADER: AddressOfEntryPoint, ImageBase과 같이 PE 파일에서 가장 중요한 정보가 있다. 좀 더 자세히 알아보자
헷갈리지 않게 구조에 맞게 표기하자면
Header/Image_NT_Headers/NT_Headers/Optional_Headers 이런 구조이다
OPTIONAL_HEADER
- Magic: PE파일이 32비트인지 64비트인지 알려준다. 0x010B → 32-bit 0x020B → 64-bit
- AddressOfEntryPoint: 윈도우의 실행 시작 위치, 즉 실행될 첫 Instruction을 가리킴. 오프셋 값이라 ImageBase 값에 더해야 함 BaseOfCode and BaseOfData: 코드섹션과 데이터 섹션의 상대적 주소
- ImageBase: 해당 PE 파일이 메모리에 로드되는 가상주소 값
- Subsystem: 이미지 실행에 필요한 서브시스템
- DataDirectory: PE 파일의 import/export 정보
실행파일에서 가장 먼저 실행되는 코드의 가상주소 : ImageBase + AddressOfEntryPoint 값을 더하면 됨
IMAGE_SECTION_HEADER
섹션 헤더 혹은 섹션 테이블이라고 불린다. PE 파일에는 각 기능(코드, 아이콘, 이미지)에 따른 다른 섹션들이 있는데 섹션의 메타데이터를 가지고 있는 것이 바로 섹션 테이블이다. 헤더가 메모리의 한 페이지에 로드되면 섹션 개수, 위치, 각 권한(protection) 등에 대한 정보가 헤더 내에 Section Table에 있음
→ OS 로더는 헤더의 섹션 테이블을 먼저 읽고 각 섹션을 로드 및 매핑할 수 있음
각 섹션들의 종류와 기능
- .text: 기계어 코드가 있는 섹션으로, 무결성을 위해 WRITE를 제외한 권한이 있다 RX
- .data: 전역변수가 저장돼있고 실행 권한을 제외한 읽기/쓰기 권한이 있음
- .idata: import 할 dll 이름과 dll의 API이름이 존재→ 실행파일의 기능 엿볼 수 있음, 매핑 과정에서 바인딩을 거쳐 메모리에 로드될 때 데이터에 변화
- .ndata: 초기화되지 않은 데이터
- .rsrc: UI에 필요한 아이콘, 이미지 등의 리소스 포함
각 섹션의 헤더에 있는 정보
- VirtualAddress: 메모리의 상대적인 가상주소(RVA)
- VirtualSize: 메모리에 로드되었을 때 섹션의 크기
- SizeOfRawData: 메모리에 로드되기 전 디스크에 저장되어 있을 때의 섹션의 크기
- Characteristics: 섹션의 권한. (READ, WRITE, EXECUTE)
4개의 섹션이 있다면 각각에 대한 4개의 IMAGE_SECTION_HEADER이 있고, IMAGE_SECTION_HEADER들이 모인 게 섹션 테이블!
IMPORT TABLE(IMAGE_IMPORT_DESCRIPTOR)
Image import descriptor 구조체에는 PE 파일이 로드될 때 필요한 API 정보가 담겨있어 PE 파일의 기능을 파악하는데 유용하다.
예를 들어 PE파일이 CreateFileAPI을 임포트 한다면 파일을 생성한다는 것을 추측할 수 있다.
- 실제 위치는 섹션 내 .idata 섹션이고 위치확인은 PE파일 헤더 내에 있는
- IMAGE_OPTIONAL_HEADER\IMAGE_DIRECTORY_ENTRY_IMPORT 에서 확인 가능
- 각 Descriptor 들은 DLL 이름, API 이름 리스트 등 정보 가지고 있음
- 배열의 끝에는 NULL로 표시되어 Descriptor의 끝을 알림
- PE-COFF 파일이 실행 과정에서 참조해야 하는 DLL, API 관련 정보를 구조화, 유지하는 게 임포트 테이블
- 저장매체 상에서, 메모리 실행 단계에서 내용이 달라짐
- 로더는 실행파일을 메모리에 매핑한 후 바인딩을 해서 API 주소 확보
- 확보한 API 주소들은 IAT(Import Address Table)에 기록 → 실행파일이 호출
- 주로 .idata 섹션에 임포트 테이블이 위치하고 DLL 이름, 함수 이름, API 정보 포함
IMAGE_IMPORT_DESCRIPTOR의 구성요소
: 실행파일이 참조하는 DLL 개수와 Descriptor의 개수가 유사함
OriginalFirstTHUNK
- TimeDateStamp
- ForwarderChain
- Name: 참조하는 DLL 이름
- FirstThunk: 포인터 값, 함수의 주소 값
ASLR
가상 메모리 공간에서 주소 값이 고정이 아닌 임의로 설정되는 기술
DLL, API 주소 값은 빌드 단계에서 알 수 없고, 실행 후 메모리 임의 주소로 매핑해서 악성코드 공격을 어렵게 함
바인딩
메모리에 로드된 DLL 및 함수의 실제 주소를 임포트 테이블에 기록하는 과정,
프로그램 실행 시 로더에 의해 시작되는 정적 바인딩/실행 도중 필요할 때 이루어지는 동적 바인딩으로 분류
바인딩 이후 IAT에 실제 함수들의 주소 입력됨
'리버싱' 카테고리의 다른 글
PE 파일의 주요 자료구조 분석 섹션 테이블 실습 CFF Explorer (0) | 2025.02.21 |
---|---|
PE 파일의 자료구조 헤더 분석 실습 Stud_PE, x32 dbg (0) | 2025.02.21 |
프로세스의 가상주소공간 VAS 검증을 위한 실습 x64dbg (0) | 2025.02.18 |
운영체제 프로세스 가상주소공간 Virtual Adress Space (0) | 2025.02.17 |
윈도우 실행파일의 생성과 실행과정 (0) | 2025.02.17 |