Windows PE 구조

Windows PE 구조

2022년 3월 18일

PE 구조 #

d1c016ff-9204-43ad-a661-244ed9bc0230


1. IMAGE_DOS_HEADER #

http://stixproject.github.io/data-model/1.2/WinExecutableFileObj/DOSHeaderType/

DOS와 호환을 위해 만든 헤더

 1/*** WinNT.h ***/
 2typedef struct _IMAGE_DOS_HEADER // DOS .EXE header
 3{
 4    WORD e_magic;       // Magic number
 5    WORD e_cblp;        // Byte on last page of file
 6    WORD e_cp;          // Pages in file
 7    WORD e_crlc;        // Relocations
 8    WORD e_cparhdr;     // Size of header in paragraphs
 9    WORD e_minalloc;    // Minimum extra paragraphs needed
10    WORD e_maxalloc;    // Maximum extra paragraphs needed
11    WORD e_ss;          // Initial (relative) SS value
12    WORD e_sp;          // Checksum
13    WORD e_ip;          // Initital IP value
14    WORD e_cs;          // Initial (relative) CS value
15    WORD e_lfarlc;      // File address of relocation table
16    WORD e_ovno;        // Overlay number
17    WORD e_res[4];      // Reserved words
18    WORD e_oemid;       // OEM identifier (for e_oeminfo)
19    WORD e_oeminfo;     // OEM information; e_oemid specific
20    WORD e_res2[10];    // Reserved words
21    LONG e_lfanew;      // File address of new exe header
22} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

2. MS-DOS Stub Program #

0830001c-bd85-4b84-bd7f-7d39adb8e106

호환성을 위해 추가된 도스 실행코드이다. 실제로 도스에서 실행됐을때 이 문구가 출력된다.


3. IMAGE_NT_HEADERS #

https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_nt_headers32

1/*** 32bit ***/
2typedef struct _IMAGE_NT_HEADERS {
3  DWORD                   Signature;
4  IMAGE_FILE_HEADER       FileHeader;
5  IMAGE_OPTIONAL_HEADER32 OptionalHeader;
6} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
7
8/*** 64bit는 OPTIONAL_HEADER가 64bit용인 것 빼고는 차이 없다. ***/
  • Signature : PE 파일은 무조건 PE가 적혀있다.

3-1. IMAGE_FILE_HEADER #

https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_file_header

파일에 대한 기본적인 정보가 저장되어 있다.

1typedef struct _IMAGE_FILE_HEADER {
2  WORD  Machine;              // 파일이 동작하는 CPU 종류
3  WORD  NumberOfSections;     // 섹션의 수
4  DWORD TimeDateStamp;        // 파일 생성 시간
5  DWORD PointerToSymbolTable;
6  DWORD NumberOfSymbols;
7  WORD  SizeOfOptionalHeader; // OptionalHeader 크기
8  WORD  Characteristics;      // 파일의 형식
9} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
  • Machine : x86=0x14c, x64=0x200, AMD64=0x8664

  • TimeDateStamp : 링커에서 이미지를 만든 시간을 의미한다

  • PointerToSymbolTable, NumberOfSymbols : 옛날에 심볼 테이블이 파일 내에 포함되어있을때 사용하던 방식인데, 요즘 심볼은 크기가 너무 커져 별도로 생성되기 때문에 사용되지 않는 필드이다.

  • SizeOfOptionalHeader : 이후에 나오게될 OptionalHeader의 크기를 저장한다. 32bit=0xE0, 64bit=0xF0, OBJ=0X00

  • Characteristics : 현재 파일의 형식을 알려준다.

    • DLL : 0x2000
    • execute : 0x0002 (dll도 여기에 포함된다)

3-2. IMAGE_OPTIONAL_HEADER32 #

https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header32

파일 실행에 관한 중요한 정보가 저장된다.

