PE Header : Dos Header ~ Section Header
PE body : Section들의 내용
참고) 파일에서는 offset으로, 메모리에서는 Virutal Address(절대주소)로 위치를 표현한다.
DOS Header
- 중요 멤버 : e_magic, e_lfanew
- 크기 : 0x40(64)byte
#include <WinTH.h>
typedef struct _IMAGE_DOS_HEADER // DOS .EXE header
{
WORD e_magic; // Magic number / DOS signature : 4D5A("MZ")
WORD e_cblp; // Byte on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Inital SP value
WORD e_csum; // Checksum
WORD e_ip; // Initital IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header / offset to NT header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
NT_HEADER
- 크기 : 32bit - 0xF8(248)byte, 64bit - 0x108(264)byte
#include <WinTH.h>
typedef struct _IMAGE_NT_HEADERS
{
DWORD Signature; // 4byte, PE Signature : 50450000("PE"00)
IMAGE_FILE_HEADER FileHeader; // 20 byte
IMAGE_OPTIONAL_HEADER32 OptionalHeader; // 224byte
} IMAGE_NT_HEADER32, *PIMAGE_NT_HEADER32;
NT HEADER - File Header
- 파일의 개략적인 속성을 나타내는 IMAGE_FILE_HEADER 구조체이다.
- 크기 : 20byte
- 중요 멤버 : Machine, NumberOfSections, SizeOfOptionalHeader, Characteristics
(이 값이 정확히 세팅되어 있지 않으면 파일은 정상적으로 실행되지 않는다.)
typedef struct _IMAGE_FILE_HEADER{
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAE_FILE_HEADER;
Machine
#define IMAGE_FILE_MACHINE_UNKNOWN 0
#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386.
#define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
#define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5 0x01a8 // SH5
#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB 0x01c2 // ARM Thumb/Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_AM33 0x01d3
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE 0x0520 // Infineon
#define IMAGE_FILE_MACHINE_CEF 0x0CEF
#define IMAGE_FILE_MACHINE_EBC 0x0EBC // EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R 0x9041 // M32R little-endian
#define IMAGE_FILE_MACHINE_CEE 0xC0EE
Characteristics
#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.
NT Header - Optional Header
- PE 헤더 구조체 중에서 가장 크기가 큰 IMAGE_OPTIONAL_HEADER이다.
- 중요 멤버 : Magic, AddressOfEntryPoint, ImageBase, SectionAlignment, FileAlignment, SizeOfimage, SizeOfHeader, Subsystem, NumberOfRvaAndSizes, DataDirectory
- 크기 : 32bit - 224byte, 64bit - 240byte
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;
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
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;
Subsystem
값 | 의미 | 비고 |
1 | Driver File | 시스템 드라이버(ex. ntfs.sys) |
2 | GUI 파일 | 창 기반 애플리케이션(ex. notepad.exe) |
3 | CUI 파일 | 콘솔 기반 애플리케이션(ex. cmd.exe) |
DataDirectory
- IMAGE_DATA_DIRECTORY 구조체의 배열로 배열의 각 항목마다 정의된 값을 가진다.
DataDirectory[0] = EXPORT Directory
DataDirectory[1] = IMPORT Directory
DataDirectory[2] = RESOURCE Directory
DataDirectory[3] = EXCEPTION Directory
DataDirectory[4] = SECURITY Directory
DataDirectory[5] = BASERELOC Directory
DataDirectory[6] = DEBUG Directory
DataDirectory[7] = COPYRIGHT Directory
DataDirectory[8] = GLOBALPTR Directory
DataDirectory[9] = TLS Directory
DataDirectory[A] = LOAD_CONFIG Directory
DataDirectory[B] = BOUND_IMPORT Directory
DataDirectory[C] = IAT Directory
DataDirectory[D] = DELAY_IMPORT Directory
DataDirectory[E] = COM_DESCRIPTOR Directory
DataDirectory[F] = Reserved Directory
IMAGE_SECTION_HEADER
- 각 섹션별 IMAGE_SECTION_HEADER 구조체 배열로 되어 있다.
- 중요 멤버 : VirtualSize, VirtualAddress, SizeofRawData, PointerToRawData, Characteristics
참고)
VirtualAddress와 PointerToRawData는 아무 값이나 가질 수 없고, 각각 IMAGE_OPTIONAL_HEADER에 정의된 SectionAlignment와 FileAlignment에 맞게 결정된다.
VirtualSize와 SizeOfRawData는 일반적으로 서로 다른 값을 가지는데, 즉 파일에서의 섹션 크기와 메모리에 로딩된 섹션의 크기를 다르다
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
Characteristics
#define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized data.
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable.
#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable.
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_section_header
IMAGE_IMPORT_DESCRIPTOR
- PE 파일은 자신이 어떤 라이브러리를 Import 하고 있는지 IMAGE_IMPORT_DESCRIPTOR 구조체에 명시하고 있다.
- 라이브러리의 개수만큼 구조체의 배열 형식으로 존재한다.
- 구조체 배열의 마지막은 NULL 구조체로 끝나게 된다.
- 중요 멤버 : OriginalFirstThunk, Name, FirstThunk
- 크기 : 20byte
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; // INT(Import Name Table) address (RVA)
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name; // library name string address (RVA)
DWORD FirstThunk; // IAT(Import Address Table) address (RVA)
} IMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_THUNK_DATA64
{
union
{
ULONGLONG ForwarderString; // PBYTE
ULONGLONG Function; // PDWORD
ULONGLONG Ordinal;
ULONGLONG AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} 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
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
/*
공용체이기 때문에 상황에 따라 쓰임새가 다르므로 상황에 따라 아래와 같이 해석하면 된다.
ForwarderString : 실제 Import 한 함수가 Forwarding된 함수일 경우. Forwarding에 대해서는 Export에서 설명
Function : FirstThunk의 경우 메모리에 로딩이 되면 실제 함수 주소를 가리키는 IMAGE_THUNK_DATA의 RVA값을 표시한다고 했는데, 이때 실제 Function의 주소가 포함된 부분
Ordinal : Import를 함수 명이 아니라 서수(Ordinal)로 한 경우. 0x80000000 값으로 마스크하면 최 상위 비트를 구할 수 있는데 이것이 1로 셋팅된 경우 서수로 판단
AddressOfData : 실제 Import 된 함수의 이름이 포함된 IMAGE_IMPORT_BY_NAME 구조체에 대한 RVA 주소 포함
*/
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint; // ordinal
BYTE Name[1]; // function name string
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
미리보기
분석은 windows 11 64bit 환경에서 진행했다.
위의 사진은 notepad.exe 를 HXD로 열었을 때 나오는 값이고, PE 파일의 기본 구조이다.
4D 5A와 50 45 00 00 부분을 보면 “MZ”와 “PE”이다.
4D 5A = “MZ”는 DOS Header의 e_Magic의 값이고, “50450000”는 NT_Header의 signature 값이다.
참고) “MZ” : 마크 주비코브스키
00000000 주소의 “4D”부터 40byte는 DOS Header에 해당하고, 000000F0 주소의 50부터는 NT_Header에 해당하며, 이 사이의 값은 DOS Stub이다.
참고) DOS Stub의 존재는 옵션이며, 크기도 가변적이다.
Dos Header
PE 스펙에 맞게 파일 시작 2byte e_magic의 값은 4D5A이며, e_lfanew 값은 000000F0이다.
e_magic : 시그니처
e_lfanew : NT Header 시작 offset
시험 삼아 이 값들을 변경한 후 저장해서 실행하면 정상 실행되지 않을 것이다.
(PE 스펙에 따라서 더 이상 PE 파일이 아니기 때문이다.)
Dos Stub
파일 offset 40 ~ 4D 영역은 16bit 어셈블리 명령어이다.
32bit Windows OS에서는 이쪽 명령어가 실행되지 않는다.
(PE 파일로 인식하기 때문에 아예 이쪽 코드를 무시한다.)
Notepad.exe 파일을 DOS 환경에서 실행하거나, DOS용 디버거(debug.exe)를 이용해서 실행하면 저 코드를 실행시킬 수 있다
(이들은 PE File Format을 모르기 때문에 DOS EXE 파일로 인식한다.)
debug notepad.exe
Windows XP SP3에서 debug로 notepad를 열고 u(Unassemble) 명령을 입력하면 위의 사진과 같이 뜨는데
mov DX, 000E 에서 0E는 “This program cannot be run in DOS mode” 문자열을 가리킨다.
INT 21 는 AH = 09 구문을 통해 09라는 인자에 해당하는 인터럽트를 호출하는 건데 이는 WriteString()이라는 함수를 호출하는 것이다.
(즉 INT21, AH = 09는 문자열을 화면에 출력하는 인터럽트 기능이다.)
두번째 INT 21 부분은 AX = 4C01 구문을 통해 4C01이라는 인자에 해당하는 인터럽트를 호출하는 건데 이는 Exit()이라는 함수를 호출하는 것이다.
참고) 하나의 실행 파일에 DOS와 Windows에서 모두 실행 가능한 파일을 만들 수도 있다.
(DOS 환경에서는 16bit DOS용 코드, Windows 환경에서는 32bit Windows 코드가 각각 실행)
NT Header
위의 사진은 notepad.exe.의 IMAGE_NT_HEADERS의 내용이다.
IMAGE_NT_HEADERS 구조체의 크기는 108h(=264)로 상당히 큰 구조체이다.
IMAGE_NT_HEADERS 구조체의 가장 첫 번째 멤버는 Signature로 50450000h(”PE”00) 값을 가진다.
즉, 50450000 부분부터가 NT Header의 시작 부분이다.
IMAGE_FILE_HEADER
Hex Editor에서 notepad.exe의 IMAGE_FILE_HEADER 구조체이다.
위의 사진을 알아보기 쉽게 구조체 멤버로 표현하면 아래와 같다.
주소 | Value | Description |
000000F4 | 8664 | machine |
000000F6 | 0007 | number of sections |
000000F8 | 22958C2B | time date stamp |
000000FC | 00000000 | offset to symbol table |
00000100 | 00000000 | number of symbols |
00000104 | 00F0 | size of optional header |
00000106 | 0022 | characteristics |
[ IMAGE_FILE_HEADER ] - notepad.exe
offset value description
----------------------------------------------------------
000000f4 8664 machine
000000f6 0007 number of sections
000000f8 22958C2B time date stamp
000000fc 00000000 offset to symbol table
00000100 00000000 number of symbols
00000104 000F sizeof optional header
00000106 0022 characteristics
IMAGE_FILE_EXECUTABLE_IMAGE
IMAGE_FILE_LARGE_ADDRESS_AWARE
IMAGE_OPTIONAL_HEADER
위의 사진은 HxD에서 notepad.exe 파일의 IMAGE_OPTIONAL_HEADER 구조체 영역을 표시한 것이다.
구조체 멤버들의 값과 설명은 아래의 windows xp sp3 환경에서의 notepad.exe를 HxD로 열었을 때의 사진을 참고하면 된다.
참고)
- Magic의 값이 20B 이므로 IMAGE_OPTIONAL_HEADER64 구조체를 사용한다.
- AddressofEntryPoint : 1B60
- ImageBase 값 : 00 00 00 01 40 00 00 00 = 140000000
- NT Header의 IMAGE_OPTIONAL_HEADER64의 크기는 NT Header의 File Header에서 SizeofOptionalHeader의 값을 참고하여 F0(=240byte)이다.
- Checksum : 00055BE9
- Subsystem : 0002(=GUI)
- SizeofRvaAndSizes : 00000010(=16)
- RVA of import directory : 02E580
- Size of import directory : 00000208(=520byte)
- RVA of IAT directory : 000278C8
- Size of IAT directory : 00000990(=2448byte)
IMAGE_SECTION_HEADER
참고)
VirtualAddress는 memory 상에서 Section이 시작하는 주소(RVA)이며, .text 섹션의 VirtualAdress는 1000(RVA) 이다.
PointerToRawData는 1000 값이다.
각 섹션들의 VirtualAddress와 PointerToRawData를 구하면 아래와 같다.
.text
Virtual Size : 25E42
VA : 140000000(ImageBase) + 1000(VirtualAddress RVA) = 140001000(VA)
size of Raw Data: 26000
PointerToRawData: offset 1000
.rdata
Virtual Size : 989A
VA : 140000000(Imagebase) + 27000(VirtualAddress RVA) = 140027000(VA)
size of Raw Data : A000
PointerToRawData : offset 27000
.data
Virtual Size : 26A0
VA : 140000000(Imagebase) + 31000(VirtualAddress RVA) = 140031000(VA)
size of Raw Data : 1000
PointerToRawData: offset 31000
.pdata
Virtual Size : 134D
VA : 140000000(Imagebaes) + 34000(VirtualAddressRVA) = 140034000(VA)
size of Raw Data : 2000
PointerToRawData: offset 32000
.didat
Virtual Size : 188h
VA : 140000000(Imagebaes) + 36000(VirtualAddressRVA) = 140036000(VA)
size of Raw Data : 1000
PointerToRawData: offset 34000
.rsrc
Virtual Size : 1E1D0
VA : 140000000(Imagebaes) + 37000(VirtualAddressRVA) = 140037000(VA)
size of Raw Data : 1F000
PointerToRawData: offset 35000
.reloc
Virtual Size : 31C
VA : 140000000(Imagebaes) + 56000(VirtualAddressRVA) = 140056000(VA)
size of Raw Data : 1000
PointerToRawData: offset 54000
구조체 멤버들과 값은 아래의 내용을 참고한다.
참고)
PE 파일 설명에서 IMAGE라는 용어가 자주 등장하는데, 이는 PE 파일이 메모리에 로딩될 때 파일이 그대로 올라가는 것이 아니라 섹션 헤더에 정의된 섹션 시작 주소, 섹션 크기 등에 맞춰서 올라가기 때문에 파일에서의 PE와 메모리에서의 PE는 서로 다른 모양을 가지므로 이를 구별하기 위해서 메모리에 로딩된 상태를 이미지라는 용어를 사용해 구별한다.
VA
RVA + ImageBase = VA
RVA to RAW 계산
RAW - PointerToRawData = RVA - VirtualAddress
RAW = RVA - VirtualAddress(섹션의 시작 주소(RVA 값)) + PointerToRawData(섹션 헤더의 항목)
ex) RVA = 5000일 때의 File Offset 값 구하기
1. 해당 RVA 값이 속해 있는 섹션을 찾는다.
2. 비례식 사용
IMAGE_IMPORT_DESCRIPTOR
위의 사진에서 드래그 된 부분이 IMAGE_OPTIONAL_HEADER의 DataDirectory 부분이다.
IMAGE_IMPORT_DESCRIPTOR는 PE 헤더가 아닌 PE body에 존재하는데 그 곳으로 가기 위한 정보는 PE 헤더 중 IMAGE_OPTIONAL_HEADER.DataDirectory[1].VirtualAddress 값이 실제 IID 구조체 배열의 시작 주소이다.(RVA)
위의 사진이 IMAGE_OPTIONAL_HEADER.DataDirectory[1] 부분이고, 이 중 00 02 E5 80이 RVA of IMPORT Directory 값이고, 00 00 02 08이 size of IMPORT Directory의 값이다.
2E580(RVA) - 27000(.rdata의 VirtualAddress) + 27000(pointerToRawData) = 2E580(RAW)
Member | RVA | RAW |
OriginalFirstThunk(INT) | 0002E8B8 | 2E8B8 |
TimeDateStamp | 00000000 | |
ForwarderChain | 00000000 | |
Name | 0002F77E | 2F77E |
FirstThunk(IAT) | 000279F8 | 279F8 |
IMAGE_IMPORT_DESCRIPTOR.NAME 필드
2F77E 주소로 이동하면 KERNEL32.dll이 있다.
INT(Import Name Table)
OriginalFirstThunk(INT)의 값 2E8B8(RAW)을 따라가면 02F424(RVA)이라는 값이 있다.
위 사진이 INT 영역의 시작 부분이다.
INT 영역은 주소 배열 형태로 되어 있는데, 사실 INT는 IMAGE_THUNK_DATA 구조체 배열의 시작 주소값들이 나열 되어 있다.
(배열의 끝은 NULL로 되어 있다.)
결국에는 주소 값 하나하나가 각각의 IMAGE_IMPORT_BY_NAME 구조체를 가리키는 구조이다.
배열의 첫 번째 값인 2F424(RVA)를 따라가본다.
02F424(RVA) - 27000(.rdata VirtualAddress) + 27000(PointerToRawData) = 02F424(RAW)
IMAGE_IMPORT_BY_NAME
2F424 offset으로 이동하면 위의 사진과 같은 위치가 나온다.
offset 2F424의 최초 2byte 값 053C는 IMAGE_IMPORT_BY_NAME 구조체의 Hint(Ordinal) 값으로 라이브러리에서 함수의 고유번호이다.
Hint(Ordinal) 값 뒤로 SetEvent 함수 이름 문자열이 있다.
(문자열의 마지막은 c 언어와 마찬가지로 NULL로 끝난다.)
즉, 2F424 offset은 kernel32.dll!SetEvent 함수를 가리킨다.
참고)
- INT는 IMAGE_IMPORT_BY_NAME 구조체 포인터 배열이다.
IAT
위의 사진이 KERNEL32.dll 라이브러리에 해당하는 IAT 배열 영역의 시작 부분이다.
(IMAGE_IMPORT_DESCRIPTOR의 FirstThunk 필드 참고)
INT와 마찬가지로 구조체 포인터 배열 형태로 되어 있으며 배열은 NULL로 끝난다.
INT에서와 같이 2F424(kernel32.dll!SetEvent 함수) 라는 값이 들어있다.
x64dbg에서 ImageBase 확인
x64dbg에서 보면 7FF65CCA000이 ImageBase 값이다.
x64dbg에서 entrypoint 확인
NT Header - Optional Header - AddressOfEntryPoint의 값은 1B60(RVA)이다.
7FF65CCA000(ImageBase) + 1B60(RVA) = Entry Point의 VA
x64dbg에서 IMAGE_IMPORT_DESCRIPTOR 영역 확인
IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소는 IMAGE_OPTIONAL_HEADER.DataDirectory[1].VirtualAddress 값(RVA)이다.
위에서 IMAGE_OPTIONAL_HEADER.DataDirectory[1].VirtualAddress 값(RVA)은 2E580이었다.
7FF65CCA0000(ImageBase) + 2E580(RVA) = 7FF6 5CCC E580(VA)
IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소로 가니 위에서 봤던 IMAGE_IMPORT_DESCRIPTOR 구조체의 값이 리틀 엔디언으로 들어가있다.
Member | RVA | RAW |
OriginalFirstThunk(INT) | 0002E8B8 | 2E8B8 |
TimeDateStamp | 00000000 | |
ForwarderChain | 00000000 | |
Name | 0002F77E | 2F77E |
FirstThunk(IAT) | 000279F8 | 279F8 |
x64dbg에서 INT 영역 확인
7FF65CCA0000(ImageBase) + 2E8B8(RVA) = 7FF65CCCE8B8(VA)
위의 위치로 이동하면 INT 영역이고, IMAGE_IMPORT_BY_NAME 구조체 포인터의 배열이다.
첫 번째 IMAGE_IMOPRT_BY_NAME 구조체의 값은 02F424가 들어있다.
7FF65CCA0000(ImageBase) + 2F424(RVA) = 7FF6 5CCC F424(VA)
위의 위치로 이동하면 이전에 확인했던 kernel32.dll!SetEvent 함수가 있다.
x64dbg에서 notepad.exe의 IAT값 확인
notepad.exe의 ImageBase 값은 7FF65CCA0000이다.
kernel32.dll!SetEvent 함수의 IAT 주소는 7FF65CCA000(ImageBase) + IMAGE_IMPORT_DESCRIPTOR에서 FirstThunk(IAT)의 값 279F8(RVA) = 7FF65CCC79F8(VA) 이다.
7FF65CCC79F8로 이동하면 위의 사진과 같이 kernel32.dll!SetEvent 함수를 호출하는 주소가 들어가 있다.
7FFCB55B2DF0 주소로 이동하면 위의 사진처럼 SetEvent 함수 코드로 jmp 하는 코드가 있고
jmp문을 실행하면 위의 사진처럼 kernel32.dll!SetEvent 함수 코드 부분이 나온다.
참고 URL
https://kkamagui.tistory.com/71
https://turtleneck.tistory.com/28
https://yokang90.tistory.com/26
https://bpsecblog.wordpress.com/page/2/?s=PE
https://limjunyoung.tistory.com/180
https://maple19out.tistory.com/22
'Reversing > 실습' 카테고리의 다른 글
[Reversing 실습] 지뢰찾기.exe DLL Injection (0) | 2022.08.24 |
---|---|
Windows 7 64bit에서 32bit notepad.exe PE 재배치(Base Relocation Table) (0) | 2022.06.19 |
Windows 7 64bit에서 HxD로 32bit notepad.exe PE 파일 EP 영역 확인 (0) | 2022.06.19 |
UPX packer로 notepad.exe 실행 압축한 파일 분석 (0) | 2022.06.13 |
windows11 64bit 환경에서 PE 파일(Kernel32.dll) EAT 분석 냠냠쓰 (1) | 2022.06.08 |