[리버싱 핵심원리 study] 38장 PE32+
64bit Windows OS에서 사용되는 실행 파일 형식인 PE32+에 대해 알아본다. 예가 필요한 경우 notepad.exe의 32bit 버전과 64bit 버전의 비교를 해보자.
1. PE32+(PE+, PE64)
(1) IMAGE_NT_HEADERS
- 32bit
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
- 64bit
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
위와 같이 마지막 멤버인 IMAGE_OPTIONAL_HEADER 부분이 32bit 혹은 64bit에 의해 IMAGE_NT_HEADERS32 / IMAGE_NT_HEADERS64로 나뉜다.
(2) IMAGE_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;
PE32에서는 Machine 멤버 값이 014C로 고정되었는데, PE32+에서는 8664(AMD64)로 변경되었다.
(3) IMAGE_OPTIONAL_HEADER
- 32bit
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;
- 64bit
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG 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;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
PE32+에서 기존과 가장 많이 달라진 부분이다. 달라진 부분은 다음과 같다.
- Magic
기존 PE32에서의 Magic 넘버는 010B이지만 PE32+에서는 020B다.
- ImageBase
ImageBase의 자료형이 ULONGLONG으로 바꼈다. 이는 64bit 환경에서 가상 메모리 크기가 확장되면서 프로세스가 올라갈 수 있는 주소의 범위도 확장되었기 때문이다.
- Stack & Heap
스택과 힙을 위한 데이터의 자료형의 크기 또한 마찬가지 이유로 ULONGLONG으로 변경되었다.
달라진 위 세 부분을 CFF Explorer를 통해 찾아보면 아래와 같다.
(4) IMAGE_THUNK_DATA
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
IMAGE_IMPORT_DESCRIPTOR 구조체의 INT, IAT에 해당하는 OriginalFirstThunk, FirstThunk는 IMAGE_THUNK_DATA 구조체 리스트의 RVA이다. 이때 기존 PE32는 IMAGE_THUNK_DATA가 4바이트 배열을 이루었지만, PE32+에서는 8바이트 배열이 나타난다.
(5) IMAGE_TLS_DIRECTORY
- 32bit
typedef struct _IMAGE_TLS_DIRECTORY32
{
DWORD StartAddressOfRawData;
DWORD EndAddressOfRawData;
DWORD AddressOfIndex;
DWORD AddressOfCallBacks;
DWORD SizeOfZeroFill;
DWORD Characteristics;
} IMAGE_TLS_DIRECTORY32;
typedef IMAGE_TLS_DIRECTORY32* PIMAGE_TLS_DIRECTORY32;
-64bit
typedef struct _IMAGE_TLS_DIRECTORY64
{
ULONGLONG StartAddressOfRawData;
ULONGLONG EndAddressOfRawData;
ULONGLONG AddressOfIndex;
ULONGLONG AddressOfCallBacks;
DWORD SizeOfZeroFill;
DWORD Characteristics;
} IMAGE_TLS_DIRECTORY64;
typedef IMAGE_TLS_DIRECTORY64* PIMAGE_TLS_DIRECTORY64;
위와 같이 몇 멤버가 DWORD(4바이트)에서 ULONGLONG(8바이트)로 변경된 것을 확인할 수 있다.