reversing

PE32+

코끼리_땃쥐 2022. 7. 18. 18:06
반응형

64비트 Windows OS  에서 사용되는 실행 파일 형식인 PE32+

 

64비트 Windows OS에서 프로세스의 가상 메모리 크기는 16TB입니다.

유저 영역의 크기는 하위 8TB이고, 커널 영역의 크기는 상위 8TB입니다.

이처럼 변경된 가상 메모리 크기에 맞게 기존 PE파일 포맷(PE32)이 약간 변경되었습니다.

 

PE32+ (PE+, PE64)

 

64비트 Native 모드에서 실행되는 PE파일 포맷을 PE32+(혹은 PE+, PE64)라고 합니다. PE32+는 하위 호환을 위하여 기존 32비트 PE(PE32) 파일의 확장된 형태를 가집니다. 따라서 기존에 PE파일 포맷에 익숙한 분이라면 쉽게 익힐 수 있습니다. 그럼 변경된 사항을 위주로 설명해보겠습니다.

 

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_OPTTONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

#ifdef _WIN64
typedef IMAGE_NT_HEADERS64    IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS64   PIMAGE_NT_HEADERS;
#else
typedef IMAGE_NT_HEADERS32    IMAGE_NT_HEADERS;
typedef PIMAGe_NT_HEADERS32   PIMAGE_NT_HEADERS;
#endif

 

PE32+ 에서는 IMAGE_NT_HEADERS64 구조체를 사용합니다.

PE32에서는 IMAGE_NT_HEADERS32구조체를 사용하였습니다.

둘의 차이점은 세 번째 멤버가 ㄱ각각 IMAGE_OPTIONAL_HEADER64와 IMAGE_OPTIONAL_HEADER32라는 것입니다. 아래쪽의 #ifdef _WIN64 전처리문에 의해서 64비트/32비트 구조체가 알맞게 IMAGE_NT_HEADERS/PIMAGE_NT_HEADERS 이름으로 재정의됩니다.

 

 

IMGAE_FILE_HEADER

 

또 다른 변경 사항은 IMAGE_FILE_HEADER 구조체의 Machine 멤버의 값이 변경됩니다. PE32에서 이 Machine의 값은 014C로 고정되었습니다.

x64용 PE32+파일의 Machine 넘버는 8664입니다(IA-64용 PE32+ 파일의 경우 0200). 아래 목록은 Winnt.h 파일에 정의된 각종 CPU타입에 대한 Machine 넘버입니다.

출처 : https://docs.microsoft.com/en-us/windows/win32/debug/pe-format

 

 

IMAGE_OPTIONAL_HEADER

PE32+에서 기존과 가장 많이 변경된 부분이 바로 IMAGE_OPTIONAL_HEADER구조체 입니다.

