Abstract
PE 포맷의 DOS 헤더의 e_lfanew 멤버가 File Header의 위치를 가리킨다. File Header에는 PE 파일이 실행될 수 있는 머신(32/64 비트) 종류를 포함해 OptionalHeader의 크기, 섹션 개수, PE 파일 속성 등을 포함한다.
File Header
IMAGE_FILE_HEADER structure
PE 헤더에서 File 헤더는 파일의 개략적인 속성을 나타낸다. PE32 파일인지 PE32+(64비트 프로그램)인지도 File 헤더를 통해 알 수 있다. File 헤더는 DOS 헤더가 e_lfanew 멤버를 통해 가리키던 NT 헤더의 멤버로 포함되어 있다. winnt.h를 통해 IMAGE_NT_HEADERS 구조체를 살펴보면 아래와 같다.
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
32비트 프로그램으로 빌드할지 64비트 프로그램으로 컴파일할지에 따라 IMAGE_NT_HEADERS가 달라지지만 IMAGE_FILE_HEADER는 두 프로그램 모두 동일하다. IMAGE_FILE_HEADER는 아래와 같다.
//
// File header format.
//
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
주요 멤버는 Machine, NumberOfSections, SizeOfOptionalHeader, Characteristics다.
Machine
실행파일이 실행될 수 있는 CPU 종류를 나타내는 값이다. Intel x86 계열(32비트 프로그램)은 14Ch라는 값을 가지고 AMD64(64비트 프로그램)은 8664h 값을 가진다. 이를 통해 실행 파일이 32비트 프로그램인지 64비트 프로그램인지 구별할 수 있다.
현재 winnt.h에 정의된 Machine 값들은 아래와 같다.
#define IMAGE_FILE_MACHINE_UNKNOWN 0
#define IMAGE_FILE_MACHINE_TARGET_HOST 0x0001 // Useful for indicating we want to interact with the host and not a WoW guest.
#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_SH3 0x01a2 // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
#define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5 0x01a8 // SH5
#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB 0x01c2 // ARM Thumb/Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_AM33 0x01d3
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE 0x0520 // Infineon
#define IMAGE_FILE_MACHINE_CEF 0x0CEF
#define IMAGE_FILE_MACHINE_EBC 0x0EBC // EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R 0x9041 // M32R little-endian
#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian
#define IMAGE_FILE_MACHINE_CEE 0xC0EE
NumberOfSections
PE 파일이 가지는 섹션의 개수다. PE 파일을 패치할 때 섹션을 삭제하거나 늘리는 경우 섹션 헤더를 추가하고, 섹션 데이터를 삽입하는 것 외에도 File 헤더의 NumberOfSections 값을 조정해야 한다. 그렇지 않으면 정상적으로 실행되지 않는다. sample32.exe의 SizeOfSections는 4이며, 섹션 헤더를 보면 4개의 섹션이 있는 걸 볼 수 있다.
SizeOfOptionalHeader
앞서 Machine 멤버 값을 보고 32/64비트를 구분할 수 있다고 했다. 하지만 SizeOfOptionalHeader 멤버 값으로도 구분이 가능하다. 14Ch나 8664h는 흔하기 때문에 바로 구별할 수 있지만 Machine은 CPU를 구별하는 값이기 때문에 두 값 외에 다른 값도 들어갈 수 있다. 일반적인 경우 32/64비트 sizeof(IMAGE_OPTIONAL_HEADERS)는 아래와 같은 값을 가진다.
- 32비트: sizeof(IMAGE_OPTIONAL_HEADER32) == 224
- 64비트: sizeof(IMAGE_OPTIONAL_HEADER64) == 240
PE 헤더를 심하게 꼬아버리는 패커들은 SizeOfOptionalHeader 값도 바꿔치기 때문에 32비트와 64비트를 구별하는 가장 좋은 방법은 IMAGE_OPTIONAL_HEADER의 매직넘버 값을 보는 것이다.
Characteristics
PE 파일의 속성을 나타낸다. 실행 가능한 EXE 파일인지, 단독으론 실행이 불가능한 DLL 파일인지 등의 정보가 담긴다. 속성 값들은 OR 연산을 통해 하나 이상의 정보가 담긴다. EXE 파일은 2가, DLL 파일은 2000h 비트가 1로 설정된다.
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved external 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 // Aggressively 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.
32비트 프로그램의 Characteristics 값을 CFF Explorer로 살펴보면 EXE 파일이며, Relocation이 이루어지지 않고, 32비트 머신에서 동작하는 프로그램이란 걸 알려주고 있다. 빌드 시 ASLR 옵션을 끄고 빌드하면 IMAGE_FILE_RELOCS_STRIPPED 비트가 1로 설정되고 .reloc 섹션이 생성되지 않는다.
'Malware Analysis > PE' 카테고리의 다른 글
#6 PE - .reloc 섹션 (0) | 2021.08.01 |
---|---|
#5 PE - IAT (0) | 2021.08.01 |
#4 PE - Section Header (0) | 2021.08.01 |
#3 PE - Optional Header (1) | 2021.08.01 |
#1 PE - DOS Header (0) | 2021.07.31 |