반응형

리버싱 핵심 원리에서 나오는 Windows XP SP3 환경에서의 notepad.exe 파일 로딩

 

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;

 

IAT 영역 구조

 


 

미리보기

 

 

분석은 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

 

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의 DataDirecory

 

위의 사진에서 드래그 된 부분이 IMAGE_OPTIONAL_HEADER의 DataDirectory 부분이다.

 

IMAGE_OPTIONAL_HEADER의 DataDirectory[1] 부분

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

IAT 영역 구조 예시

 


IMAGE_IMPORT_DESCRIPTOR.NAME 필드

 

 

2F77E 주소로 이동하면 KERNEL32.dll이 있다.

 

IAT 영역 구조 예시

 


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)

 

IAT 영역 구조 예시

 


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 영역 구조 예시

 


IAT

 

 

위의 사진이 KERNEL32.dll 라이브러리에 해당하는 IAT 배열 영역의 시작 부분이다.

(IMAGE_IMPORT_DESCRIPTOR의 FirstThunk 필드 참고)

 

INT와 마찬가지로 구조체 포인터 배열 형태로 되어 있으며 배열은 NULL로 끝난다.

 

INT에서와 같이 2F424(kernel32.dll!SetEvent 함수) 라는 값이 들어있다.

 

IAT 영역 구조 예시

 


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://hashs.tistory.com/208

 

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

 

 

반응형

+ Recent posts