PE File Format

PE File Format

Description
Potable Executable File Format
category
Reversing
Tag
Date
Apr 4, 2024 04:34 AM

PE란?

Portable Executable의 약자로 Windows 운영체제에서 사용되는 실행 파일 형식을 말한다.
PE파일의 종류는 9개로 아래표와 같다.
종류
주요 확장자
실행 계열
EXE, SCR
라이브러리 계열
DLL, OCX, CPL, DRV
드라이버 계열
SYS, VXD
오브젝트 파일 계열
OBJ
 

PE구조

DOS header부터 Section header까지를 PE헤더라고 한다.
그 밑 Section들의 모음을 PE바디라고 한다.
파일에서는 offset, 메모리에서는 VA(Virtual Address)로 위치를 표현한다. 파일이 메모리에 로딩되면 모양이 달라진다.

VA & RVA

VA(Virtual Address)는 프로세스 가상 메모리의 절대 주소를 말한다.
RVA(Relative Virtual Address)는 기준 위치(ImageBase)에서부터의 상대 주소를 말한다.
RVA와 VA의 관계식
RVA + ImageBase = VA PE 헤더에는 대부분 RVA 형태로 저장되어있는 경우가 많다.
로딩 시 다른 PE파일이 로딩되어 있을 수 있기 때문이다.
 

PE 헤더

PE 헤더에는 파일이 실행되기 위해 필요한 모든 정보가 적혀있다.
예를 들어 어떤 메모리에 적재되고, 어디서 실행되어야 하고, 필요한 DLL파일 등등이 적혀있다.
Section header에는 각 Section에 대한 크기, 위치, 속성 등이 담겨있다.
 

DOS Header

Microsoft는 PE File Format을 만들 당시에 DOS에 대한 호환성을 고려해 만들었기 때문에 PE헤더 제일 앞부분에 DOS EXE Header를 확장시킨 IMAGE_DOS_HEADER 구조체가 존재한다.
notion image
IMAGE_DOS_HEADER의 크기는 40이고, 중요 멤버는 e_magic, e_lfanew이다.
  • e_magic : DOS signature
  • e_lfanew : NT header의 옵셋을 표시
 

DOS Stub

DOS Stub의 존재 여부는 옵션이며, 존재하지 않아도 프로그램 실행에 영향을 주지 않는다.
DOS Stub은 코드와 데이터의 혼합으로 이루어져 있다.
 

NT Header

NT header의 구조체는 IMAGE_NT_HEADERS이다.
IMAGE_NT_HEADERS의 구조체는 3개로 이루어져 있다.
NT header의 크기는 F8이다.

Signature

notion image
5045000 PE 값을 가집니다.
 

IMAGE_FILE_HEADER

notion image
File Header는 7개의 구조체로 이루어져있다. 특히 Machine, Number of Sections, Size of Optional Sections, Characteristics의 값이 정확히 설정되어 있지 않으면 정상적으로 실행되지 않는다.
  • Machine : CPU별 고유값
  • Number of Sections : PE파일의 섹션의 갯수 반드시 0보다 커야된다.
  • Time Date Stamp : 파일의 빌드 시간
  • Pointer to Symbol Table : COFF Symbol Table이 존재할 때 Symbol Table 주소
  • Number of Symbols : Pointer to Symbol Table값이 설정되어 있을 때 해당 Table 안의 Symbol의 개수
  • Size of Optional Header : IMAGE_OPTIONAL_HEADER의 크기를 지정한다.
  • Characteristics : 파일의 속성을 나타내는 값으로 실행이 가능한지 DLL 파일인지 등의 정보들이 Bit OR 형식으로 조합된다.