64bit 헤더는 BaseOfData가 삭제되고 일부 4byte 필드가 8byte로 변경되며 총 0x10byte 만큼 커졌다.

 1
 2typedef struct _IMAGE_OPTIONAL_HEADER {
 3  WORD                 Magic;
 4  BYTE                 MajorLinkerVersion;
 5  BYTE                 MinorLinkerVersion;
 6  DWORD                SizeOfCode;
 7  DWORD                SizeOfInitializedData;
 8  DWORD                SizeOfUninitializedData;
 9  DWORD                AddressOfEntryPoint;
10  DWORD                BaseOfCode;
11  DWORD                BaseOfData;    // 64bit : 삭제
12  DWORD                ImageBase;     // 64bit : ULONGLONG 타입 4byte->8byte
13  DWORD                SectionAlignment;
14  DWORD                FileAlignment;
15  WORD                 MajorOperatingSystemVersion;
16  WORD                 MinorOperatingSystemVersion;
17  WORD                 MajorImageVersion;
18  WORD                 MinorImageVersion;
19  WORD                 MajorSubsystemVersion;
20  WORD                 MinorSubsystemVersion;
21  DWORD                Win32VersionValue;
22  DWORD                SizeOfImage;
23  DWORD                SizeOfHeaders;
24  DWORD                CheckSum;
25  WORD                 Subsystem;
26  WORD                 DllCharacteristics;
27  DWORD                SizeOfStackReserve;  // 64bit : ULONGLONG
28  DWORD                SizeOfStackCommit;   // 64bit : ULONGLONG
29  DWORD                SizeOfHeapReserve;   // 64bit : ULONGLONG
30  DWORD                SizeOfHeapCommit;    // 64bit : ULONGLONG
31  DWORD                LoaderFlags;
32  DWORD                NumberOfRvaAndSizes;
33  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
34} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
35
36typedef struct _IMAGE_DATA_DIRECTORY {
37  DWORD VirtualAddress;
38  DWORD Size;
39} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
  • Magic : OPTIONAL_HEADER32 구조체인 경우 0x10B, 64구조체인 경우 0x20B
  • SizeOfCode : .text Section의 크기를 나타낸다.
  • AddressOfEntryPoint : 프로그램 시작지점 코드(EP)의 주소가 RVA 값으로 저장되어 있다.
  • BaseOfCode : 코드 영역이 시작되는 RVA
  • ImageBase : PE파일이 메모리에 로드되는 시작주소. 보통 정해져있지만 설정에 따라 달라질 수 있다.
  • SectionAlignment : 메모리에서 섹션의 최소단위. 메모리섹션의 시작주소는 반드시 이 값의 배수가 된다.
  • FileAlignment : 파일에서 섹션의 최소단위.
  • SizeOfImage : PE파일이 메모리에 로딩되었을때 전체 크기
  • SizeOfHeaders : 모든 헤더의 크기를 나타낸다. (DOS Header + DOS Stub + PE Header + Section Header)
  • SubSystem : 서브시스템을 구분한다. Driver(0x1), GUI(0x2), CLI(0x3) 등
  • NumberOfRvaAndSizes : DataDirectory의 구조체 멤버의 개수를 나타낸다.
  • DataDirectory : PE 파일에서 중요한 역할을 하는 개체들의 위치와 크기를 나타낸다.
    • [0] Export Directory : EAT와 관련된 정보들을 담고있는 구조체를 가리킨다.
    • [1] Import Directory : IAT와 관련된 정보를 가리킨다.
    • [2] Resource Directory :
    • [5] Base Relocation Directory : 재배치 관련된 데이터 구조에 대한
    • [9] TLS Directory : TLS callback 함수를 이용한 안티리버싱 기법때문에 중요하다
    • [B] Bound Import Directory :


4. IMAGE_SECTION_HEADER #

https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_section_header

섹션 마다 하나씩 가지고있고, 파일과 메모리상에서의 섹션에 대한 정보를 담고있다.

 1typedef struct _IMAGE_SECTION_HEADER {
 2  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];
 3  union {
 4    DWORD PhysicalAddress;
 5    DWORD VirtualSize;
 6  } Misc;
 7  DWORD VirtualAddress;
 8  DWORD SizeOfRawData;
 9  DWORD PointerToRawData;
10  DWORD PointerToRelocations;
11  DWORD PointerToLinenumbers;
12  WORD  NumberOfRelocations;
13  WORD  NumberOfLinenumbers;
14  DWORD Characteristics;
15} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
  • VirtualSize : 메모리에서 섹션이 차지하는 크기
  • VirtualAddress : 메모리에서 섹션의 시작주소 RVA. ImageBase와 더해서 VA를 찾을 수 있다.
  • SizeoOfRawData : 파일에서 섹션이 차지하는 크기
  • PointerToRawData : 파일에서 섹션의 시작 위치 (NULL 패딩 포함)
  • Characteristics : 섹션의 특징
    ex) .text(0x60000020), .data(0xC0000040), .rsrc(0x40000040)
    • CNT_CODE(0x20) : 섹션에 코드가 포함되는지
    • CNT_INIT_DATA(0x40) : 섹션에 초기화 데이터가 포함되는지 .data영역
    • CNT_UNINIT_DATA(0x80) : 섹션에 초기화되지 않은 데이터가 포함되는지 .bss영역
    • MEM_EXECUTE(0x20000000) : 실행가능 권한 포함
    • MEM_READ(0x40000000) : 읽기 권한 포함
    • MEM_WRITE(0x80000000) : 쓰기 권한 포함