typedef struct _IMAGE_OPTIONAL_HEADER32 {
	WORD  Magic;
    BYTE  MajorLinkerVersion;
    BYTE  MinorLinkerVersion;
    DWORD SizeOfCode;
    DWORD SizeOfInitializedDate;
    DWORD SizeOfUninitializedData;
    DWORD AddressOfEntryPoint;
    DWORD BaseOfCode;
    DWORD BaseOfData;
    DWORD ImageBase;
    DWORD SectionAlignment;
    DWORD FileAlignment;
    WORD  MajorOperatingSystemVersion;
    WORD  MinorOperatingSystemVersion;
    WROD  MajorImageVersion;
    WROD  MinorImageVersion;
    WORD  MajorSubsystemVersion;
    WORd  MinorSubsystemVersion;
    DWORD Win32VersionValue;
    DWORD SizeOfImage;
    DWORD SizeOfHeaders;
    DOWRD CheckSum;
    WORD  Subsystem;
    WORD  DllCharacteristics;
    DWORD SizeOfStackReserve;
    DWORD SizeOfStackCommit;
    DOWRD SizeofHeapReserve;
    DWORD SizeOfHeapCommit;
    DWORD LoaderFlags;
    DWORD NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

typedef struct _IMAGE_OPTIONAL_HEADER64 {
	WORD    Magic;
    BYTE      MajorLinkerVersion;
    BYTE      MinorLinkerVersion;
    DOWRD     SizeOfCode;
    DWORD     SizeOfInitializedData;
    DWORD     SizeOfUninitializedData;
    DWORD     AddressOfEntryPoint;
    DWORD     BaseOfCode;
    ULONGLONG ImageBase;
    DWORD     SectionaAlignment;
    DWORD     FileAlignment;
    WORD      MajorOperatingSystemVersion;
    WORD      MinorOperatingSystemVersion;
    WORD      MajorImageVersion;
    WORD      MinorImageVersion;
    WORD      MajorSubsystemVersion;
    WORd      MinorSubsystemVersion;
    DOWRD     Win32VersionValue;
    DWORD     SizeOfImage;
    DWORD     SizeOfHeaders;
    DWORD     CheckSum;
    WORD      Subsystem;
    WORD      DllCharacteristics;
    ULONGLONG SizeOfStackReservce;
    ULONGLONG SizeOfStackCommit;
    ULONGLONG SizeOfHeapReserve;
    ULONGLONG SizeOfHeapCommit;
    DWORD     LoaderFlags;
    DOWRD     NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONALHEADER64;

#define IMAGE_NT_OPTIONAL_HDR32_MAGIC  0X10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC  0X20b

 

 Magic 

먼저 Magic  넘버가 변경됩니다. PE32에서 Magic 넘버는 010B 값이고 PE32+에서 Magic 넘버는 020B 입니다.

Windows의 PE 로더는 이 값을 확인하여 IMAGE_OPTIONAL_HEADER 구조체가 32비트용 인지 64비트용인지 구별합니다.

 

BaseOfData

PE 파일의 데이터 섹션의 시작 주소(RVA)를 의미하던 BaseOfData 항목이 제거 되었습니다.

 

ImageBase

ImageBase 멤버의 자료형 (Data Type)이 ULONGLONG(8바이트) 으로 변경되었습니다.

이는 크기가 확장된 프로세스의 가상 메모리에 대응하기 위함입니다.

이로써 PE32+ 파일은 64비트 프로세스의 16TB에 달하는 가상 메모리 공간의 어느 곳에라도 로딩될 수 있습니다(exe/dll  파일은 하위 8TB의 유저 영역에, SYS파일은 상위 8TB 커널 영역에 로딩됨).

 

스택 &  힙

마지막으로 스택과 힙을 위한 데이터(SizeOfStackReserve, SizeOfStackCommit, SizeOfHeapReserve, SizeOfHeapCommit)의 자료형의 크기가 ULONGLONG(8바이트)로 변경되었습니다.

이것도 역시 확장된 프로세스 가상 메모리에 대응하기 위한 것입니다.

 

IMAGE_THUNK_DATA

IMAGE_THUNK_DATA 구조체의 크기가 기존 4바이트에서 8바이트 크기로 변경되었습니다.

typedef struct _IMAGE_THUNK_DATA64 {
	union {
    	ULONGLONG ForwarderString; // PBYTE
        ULONGLONG Function;        // PDOWRD
        ULONGLONG Ordinal;
        ULONGLONG AddressOfdata;   // PIMAGE_IMPORT_BY_NAME
    } ul;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;

typedef struct _IMAGE_THUNK_DATA32 {
	union {
    	DWORD ForwarderString;	// PBYTE
        DWORD Function;			// PDWORD
        DWORD Ordinal;
        DWORD AddressOfData;	// PIMAGE_IMPORT_BY_NAME
    } ul;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

#ifdef _WIN64
typedef IMAGE_THUNK_DATA64		IMAGE_THUNK_DATA;
typedef PIMAGE_THUNK_DATA64		PIMAGE_THUNK_DATA;
#else
typedef IMAGE_THUNK_DATA32		IMAGE_THUNK_DATA;
typedef PIMAGE_THUNK_DATA32		PIMAGE_THUNK_DATA;
#endif

 

IMAGE_IMPORT_DESCRIPTOR 구조체의 OriginalFirstThunk(INT)와 FirstThunk(IAT) 멤버의 값이 바로 IMAGE_THUNK_DATA 구조체 리스트의 RVA입니다.

 

 

IMAGE_IMPORT_DESCRIPTOR 구조체

typedef struct _IMAGE_IMPORT_DESCRIPTOR{
	union {
    	DWORD	Characteristics;
        DWORD	OriginalFirstThunk;		// - INT : RVA of IMAGE_THUNK_DATA
    } DUMMYUNIONNAME;
    DWORD	TimeDateStamp;
    DWORD	ForwarderChain;
    DWORD	Name;
    DWORD	FirstThunk;					// - IAT : RAV of IMAGE_THUNK_DATA
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

즉 기존 PE32 파일의 INT, IAT 값을 따라가면, IMAGE_THUNK_DATA32 구조체(크기 : 4바이트) 배열이 나타났지만, PE32+ 파일의 경우에는 IMAGE_THUNK_DATA64 구조체(크기 : 8바이트) 배열이 나타납니ㅏㄷ. 따라서 IAT를 따라갈 때 배열 원소의 크기에 주의해야 합니ㅏㄷ.

 

 

IMAGE_TLS_DIRECTORY

IMAGE_TLS_DIRECTORY 구조체의 멤버 중 일부는 VA(Virtual Address)를 의미 하는데, 이들도 8바이트의 크기로 확장되었습니다.

tpyedef struct _IMAGE_TLS_DIRECTORY64 {
	ULONGLONG	StartAddressOfRawData;
    ULONGLONG	EndAddressOfRawData;
    ULONGLONG	AddressOfIndex			// PWORD
    ULONGLONG	AddressOfCallBacks;		// PIMAGE_TLS_CALLBACK *
    DWORD	SizeOfZeroFill;
    DWORD	Characteristics;
} IMAGE_TLS_DIRECTORY64;
typedef IMAGE_TLS_DIRECTORY64 * PIMAGE_TLS_DIRECTORY64;

typedef struct _IMAGE_TLS_DIRECTORY32 {
	DWORD	StartAddressOfRawData;
    DWORD	EndAddressOfRawData;
    DWORD	AddressOfIndex;				// PWORD
	DWORD	AddressOfCallBacks;			// PIMAGE_TLS_CALLBACK *
    DWORD	SizeOfZeroFill;
    DWORD	Characteristics;
} IMAGE_TLS_DIRECTORY32;
typedef IMAGE_TLS_DIRECTORY32 * PIMAGE_TLS_DIRECTORY32;

#ifdef _WIN64
typedef IMAGE_TLS_DIRECTORY64		IMAGE_TLS_DIRECTORY;
typedef PIMAGE_TLS_DIRECTORY64		PIMAGE_TLS_DIRECTORY;
#else
typedef IMAGE_TLS_DIRECTORY32		IMAGE_TLS_DIRECTORY;
typedef PIMAGE_TLS_DIRECTORY32		PIMAGE_TLS_DIRECTORY;
#endif

IMAGE_TLS_DIRECTORY 구조체의 StartAddressOfRawData, EndAddressOfRawData, AddressOfIndex, AddressOfCallBacks 멤버는 모두 VA(virtual Address) 값을 가집니다. 따라서 64비트 OS의 주소 크기인 8바이트로 확장되었습니다.

반응형