1. PE File 소개
PE(Portable Excutable) 파일은 Windows 운영체제에서 사용되는 실행 파일 형식이다. 기존 UNIX에서 사용되는 COFF(Commeon Object File Format)를 기반으로 Microsoft에서 만들었다.
● 분류
- 32비트 형태의 실행 파일 : PE (또는 PE32)
- 64비트 형태의 실행 팡리 : PE+ ( 또는 PE32+)
2. PE File Format
● 종류
종류 | 주요 확장자 |
실행계열 | EXE,SCR |
라이브러리 계열 | DLL, OCX, CPL, DRV |
드라이브 계열 | SYS, VXD |
오브젝트 파일 계열 | OBJ |
2. PE File 기본 구조
각 실행파일에는 UNIX 시스템에서 사용되는 실행파일, 개체 코드 및 공유 라이브러리 컴퓨터 파일의 형식인 COFF(Common Object File Format)라는 공통 형식이 있다. 그리고 PE(Portable Executable)형식은 현재 WIndows 운영체제의 32비트 및 64비트 버전에서 실행파일, 개체코드, DLL, FON 글꼴 파일 및 코어 덤프에 사용할 수 있는 COFF 방식중 하나이다.
그리고 Linux의 ELF(Excutable Link File) 형식이 있다.
PE(Portable Excutable) 파일형식은 WIndows OS 로더에 래핑된 실팽코드를 관리하는데 필요한 정보를 알려주는 데이터 구조이다. 여기에는 연결, API 내보내기, 테이블 가져오기, 리소스 관리 데이터 및 TLS 데이터에 대한 동적 라이버르리 참조가 포함된다.
디스크의 데이터 구조는 메모리에서 사용되는 데이터 구조와 동일하며 PE 파일에서 무언가를 찾는 방법을 알고 있다면 파일이 메모리에 로드된 후 거의 확실하게 정확한 정보를 찾을 수 있다.
PE 파일은 단일 메모리 매핑 파일로 메모리에 매핑되지 않는 다는 점에 유의해야한다. 대신 WIn32 로더는 PE 파일을 보고 매핑할 부분을 결정한다.
메모리의 모듈은 프로세스에 필요한 실팽 파일의 모든 코드, 데이터 및 리소스를 나타낸다. PE파일의 다른 부분은 읽을 수 있지만 매핑할 수는 없다. 예를 들어 디버그 정보가 파일 끝에 배치되는 경우와 같이 일부 부분은 전혀 매핑되지 않을 수 있다.
PE 헤더의 필드는 실행파일을 메모리에 매핑하기 위해 따로 설정해야 하는 메모리 양의 시스템에 알려준다. 매핑되지 않는 데이터는 매핑될 부분을 지나서 파일 끝에 배치된다.
PE 데이터 구조에는 DOS Header, DOS Stub, PE File Header, Image Optional Header, Section Table, Data Dictionaries, and Sections.이 포함된다.
HxD 로 메모장(notepad.exe) 을 열어 자세히 살펴보자
(윈도우에 기본제공 되는 notepad.exe를 열어도 되지만 실습과 똑같은 값을 얻고 싶다면 예제파일안의 notepad.exe.를 이용하자)
2.1 DOS Header
DOS 헤더는 파일의 처음 64바이트를 차지한다. 위의 이미지와 같이 HxD로 프로그램을 열었을때 처음 4개 행이다. 파일의 시작부분에 언급되는 ASCII 문자열 "MZ"를 볼수 있는데 이 MZ 는 5Ah 4Dh로 읽히는 DOS 헤더의 처음 두바이트(16진수 : 4D5A 또는 0x54AD)를 차지한다.
MZ는 MS-DOS 개발자 중 한명인 Mark Zbikowski의 이니셜이다. 이필드는 e_magic 또는 매직넘버라고 하며 MS-DOS 호환파일 유형을 식별하는 필수 필드이다. 모든 MS-DOS 호환 실행파일은 이 값을 0x5A4D( ASCII의 "MZ")로 설정한다.
마지막 필드인 e_lfanew는 4바이트 오프셋 (F0 00 00 00) 이며 PE File 헤더(NT Header)가 있는 위치를 알려준다.
2.2 DOS Stub Program
스텁은 애플리케이션 실행이 시작될 때 기본적으로 실행되는 작은 프로그램 또는 코드 조각이다. 프로그램이 Windows 와 호환되지 않을 때 "This program cannot be run in DOS mode" 라는 메시지를 출력한다.
따라서 Win32를 지원하지 않는 환경에서 Win32 기반 프로그램을 실행하면 오류 메시지가 표시된다.
이경우 실행 파일이 로드될 때 real-mode stub 프로그램이 MS-DOS에 의해 실행된다. Windows 로더가 PE 파일을 메모리에 매핑할 때 매핑되는 파일의 첫 번째 바이트는 MS-DOS 스텁의 첫 번째 바이트에 해당한다.
2.3 PE File Header ( NT Header)
PE Header는 MS-DOS 헤더의 e_lfanew 필드를 보면 찾을수 있다. e_lfanew 필드는 PE 헤더 위치의 오프셋을 제공한다. 필드 e_lefanew는 새 .exe 헤더의 파일 헤더를 나타낸다( NT Header) 기본 PE 헤더는 IMAGE_NT_HEADERS 유형의 구조이며 주로 SIGNATURE, IGMAGE_FILE_HEADER 및 IMAGE_OPTIONALHEADER 를 포함한다.
SIGNATURE : 4바이트 Dword Signature 이다. 아래 사진의 경우 PE 헤더에 대한 오프셋 세트는 000000E0이고 PE Signature는 50 45 00 00 에서 시작한다. (문자 PE 뒤에 두 개의 종료 00 이 있음)
IMAGE_FILE_HEADER : 파일 헤더는 PE 파일의 다음 20바이트이며 파일 레이아웃에 대한 가장 기본적인 정보만 포함한다.
위 그림에서 드래그한 부분은 이식 가능한 실행파일의 헤더를 나타낸다.
※ IMAGE_FILE_HEADER 필드 참고
IMAGE_OPTIONAL_HEADER : IMAGE_FILE_HEADER 데이터 구조에 포함된 기본 정보 이외의 몇 가지 중요한 정보가 포함되어 있다.
- Magic : Magic 필드는 실행 가능한 이미지가 32비트인지 64비트인제 알려준다. Magic 필드에 설정된 값은 IMAGE_NT_OPTIONAL_HDR_MAGIC 이며, 그 값은 32비트 어플리케이션에서는 IMAGE_NT_OPTIONAL_HDR32_MAGIC(0x10b) 이며, 64비트 어플리케이션에서는 IMAGE_NT_OPTIONAL_HDR64_MAGIC(0x20b)로 정의된다.
- AddressOfEntryPoint : Windows 로더가 실행을 시작할 주소이다. 모듈의 진입점(EP)의 RVA(상대 가상주소)를 보유하며 일반적으로 .text 섹션에서 찾을 수 있다. 실행파일의 경우 시작 주소이다. 장치 드라이버의 경우 초기화된 함수의 주소이다. 진입점 함수는 DLL에 대해 선택 사항이며 진입점이 없으면 이 멤버는 0이다.
- BaseOfCode 및 BaseOfData 멤버는 각각 코드 및 데이터 섹션 시작부분의 RVA를 보유한다.
- ImageBase : 실행파일이 메모리의 특정 위치에 메모리 매핑될 주소이다. Windows NT 에서 실팽하일의 기본 이미지 기반은 0x10000 이고 DLL 경우 기본 이미지는 0x400000이다. Windows 95의 경우 주소 0x10000은 모든 프로세스가 공유하는 선형 주소 영역에 있기 때문에 32비트 EXE를 로드하는 데 사용할 수 없다. 이 때문에 Microsoft는 Win32 실행 파일의 기본 기본 주소를 0x400000으로 변경하기로 결정, 따라서 기본적으로 응용 프로그램의 경우 0x400000이고 DLL의 경우 0x10000000이다.
- SectionAlignment 및 FileAlignment: 두 멤버는 각각 메모리와 파일에서 PE 섹현의 정렬을 나타낸다. 실행파일이 메모리에 매핑되면 해당 실행 파일의 각 섹션은 이 값의 배수인 가상 주소에서 시작한다.
- SizeOfImage : SizeOfImage 멤버는 런타임에 PE 파일이 차지하는 메모리 크기를 나타낸다. SectionAlignment 값의 배수여야 한다.
- Subsystem : 이 필드는 실행 파일의 대상 하위 시스템, 즉 실행 파일이 사용자 인터페이스에 사용하는 하위 시스템 유형을 식별한다. 가능한 각 하위 시스템 값은 WINNT.H 파일에 정의 되어 있다.
마지막으로 IMAGE_OPTIONAL_HEADER 구조의 끝에서 다음은 소위 IMAGE_DATA_DIRECTORY 구조의 Data Directory 배열 이다. Data Directory 멤버는IMAGE_DATA_DIRECOTRY 구조의 첫번째를 가리킨다.
IMAGE_DATA_DIRECTORY : Data Directory 필드는 파일에서 실행 가능한 정보의 다른 중요한 구성요소를 찾을 위치를 나타낸다. 이 필드의 구조는 선택적 헤더 구조의 맨 아래에 있다. 현재 PE 파일 형식은 16개의 가능한 데이터 구조를 정의하며 그 중 11개가 사용된다.
다음은 일부 데이터 디렉토리 이다.
// Directory Entries
// Export Directory
define IMAGE_DIRECTORY_ENTRY_EXPORT 0
// Import Directory
define IMAGE_DIRECTORY_ENTRY_IMPORT 1
// Resource Directory
define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
// Exception Directory
define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
// Security Directory
define IMAGE_DIRECTORY_ENTRY_SECURITY 4
// Base Relocation Table
define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
// Debug Directory
define IMAGE_DIRECTORY_ENTRY_DEBUG 6
// Description String
define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
// Machine Value (MIPS GP)
define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
// TLS Directory
define IMAGE_DIRECTORY_ENTRY_TLS 9
// Load Configuration Directory
define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
각 데이터 디렉토리 항목은 디렉토리의 크기와 상대 가상주소를 지정한다. 특정 디렉터리를 찾으려면 먼저 선택적 헤더의 데이터 디렉터리 배열에서 상대 가상주소를 결정해야한다. 그 다음 가상 주소를 사용하여 디렉토리가 있는 세션을 결정해야 한다.
디렉토리가 포함된 섹션을 식별하면 해당 섹션의 섹션 헤더를 사용하여 데이터 디렉토리의 정확한 파일 오프셋 위치를 찾는다.
3. 섹션 헤더 테이블
섹션 헤더 테이블은 IMAGE_SECTION_HEADER 구조의 배열이며 실행 파일의 이미지에서 사용할 수 있는 다양한 섹션과 관련된 정보를 포함한다. 이미지의 섹션은 사전순이 아닌 RVA를 기준으로 정렬된다.
섹션 헤더 테이블에는 다음과 같은 중요한 필드가 포함되어 있다.
- SizeOfRawData : 파일에서 섹션의 실제 크기를 지정한다. 이값을 PointerToRawData 값과 합하면 자체의 크기가 된다.
- VirtualSize : 메모리에 있는 섹션의 크기를 나타낸다.
- PointerToRawData : 파일에서 RawData 섹션이 시작되는 오프셋이다. 따라서 위의 값에 이것을 추가하고 파을 정렬 속성이 기본값으로 설정되어 있다면 파일에서 다음 섹션이 시작되는 위치의 오프셋을 얻을 수 있다.
- VirtualAddress : 메모리 섹션의 RVA 이다. VirtualAddress 및 VirtualSize 정보를 사용하며 메모리 정렬 속성이 기본값으로 설정되어 있다면 다음 섹션의 RVA를 얻을 수 있다.
- characteristics : 플래그(R, RW, RWE 등)로 표시된 메모리의 해당 섹션에 대한 메모리 액세스 권한에 대해 알려준다. 이러한 플래그는 섹션이 실행가능, 읽기가능, 쓰기가능 또는 이들의 일부 조합인지 여부를 설명한다.
4. 섹션
PE 파일 섹션 헤더는 또한 Name 이라는 간단한 문자 배열 필드를 사용하여 섹션 이름을 지정한다. 다음은 실행파일에서 사용할 수 있는 다양한 공통 섹션 이름이다.
- .text : 일반적으로 첫 번째 섹션이며 응용 프로그램의 실행 코드를 포함한다. 이 섹션 안에는 애플리케이션의 진입점 ( 실행될 첫 번째 애플리케이션 명령의 주소)가 존재한다. 애플리케이션은 실행코드가 포함된 섹션을 두개이상 가질 수 있다.
- .data : 문자열과 같은 애플리케이션의 초기화된 데이터가 포함된다.
- .rdata 또는 .idata : 이름가져오기 테이블이 있는 섹션에 사용된다.
- .reloc : 재배치 정보를 포함한다.
- .rsrc : 애플리케이션의 UI에서 사용되는 이미지와 같은 항목을 포함하는 리소스 컨테이너 섹션이다.
- .debug : 디버그 정보를 포함한다.
이러한 섹션의 이름은 사용자에 의해 수정이 가능하며 배열의 길이가 8바이트에 불과하기 때문에 PE 섹션 이름은 8자로 제한된다. 최대 길이는 ASCII 문자 8자이며 각 섹션에는 고유한 특성(메모리에 대한 엑세스 권한)이 있다.
'Reverse Engineernig > study' 카테고리의 다른 글
[리버싱 핵심원리] ch.10 함수 호출 규약 (0) | 2023.01.03 |
---|---|
[리버싱 핵심원리] ch08. abex' crackme2 (0) | 2022.12.30 |
[리버싱 핵심원리] ch07. 스택 프레임 (0) | 2022.12.28 |
[리버싱 핵심원리] ch06. abex' crackeme #1 분석 (0) | 2022.12.28 |
[리버싱 핵심원리] ch05. 스택 (0) | 2022.12.27 |