RAW to RVA #

PE 파일이 가상메모리에 올라갈때 그대로 올라가지 않고 ImageBase의 위치부터 올라가게 된다.

그리고 헤더의 위치는 그대로 올라가지만, 섹션들 사이의 간격이 더 벌어진다.

03b9ca76-155a-460d-b434-03abff592a9e

  • RAW : PE파일이 로딩되기 전 파일에서의 오프셋 위치.
    • RAW = RVA - SectionHeader.VA + PointerToRawData.
    • 현재 위치(RVA)에서 해당 섹션의 시작주소(VA)를 빼면 섹션 내에서 위치 오프셋이 나온다. 파일에서 섹션의 시작주소를 더하면 원하는 RVA의 파일오프셋값이 나온다.
  • ImageBase : 프로세스가 메모리에 올라갈때 시작주소를 의미한다. 32bit 시스템에선 exe: 0x00400000, dll:0x10000000 디폴트 였지만, 64bit 시스템에선 보안상의 이유로 프로그램 로더가 결정한다.
  • VA : 프로세스 가상메모리의 절대주소. RVA + ImageBase = VA
  • RVA : 프로세스 가상메모리의 상대주소

Section #

메모리에 올라갈때 ImageBase + 각 섹션 헤더의 VirtualAddress(RVA) 의 가상주소공간에 로드된다.

.text(r-x): 프로그램을 실행하기 위한 코드를 담고있는 섹션. 증분링크를 설정한경우 .textbss 가 생성된다.

.bss(rw-) : 초기화되지 않은 변수를 담은 섹션. 기본 0으로 초기화되는 영역. 메모리에 로드될때 .data섹션에 병합되기 때문에 PE에서 볼수있어도 메모리에선 볼 수 없다. 64bit 에선 PE파일에서도 병합됐다.

.data(rw-): 초기화된 변수를 담고있는 섹션. .bss 섹션도 있는데,

.rdata(r–): 읽기전용 데이터 섹션. 문자열 상수나 c++ 가상함수테이블 등이 있다. .edata, .debug 섹션도 이 섹션에 병합되고, .idata, didat 섹션도 증분링크 옵션을 해제하면 이 섹션으로 병합된다.

데이터디렉터리의 여러 엔트리도 이 섹션에 위치한다.

.reloc: 실행파일에 대한 기준 재배치 정보를 담고있는 섹션.

  • 기준 재배치 : PE가 원하는 위치에 로드되지 못하고 재배치됐을경우 코드실행중 포인터 연산등의 주소참조를 할때 주소를 갱신해주는것을 말한다.

.edata: 내보낼 함수, 변수에 대한 정보를 담고있는 섹션. 보통 DLL의 PE파일에서 이 섹션을 발견할 수 있지만, 로드시 .rdata에 병합된다

.idata: 가져올 DLL, 함수, 변수에 대한 정보를 담고있는 섹션. IAT가 포함되는 섹션이고, 증분링크 옵션을 해제하면 .rdata에 병합된다.

.didat: 지연로딩을 위한 섹션이다.

.tls: Thread Local Storage의 데이터 초기화를 위한 섹션

.rsrc: 대화상자, 아이콘, 커서, 버전정보 등 리소스 관련 데이터들이 배치된다​

.debug: 디버깅 관련 기초 정보가 담기는 섹션. 실제 정보는 PDB 파일에 별도로 보관한다.

.pdata: 예외정보를 담고있는 섹션이며 IMAGE_RUNTIME_FUNCTION_ENTRY 구조체의 배열로 구성된다. 테이블 베이스 예외처리를 사용하는 CPU 플랫폼에서만 제공되기 때문에 64bit PE에 존재하며 코드섹션 함수 분석에도 중요한 역할을 한다.

comments powered by Disqus