PE(Portable Executable) File Format (3) - PE Header
NT header
NT header 구조체 IMAGE_NT_HEADERS 입니다.
DWORD Signature; // PE Signature : 50450000 ("PE"00)
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
* 출처 : Microsoft 의 Visual C++ 에서 제공하는 winnt.h
위 구조체는 32 bit 용이며, 64 bit 용은 세번째 멤버가 IMAGE_OPTIONAL_HEADER64 입니다.
IMAGE_NT_HEADER 구조체는 3개의 멤버로 되어 있는데요,
제일 첫 멤버는 Signature 로서 50450000h ("PE"00) 값을 가집니다. (변경불가!)
그리고 FileHeader 와 OptionalHeader 구조체 멤버가 있습니다.
notepad.exe 의 IMAGE_NT_HEADERS 의 내용을 hex editor 로 살펴보겠습니다.
IMAGE_NT_HEADERS 구조체의 크기는 F8h 입니다. 상당히 큰 구조체 입니다.
FileHeader 와 OptionalHeader 구조체를 하나하나 살펴보겠습니다.
IMAGE_NT_HEADERS - IMAGE_FILE_HEADER
파일의 개략적인 속성을 나타내는 IMAGE_FILE_HEADER 구조체 입니다.
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
* 출처 : Microsoft 의 Visual C++ 에서 제공하는 winnt.h
IMAGE_FILE_HEADER 구조체에서 아래 4 가지 멤버들이 중요합니다.
(이 값들이 정확히 세팅되어 있지 않으면 파일은 정상적으로 실행되지 않습니다.)
#1. Machine
Machine 넘버는 CPU 별로 고유한 값이며 32 bit Intel 호환 칩은 14Ch 의 값을 가집니다.
아래는 winnt.h 파일에 정의된 Machine 넘버의 값들입니다. (일반적인 14Ch 의 값을 기억하면 됩니다.)
#define IMAGE_FILE_MACHINE_UNKNOWN 0
#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386.
#define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian
#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB 0x01c2
#define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
#2. NumberOfSections
PE 파일은 코드, 데이타, 리소스 등이 각각의 섹션에 나뉘어서 저장된다고 설명드렸습니다.
NumberOfSections 는 바로 그 섹션의 갯수를 나타냅니다.
이 값은 반드시 0 보다 커야 합니다.
정의된 섹션 갯수보다 실제 섹션이 적다면 실행 에러가 발생하며,
정의된 섹션 갯수보다 실제 섹션이 많다면 정의된 갯수만큼만 인식됩니다.
#3. SizeOfOptionalHeader
IMAGE_NT_HEADERS 구조체의 마지막 멤버는 IMAGE_OPTIONAL_HEADER32 구조체입니다.
SizeOfOptionalHeader 멤버는 바로 이 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 나타냅니다.
IMAGE_OPTIONAL_HEADER32 는 C 언어의 구조체이기 때문에 이미 그 크기가 결정되어 있습니다.
그런데 Windows 의 PE Loader 는 IMAGE_FILE_HEADER 의 SizeOfOptionalHeader 값을 보고
IMAGE_OPTIONAL_HEADER32 구조체의 크기를 인식합니다.
IMAGE_DOS_HEADER 의 e_lfanew 멤버와 IMAGE_FILE_HEADER 의 SizeOfOptionalHeader 멤버 때문에
일반적인(상식적인) PE 파일 형식을 벗어나는 일명 '꽈배기' PE 파일(PE Patch) 이 만들 수 있습니다.
(나중에 PE Patch 에 대해서 상세히 설명하도록 하겠습니다.)
#4. Characteristics
파일의 속성을 나타내는 값으로써, 실행이 가능한 형태인지(executable or not)
혹은 DLL 파일인지 등의 정보들이 bit OR 형식으로 조합됩니다.
아래는 winnt.h 파일에 정의된 Characteristics 값들입니다. (0002h 와 2000h 의 값을 기억해 두세요.)
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable
// (i.e. no unresolved externel references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 // Agressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from
// file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // If Image is on removable media,
// copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 // If Image is on Net,
// copy and run from the swap file.
#define IMAGE_FILE_SYSTEM 0x1000 // System File.
#define IMAGE_FILE_DLL 0x2000 // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.
참고로 PE 파일중에 Characteristics 값에 0002h 가 없는 경우(not executable)가 있을까요?
네, 있습니다. 예를 들어 *.obj 와 같은 object 파일이 그런 경우이고, resource DLL 같은 파일이 그런 경우 입니다.
이 정도면 IMAGE_FILE_HEADER 의 구조를 이해하는데 부족함이 없을 것입니다.
마지막으로 IMAGE_FILE_HAEDER 의 TimeDateStamp 멤버에 대해서 설명드리겠습니다.
이 값은 파일의 실행에 영향을 미치지 않는 값으로써 해당 파일의 빌드 시간을 나타낸 값입니다.
단, 개발 도구에 따라서 이 값을 세팅해주는 도구(VB, VC++)가 있고, 그렇지 않은 도구(Delphi)가 있습니다.
(또한 개발 도구의 옵션에 따라서 달라질 수 있습니다.)
이제 실제로 notepad.exe 의 IMAGE_FILE_HEADER 를 확인해 보겠습니다.
위 그림은 hex editor 로 봤을때의 그림이고, 이를 알아보기 쉽게 구조체 멤버로 표현하면 아래와 같습니다.
offset value description
-------------------------------------------------------------------------------
000000E4 014C machine
000000E6 0003 number of sections
000000E8 48025287 time date stamp (Mon Apr 14 03:35:51 2008)
000000EC 00000000 offset to symbol table
000000F0 00000000 number of symbols
000000F4 00E0 size of optional header
000000F6 010F characteristics
IMAGE_FILE_RELOCS_STRIPPED
IMAGE_FILE_EXECUTABLE_IMAGE
IMAGE_FILE_LINE_NUMS_STRIPPED
IMAGE_FILE_LOCAL_SYMS_STRIPPED
IMAGE_FILE_32BIT_MACHINE
다음에는 NT header 의 세번째 멤버인 IMAGE_OPTIONAL_HEADER32 에 대해서 알아보겠습니다.
(continue)