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 구조체가 존재한다.
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
5045000 PE 값을 가집니다.
IMAGE_FILE_HEADER
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를 저장하는데 각각 특성 및 접근권한을 다르게 설정할 필요가 있다.