앙 규문띻
PE file
오늘은 PE file 구조에 대해서 공부해야겠다.
그전에 PE파일에 대해서 잘 정리해놓은 블랙펄 콘치님의 PE둥 PE둥 시리즈들을 먼저 보고오면 좋을듯하다. (짱짱;;)
PE file 이란 ?
- PE 포맷(Portable Executable)은 윈도우 운영 체제에서 사용되는 실행 파일, DLL, object 코드, FON 폰트 파일[1] 등을 위한 파일 형식이다 (위키)
즉 .exe 뿐만아니라 dll, sys같은 파일들도 전부 PE 파일이라고 명칭할 수 있다고 한다.
이미지 - https://slimv.tistory.com/entry/PE-File-Format-0x01
먼저 헤더부터 살펴보자.
PE 파일의 헤더는 3가지로 구분됩니다.
1) DoS Header
2) NT Header
3) Section Header
DoS Header
DoS쓰지도않는데 DoS헤더가 왜있을까!
=> PE file format을 만들 당시 널리 사용되던 DOS파일에 대해 하위호환성을 고려해서 만들었음.
그 결과로 인해 PE 헤더의 제일 앞부분에 기존 DOS EXE Header를 확장시킨 IMAGE_DOS_HEADER 구조체가 존재.
그렇다면 구조체 내용을 보자!
typedef struct _IMAGE_DOS_HEADER {
WORD e_magic; /* 00: MZ Header signature */
WORD e_cblp; /* 02: Bytes on last page of file */
WORD e_cp; /* 04: Pages in file */
WORD e_crlc; /* 06: Relocations */
WORD e_cparhdr; /* 08: Size of header in paragraphs */
WORD e_minalloc; /* 0a: Minimum extra paragraphs needed */
WORD e_maxalloc; /* 0c: Maximum extra paragraphs needed */
WORD e_ss; /* 0e: Initial (relative) SS value */
WORD e_sp; /* 10: Initial SP value */
WORD e_csum; /* 12: Checksum */
WORD e_ip; /* 14: Initial IP value */
WORD e_cs; /* 16: Initial (relative) CS value */
WORD e_lfarlc; /* 18: File address of relocation table */
WORD e_ovno; /* 1a: Overlay number */
WORD e_res[4]; /* 1c: Reserved words */
WORD e_oemid; /* 24: OEM identifier (for e_oeminfo) */
WORD e_oeminfo; /* 26: OEM information; e_oemid specific */
WORD e_res2[10]; /* 28: Reserved words */
DWORD e_lfanew; /* 3c: Offset to extended header */
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
크기는 총 40이다. 여기서 일단 살펴볼껀 2가지를 살펴보자.
- e_magic
- e_lfanew
e_magic : DOS signature 4D5A를 나타낸다. ('MZ')
PE의 스펙에 맞게 '4D5A'의 2바이트로 시작하며 'MZ'가 보입니다. 그 다음으로
e_lfanew : NT Header의 offset을 나타낸다.
빨간네모박스가 e_lfanew 값입니다. 000000D8 입니다. (리틀 엔디언)
실제 000000D8에 'PE'가 보이네요 [NT Header 시작부분]
이 값들은 PE header이기 때문에 변조하면 정상 실행되지 않습니다.
DoS Stub
다음으로 DoS Stub 입니다.
DoS Header와 NT Header 사이가 그렇다면 DoS Stub이 되겠죠 ??
DOS Stub의 존재여부는 옵션이며 크기도 일정하지 않습니다. (없어도 파일실행과 상관없음!)
위그림에서 offset 40~4D(this 전) 까지는 16비트 어셈영역이셈 32비트 운영체제에서 실행안됌. (DOS환경에서 실행)
=> 즉 DoS환경과 window 환경 둘다 호환되는 실행파일을 만들 수 있다?!
끝
NT Header
다음으로 NT Header의 구조체 Image_NT_HEADERS입니다.
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader; /* 0x04 */
IMAGE_OPTIONAL_HEADER32 OptionalHeader; /* 0x18 */
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
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;
typedef struct _IMAGE_OPTIONAL_HEADER {
/* Standard fields */
WORD Magic; /* 0x10b or 0x107 */ /* 0x00 */
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint; /* 0x10 */
DWORD BaseOfCode;
DWORD BaseOfData;
/* NT additional fields */
DWORD ImageBase;
DWORD SectionAlignment; /* 0x20 */
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion; /* 0x30 */
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum; /* 0x40 */
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve; /* 0x50 */
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* 0x60 */
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
복잡;;;
먼저 IMAGE_NT_HEADERS 부터 봅시다...
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader; /* 0x04 */
IMAGE_OPTIONAL_HEADER32 OptionalHeader; /* 0x18 */
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
제일 첫번째 멤버로 Signature(50450000)을 가집니다 'PE'글자를 나타내며 이글자가 보이면 NT header의 시작이라고 볼 수 있습니다.
다음으로 밑에 2개의 구조체가 있네요?
먼저 파일의 대략적인 속성을 나타내는 NT header - File header 입니다.
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;
이 구조체에서는 4가지 멤버가 중요합니다. (이 값이 정확하지 않으면 파일이 정상실행 되지않는다고 함.)
#1. Machine
Machine 넘버는 CPU 별로 고유한 값이며 32비트 Intel x86 호환 칩은 14C의 값을 가짐
=> 쉽게말해 이게 어떤 환경에 돌아가는지 알려줌 ^호^
Machine의 넘버링은 아래를 참조하자!
/* These are the settings of the Machine field. */
#define IMAGE_FILE_MACHINE_UNKNOWN 0
#define IMAGE_FILE_MACHINE_I860 0x014d
#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_THUMB 0x01c2
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4
#define IMAGE_FILE_MACHINE_ARM64 0xaa64
#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
[Machine 넘버 참조]
#2. NumberOfSections
PE 파일은 코드, 데이터, 리소스가 각각의 세션에 나뉘어져서 저장됩니다.
이 멤버는 그 섹션의 개수를 나타낸다고 합니다. 이 값은 반드시 0보다 커야하며 정의된 섹션 개수와 실제 섹션이 다르면 실행 에러가 발생합니다.
위 그림에서 4개의 섹션을 가지고 있다고 합니다. PEview를 통해서 확인해볼까요?
#3. SizeofOptionalHeader
IMAGE_NT_HEADERS 구조체체의 마지막 멤버는 IMAGE_OPTIONAL_HEADER32 입니다.
이 멤버는 바로 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 나타냅니다.
PE32+의 경우에는 IMAGE_OPTIONAL_HEADER64 구조체를 활용합니다. (두개의 구조체 크기는 다름)
=> 이렇게 구조체의 범위를 미리 잡아놓고 그만큼의 데이터를 파일의 정보로 받아들임 ㅋ.
#4. Characteristics
파일의 속성을 나타내는 값으로, 실행 가능한 형태인지 혹은 DLL파일인지의 정보들이 bit OR 형식으로 조합됨
ex) '0102' = '0100' + '0002'
=> PE 파일이 어떤 속성을 가지고 있는지 조합된 숫자로 파악한다!
0002와 2000은 기억하도록 하자(실행가능과 DLL)
#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.
[Characteristic]
'Reversing' 카테고리의 다른 글
PE file(3) (0) | 2019.04.03 |
---|---|
PE File(2) (0) | 2019.04.03 |
리버싱 5 (0) | 2019.04.02 |
리버싱 4 (0) | 2019.03.29 |
리버싱 3 Stackframe (0) | 2019.03.28 |