- NT Header
NT Header 부분에는 IMAGE_NT_HEADERS 구조체가 존재 하고, 구조체의 크기는 F8 이다.
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; // PE Signature : 50450000 ("PE"00)
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
이 구조체는 Signature, FileHeader, Optionalheader 이 3개의 멤버로 되어 있는데,
NT Header의 첫번째 멤버는 Signature로 50450000 ("PE"00)의 값을 가진다.
50 45으로 되어있는 오프셋부터 NT Header의 시작이다.
NT Header의 두번째 멤버는 FileHeader로 구조체 형식으로 되어 있다
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;
FileHeader의 구조체 멤버들 중에서는 이 1 ~ 4 멤버가 제일 중요하다. 5는 그냥 번외다 알면 좋은
- Machine
- NumberOfSections
- SizeOfOptionalHeader
- Characteristics
- TimeDateStamp
1. Machine은 CPU별로 고유한 값이며 Machine 안에는 CPU마다 다른 값들이 들어간다.
이 정보들은 winnt.h 헤더 파일에 정의 되어 있다.
2. PE파일은 코드, 데이터, 리소스 등이 각각의 섹션에 나뉘어서 저장되는데, NumberOfSections 이 섹션의 개수를 나타낸다 반드시 0보다 커야한다.
3. SizeOfOptionalHeader는 (Optionalheader가 미리 나오는데), NT Header의 마지막에 있는 Optionalheader의 구조체 인 IMAGE_OPTIONAL_HEADER32 의 크기를 나타낸다. C언어의 구조체라 크기가 이미 결정되어 있지만, PE로더는 이것을 보고 Optionalheader의 구조체 크기를 인식한다.
SizeOfOptionalHeader와 e_lfanew멤버의 범위를 이상하게 지정하면 꽈배기 PE파일을 만들 수 있다.
32비트 PE는 IMAGE_OPTIONAL_HEADER32
64비트 PE는 IMAGE_OPTIONAL_HEADER64
4. Characteristics는 처음 말했던 파일의 속성을 나타내는 값으로 이 파일이 실행 가능한 형태인지, DLL파일인지 등의 정보들이 bit OR 형식으로 조합된다. 이 정보들도 winnt.h 헤더 파일에 정의 되어있다.
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable
#define IMAGE_FILE_DLL 0x2000 // File is a DLL.
이런 식으로 값을 조합하여 Characteristics안에 들어가게 된다.
5. TimeDateStamp는 실행에 영향을 미치지 않으며 파일의 빌드 시간을 나타낸 값이다.
NT Header의 세번째 멤버로 Optionalheader 는 IMAGE_OPTIONAL_HEADER32라는 구조체를 가진다.
(아까 나왔던) IMAGE_FILE_HEADER의 SizeOfOptionalHeader옵션이 이 구조체의 크기를 나타내는 것이다.
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
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;
1. Magic은 이 구조체가 32bit용이라면 0x10B의 값을,64bit용이라면 0x20B의 값을 가지게 된다.
2. AddressOfEntryPoint은 EP(Entry Point) 의 RVA(Relative Virtual Address) 값을 가지고 있다.
- RVA값이기 때문에 실제 EP의 VA(실제 주소)는 AddressOfEntryPoint+ImageBase이다.
- PE로더는 파일을 메모리에 올리고 EIP를 AddressOfEntryPoint+ImageBase로 설정한다.
3. ImageBase는 PE파일이 메모리에 로딩되는 시작 주소를 나타낸다.
4. SectionAlignment, FileAlignment들은 하나씩 보면
- 파일에서 섹션의 최소 단위를 나타내는것이 FileAlignment이다.
- 메모리에서 섹션 최소 단위를 나타내는것이 SectionAlignment이다.
- 그렇기 때문에 파일/메모리의 섹션 크기는 반드시 각각 FileAlignment/SectionAlignment 의 배수가 되어야 한다.
5. SizeOflmage는 PE파일이 메모리에 로딩됐을 때 가상 메모리(VA)에서 PE Image(PE구조)가 차지하는 크기를 나타낸다.
6. SizeOfHeader는 PE header 의 전체 크기를 나타냅니다.
- 이 값 역시 FileAlignment 의 배수 이어야 한다.
- 파일 시작에서 SizeOfHeader 옵셋만큼 떨어진 위치에 첫번째 섹션이 위치한다.
- PE header 다음에는 PE body니까 첫번째 섹션이 오게 되는거다.
7. Subsystem은 이 값을 보고 무슨 파일인지 알 수 있다.
값 | 의미 | 비고 |
1 | Driver file | 시스템 드라이버(예: ntfs.sys) |
2 | GUI(Graphic User Interface) 파일 | 창 기반 애플리케이션(예: notepad.exe) |
3 | CUI(Console User Interface) 파일 | 콘솔 기반 애플리케이션(예: cmd.exe) |
8. NumberOfRvaAndSizes는 바로 밑에 있는 IMAGE_DATA_DIRECTORY 구조체의 배열의 크기를 정합니다.
- 구조체에 #define으로 16이라고 크기가 정해져있지만 windows에서는 이 값을 보고 구조체 배열의 크기를 정한다.
9. DataDirectory는 바로 위에 있던 멤버가 크기를 가르키던 IMAGE_DATA_DIRECTORY 구조체의 배열로써, 배열의 각 항목마다 정의된 값을 가지게 된다.
DataDirectory[0] = EXPORT Directory
DataDirectory[1] = IMPORT Directory
DataDirectory[2] = RESOURCE Directory
DataDirectory[9] = TLS Directory
일부만 가져왔는데, 빨간색으로 표시한 EXPORT, IMPORT, RESOURCE, TLS Directory 이 4개가 중요하고,
특히 IMPORT 와 EXPORT Directory 구조는 PE header 에서 매우 중요하다고 한다. (책 뒤에 나온다고 한다.)
이것들을 이미지로 확인해보면,
Magic : 020B (64bit)
AddressOfEntryPoint : 00024050
Imagebase : 00000001
SectionAlignment : 00001000
FileAlignment : 00000200
SizeOfImage : 03800000
SizeOfHeader : 04000000
Subsystem : 0200
NumberOfRvaAndSizes : 00000010 (0x10 == 16)
NT_header 끝
다음에는 Section Header 하겠습니다.
'윈도우 > PE 구조' 카테고리의 다른 글
PE 구조 (6) - IAT (0) | 2022.04.19 |
---|---|
PE 구조 (5) - (RVA to RAW) 예제 포함 (0) | 2022.04.05 |
PE 구조 (4) - (Section Header) (0) | 2022.04.02 |
PE구조 (2) - (DOS Header, DOS Stub) (0) | 2022.03.30 |
PE 구조 (1) - (PE File Format, VA & RVA) (0) | 2022.03.29 |
최근댓글