Machine의 값과 Characteristics의 값은 winnt.h에 정의되어 확인해 볼 수 있다.
winnt.h
//Machine 넘버의 값 #define IMAGE_FILE_MACHINE_UNKNOWN 0 #define IMAGE_FILE_MACHINE_I386 0x014c #define IMAGE_FILE_MACHINE_R3000 0x0162 #define IMAGE_FILE_MACHINE_R4000 0x0166 #define IMAGE_FILE_MACHINE_R10000 0x0168 #define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 #define IMAGE_FILE_MACHINE_ALPHA 0x0184 #define IMAGE_FILE_MACHINE_SH3 0x01a2 #define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 #define IMAGE_FILE_MACHINE_SH3E 0x01a4 #define IMAGE_FILE_MACHINE_SH4 0x01a6 #define IMAGE_FILE_MACHINE_SH5 0x01a8 #define IMAGE_FILE_MACHINE_ARM 0x01c0 #define IMAGE_FILE_MACHINE_ARMV7 0x01c4 #define IMAGE_FILE_MACHINE_ARMNT 0x01c4 #define IMAGE_FILE_MACHINE_THUMB 0x01c2 #define IMAGE_FILE_MACHINE_AM33 0x01d3 #define IMAGE_FILE_MACHINE_POWERPC 0x01F0 #define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 #define IMAGE_FILE_MACHINE_IA64 0x0200 #define IMAGE_FILE_MACHINE_MIPS16 0x0266 #define IMAGE_FILE_MACHINE_ALPHA64 0x0284 #define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 #define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 #define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64 #define IMAGE_FILE_MACHINE_TRICORE 0x0520 #define IMAGE_FILE_MACHINE_CEF 0x0CEF #define IMAGE_FILE_MACHINE_EBC 0x0EBC #define IMAGE_FILE_MACHINE_AMD64 0x8664 #define IMAGE_FILE_MACHINE_M32R 0x9041 #define IMAGE_FILE_MACHINE_CEE 0xc0ee
//Characteristics 값 #define IMAGE_FILE_RELOCS_STRIPPED 0x0001 #define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 #define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 #define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 #define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 #define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 #define IMAGE_FILE_32BIT_MACHINE 0x0100 #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 #define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 #define IMAGE_FILE_SYSTEM 0x1000 #define IMAGE_FILE_DLL 0x2000 #define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 #define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
 

IMAGE_OPTIONAL_HEADER

PE헤더 구조체 중 가장 크기가 크다.
IMAGE_OPTIONAL_HEADER의 구조체는 아래와 같다.
typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32,*PIMAGE_OPTIONAL_HEADER32;
IMAGE_OPTIONAL_HEADER의 구조체 중 가장 중요한 것은 아래와 같다.
  • Magic : IMAGE_OPTIONAL_HEADER32 구조체인 경우 10B의 값을 가지고 IMAGE_OPTIONAL_HEADER64 구조체인 경우는 20B의 값을 가진다.
  • Address of Entry Point : EntryPoint의 RVA(Relative Virtual Address) 값을 가지고 있다. 해당 값은 프로그램 실행 시 최초로 실행되는 코드의 시작 주소이다.
  • ImageBase : PE파일이 로딩되는 시작주소를 말한다. EXE, DLL 파일은 user memory 영역인 0~7FFFFFFF 범위에 로딩된다. SYS 파일의 경우 kernel memory 영역인 80000000~FFFFFFFF 범위에 로딩된다.
  • SectionAlignment, FileAlignment : 파일에서 섹션의 최소단위를 나타내는 것이 FileAlignment이고, 메모리에서 섹션의 최소단위를 나타내는 것이 SectionAlignment이다. 파일, 메모리의 섹션 크기는 항상 SectionAlignment, FileAlignment 값의 배수가 되어야 한다.
  • Size of Image : PE파일이 메모리에 로딩되었을 때 가상 메모리에서 PE Image가 차지하는 크기를 나타냅니다. 파일의 크기와 메모리에 로딩된 크기는 다르다.
  • Size of Header : PE헤더의 전체 크기를 말한다. Size of Header의 값은 FileAlignment 값의 배수여야 한다.
  • Subsystem : 값은 Driver file, GUI, CUI로 지정되며 값을 통해 sys, exe, dll 인지 확인할 수 있습니다.
  • Number of RVA and Size : DataDirectory 배열의 개수를 나타낸다.
  • DataDirectory : 특정 구조체들의 배열이라고 보면된다.
 

Section Header

각 Section의 속성을 정의해 놓은 것이 Section Header이다.
Section은 대체적으로 code, data, resource를 저장하는데 각각 특성 및 접근권한을 다르게 설정할 필요가 있다.