COFF Optional Header의 확장으로 윈도우의 링커나 로더가 요구하는 부가적인 정보를 담고있다.
typedef struct _IMAGE_OPTIONAL_HEADER64 {
// Standard fields.
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
// NT additional fields.
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;
64비트 IMAGE_OPTIONAL_HEADER64 의 크기는 240bytes로
112bytes를 차지하는 29개의 기본필드, 128bytes의 IMAGE_DATA_DIRECTORY 구조체 배열로 구성되어있다.
32비트에서는 IMAGE_OPTIONAL_HEADER32 라는 이름으로 정의되고, 차이점은 아래와 같다.
typedef struct _IMAGE_OPTIONAL_HEADER64 {
// Standard fields.
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData; // 64비트에서 존재하지 않는 필드로 32비트에는 존재함
// NT additional fields.
DWORD ImageBase; // 64비트에서는 ULONGLONG 이지만 32비트에서는 DWORD
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; // 64비트에서는 ULONGLONG 이지만 32비트에서는 DWORD
DWORD SizeOfStackCommit; // 64비트에서는 ULONGLONG 이지만 32비트에서는 DWORD
DWORD SizeOfHeapReserve; // 64비트에서는 ULONGLONG 이지만 32비트에서는 DWORD
DWORD SizeOfHeapCommit; // 64비트에서는 ULONGLONG 이지만 32비트에서는 DWORD
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
각 필드에 대한 설명은 아래와 같다.
Standard Fields
모든 COFF 포맷에서 정의되는 표준필드로 실행 파일의 로드와 실행에 사용되는 일반적인 정보를 포함하고 있다.
Magic
IMAGE_OPTIONAL_HEADER를 나타내는 시그니쳐로 32비트는 0x010B, 64비트는 0x020B, ROM Image는 0x0107을 가진다.
PE파일이 32비트인지 64비트인지 만을 판별하려면 IMAGE_NT_HEADER의 Machine 필드보다
IMAGE_OPTIONAL_HEADER의 Magic 필드를 사용하는것이 좋다.
MajorLinkerVersion , MinorLinkerVersion
해당 파일을 만들어낸 링커의 버전을 나타낸다.
printf("%d.%d", MajorLinkerVersion, MinorLinkerVersion); // => 11.2
SizeOfCode
여러 코드 섹션들중 IMAGE_SCN_CNT_CODE 속성을 가지는 모든 섹션의 전체 크기를 나타낸다.
이때 섹션의 크기는 IMAGE_SECTION_HEADER 구조체의 VirtualSize 필드값을
FileAlignment 필드값을 라운드 업 처리한값을 의미한다.
SizeOfInitializedData
.data, .rdata, .data, .rsrc 등과 같은 초기화된 데이터 섹션의 전체 크기를 나타낸다.
IMAGE_SCN_CNT_INITIALIZED_DATA 속성을 가지며, 각각의 섹션 크기는 IMAGE_SECTION_HEADER 구조체의
VirtualSize 필드값을 FileAlignment 필드값으로 라운드 업 처리한 값을 의미한다.
SizeOfUninitializedData
.bss, .textbss 등과 같은 초기화 되지않은 데이터 섹션의 전체 크기를 나타낸다.
IMAGE_SCN_CNT_UNINITIALIZED_DATA 속성을 가지며, 링커는 일반적으로 초기화 되지않은 데이터를
일반 데이터 섹션에 병합시키기때문에, 보통 0을 가지게된다.
AddressOfEntryPoint
PE가 로드된 후 실행을 시작할 주소에 대한 RVA를 가지고있는 중요한 필드로 코드섹션 내에서 특정번지가 된다.
EXE PE 의 경우 해당 PE가 로드된 후 이 프로세스의 메인 쓰레드가 실행하는 최초의 코드이다.
일반적으로 이 번지가 가리키는 값은 다음 런타임 시작 루틴의 RVA가 설정된다.
BaseOfCode
첫번째 코드 섹션이 시작되는 RVA를 의미한다.
BaseOfData
64비트 PE에는 존재하지 않는 필드로, PE가 메모리에 로드될때 데이터 섹션의 첫 번째 바이트의 RVA 이다.
NT Additional Fields
COFF Optional Header의 확장으로 윈도우의 링커나 로더가 요구하는 부가적인 정보를 담고있다.
ImageBase
로더가 PE파일을 메모리에 로드할때, 가상 주소공간에 매핑시키고자하는
메모리상의 시작 주소를 의미하는것으로 매우 중요한 필드이며 모든 RVA값에 대한 기준 주소가 된다.
SectionAlignment
PE 파일이 메모리에 매핑될때, 각 섹션의 시작주소가 언제나 SectionAlignment 필드값의 배수가 되도록 보장한다.
또한 이 필드의 값는 FileAlignment 필드의 값보다 크거나 같아야한다.
FileAlignment
PE 파일 내에서 섹션의 정렬 단위를 나타내는것으로, 각 섹션을 구성하는 바이너리 데이터들은
FileAlignment 필드 값의 배수로 시작하도록 보장되며 512 ~ 65,536 사이의, 2의 거듭제곱이되는 값으로 설정되야한다.
만약 SectionAlignment 필드의 값이 해당 플랫폼의 메모리 페이지 크기보다 작을 경우
FileAlignment 필드의 값은 SectionAlignment 필드 값으로 설정된다.
MajorOperatingSystemVersion , MinorOperatingSystemVersion
해당 PE 를 실행하는데 필요한 운영체제의 최소 버전을 의미한다.
MajorImageVersion , MinorImageVersion
사용자가 정의할 수 있는 필드로, 우리가 만드는 EXE나 DLL에 사용자 임의의 버전을 주입할 수 있다.
MajorSubsystemVersion , MinorSubsystemVersion
해당 PE 를 실행하는데 필요한 서브 시스템의 최소 버전을 의미한다.
Win32VersionValue
VC++ 6.0 SDK 까지는 예약필드, VC++ 7.0 SDK 부터는 Win32VersionValue 라는 이름을 가진 필드로 바뀌었다.
하지만 거의 사용되지 않으며, 보통 0 으로 설정된다.
SizeOfImage
로더가 해당 PE를 메모리에 로드할때 예약(Reserve) 해야 할 충분한 크기를 가리키는 값으로
반드시 SectionAlignment 필드값의 배수가 되어야한다.
SizeOfHeaders
PE 파일의 전체 헤더 크기를 의미하는것으로, 아래 항목들의 크기를 모두 합친 바이트 수를 가진다.
- IMAGE_DOS_HEADER 의 e_lfanew
- IMAGE_NT_HEADERS 의 Signature
- IMAGE_FILE_HEADER
- IMAGE_OPTIONAL_HEADER
- IMAGE_SECTION_HEADER 배열
CheckSum
이미지의 CheckSum 값으로 IMAGEHELP.DLL 의 CheckSum-MappedFile 함수를 통해 얻을 수 있다.
별도의 설정이 없는 한 0을 가지며, 바이러스나 크랙과 같이 악의적 목적을 가지고
PE의 특정 내용을 변경했을 경우 이 필드값을 체크하여 해당 PE의 내용이 변경되었는지 판별할 수 있다.
Subsystem
해당 PE 파일이 사용자 인터페이스로 사용하는 서브 시스템의 종류를 나타낸다.
WinNT.h 파일에 아래와 같이 정의되어 있다.
// Subsytem Values
#define IMAGE_SUBSYSTEM_UNKNOWN 0 // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE 1 // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI 5 // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI 7 // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 // Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12
#define IMAGE_SUBSYSTEM_EFI_ROM 13
#define IMAGE_SUBSYSTEM_XBOX 14
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16
#define IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG 17
DllCharacteristics
해당 PE가 DLL 일때 어떤 상황에서 어떤 DLL 초기화 함수가 호출되어야 하는지를 지시하는 플래그이다.
각 플래그에 대한 정보는 MSDN의 IMAGE_OPTIONAL_HEADER : DllCharacteristics 에서 찾아볼 수 있다.
SizeOfStackReserve , SizeOfStackCommit , SizeOfHeapReserve , SizeOfHeapCommit
스택과 힙의 크기와 관련된 설정을 하는 필드로, PE가 메모리에 로드될 때,
시스템은 이 필드의 값을 참조하여 해당 프로세스에 디폴트 스택과 힙을 생성한다.
LoaderFlags
0을 가지며, 더이상 사용되지 않는다.
NumberOfRvaAndSizes
IMAGE_DATA_DIRECTORY 구조체 배열의 엔트리 개수를 의미하지만 16으로 고정되어 있기 때문에
이 필드의 값은 항상 0x00000010이 되어야되며 WinNT.h 파일에는 16 대신 아래와 같은 매크로로 정의되어있다.
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
DataDirectory[]
typedef struct _IMAGE_DATA_DIRECTORY
{
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
- VirtualAddress : 각 디렉터리의 엔트리가 대표하는 정보가 위치하는 영역의 시작 RVA를 가지고있다.
DataDirectory[] 의 크기는 128bytes로 IMAGE_DATA_DIRECTORY 구조체를 배열로 가진다.
이 배열의 실제 엔트리 수는 NumberOfRvaAndSizes 필드에서 지정되고 16개의 엔트리는 모두 존재하며,
이 배열의 마지막 엔트리인 15번째 엔트리는 배열의 끝을 의미하기위해 0으로 설정된다.
15개의 각 엔트리는 각각의 의미를 가지고 있으며, 특정 섹션 아래에 위치하기때문에 반드시 참조해야 하는 필드이다.
만약 Directory Entry 와 관련된 섹션이 PE에 존재하지않을 경우 IMAGE_DATA_DIRECTORY 의 필드는 0으로 설정된다.
// Directory Entries
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
'보안 > Reversing' 카테고리의 다른 글
Injection 기법 (0) | 2020.09.04 |
---|---|
PE 파일 구조 - IMAGE_SECTION_HEADER (0) | 2020.07.24 |
PE 파일 구조 - IMAGE_FILE_HEADER (0) | 2020.07.23 |
PE 파일 구조 - IMAGE_NT_HEADERS (0) | 2020.07.23 |
PE 파일 구조 - IMAGE_DOS_HEADER (0) | 2020.07.22 |