PE File Format Advance

0. 들어가며

  • PE (Portable Executable) 파일은 Windows 운영체제에 사용되는 실행파일 형식
  • 32비트의 경우 PE32 / 64비트의 경우 PE+ 혹은 PE32+ 라고부르며 PE파일의 확장 형태

1. PE File Format

1) PE 파일 종류

종류 주요 확장자

실행 계열 EXE, SCR
라이브러리 계열 DLL, OCX, CPL, DRV
드라이버 계열 SYS, VXD
오브젝트 파일 계열 OBJ
  • OBJ 파일을 제외한 모든 것은 실행 가능한 파일
  • DLL, SYS는 쉘에서 직접 실행은 불가능하지만 다른 형태의 방법으로 실행 가능

(1) 기본 구조

  • 메모장은 일반적인 pe파일의 기본구조로 구성

  • Dos header부터 Section header 까지 = PE 헤더
  • 그 밑 부분의 Section = PE 바디
  • 파일에서는 offset / 메모리에서는 VA로 위치를 표시
  • 메모리에 로딩되면 모양이 변경되어 보여짐
  • NULL padding 존재 = 처리 효율을 높이기위해 최소 기본 단위 사용
  • → 남은 빈 부분을 NULL로 채움

(2) VA&RVA

  • VA : 프로세스 가상 메모리의 절대주소
  • RVA : 기준 위치로부터의 상대주소
  • RVA + ImageBAse = VA
  • PE헤더 내의 정보는 RVA 형태가 많음 → 다른 PE 파일과 충돌이 일어나지 않게 재배치 시 액세스가 가능하도록 RVA 사용

3) PE Header

(1) DOS Header

  • PE 파일 포맷을 만들 당시 DOS 파일에 대한 호환성을 고려하여 만들어짐
  • PE헤더의 앞부분에는 IMAGE_DOS_HEADER 구조체 존재
typedef struct _IMAGE_DOS_HEADER // DOS .EXE header
{
    WORD e_magic;       // Magic number
    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;          // 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
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
  • 구조체의 크기는 40이며, e_magic과 e_lfanew를 꼭 알아두자
  • 첫 번째 필드 e_magic : DOS Signature ( MZ ⇒ ASCII 0x5A4D) 리틀 엔디안으로 4D 5A 형식으로 보여짐
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
  • 마지막 필드 e_lfanew : NT header의 옵셋 표시 해당 값이 가리키는 위치에 NT header의 구조체가 존재

→ NT header의 시작이 100인것을 마지막 필드를 통해 확인 가능

(2) DOS Stub

  • DOS Stub의 존재 여부는 옵션
  • 메모장의 경우 16비트에서 사용되는 DOS용 코드가 DOS Stub에 존재
  • 이 특성을 활용하여 하나의 실행 파일에 두 환경에서 모두 실행 가능한 파일을 생성 가능

(3) NT Header

typedef struct _IMAGE_NT_HEADERS
{
	DWORD Signature;
	IMAGE_FILE_HEADER FileHeader;
	IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADER32, *PIMAGE_NT_HEADER32;
  • 첫 번째 멤버 : Signature → 해당 파일의 구조가 PE인것을 확인 가능
  • 두 번째 멤버 : File_Header
  • 세 번쩨 멤버 : Optional_Header

(4) NT Header - IMAGE_FILE_HEADER

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;

#1 Machine

  • cpu별로 고유한 값

#2 NumberOfSections

  • pe파일이 가지고 있는 섹션의 개수를 나타내며 반드시 0보다는 커야함

#3 SizeOfOptionalHeader

  • 32비트와 64비트의 optional header 구조체의 크기가 다르므로 크기를 명시

#4 Characteristics

  • 파일의 속성을 나타내는 값

사진에 나와있는 것을 통해 파일의 속성이 무엇인지 확인 가능

(5) - 1 / NT Header - IMAGE_OPTIONAL_HEADER32

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 Machine

  • 32비트의 경우 10B, 64비트의 경우 20B

#2 AddressOfEntryPoint

  • Entry Point의 RVA값을 가지고 있음 (프로그램에서 최초로 실행되는 코드의 주소)

#3 ImageBase

  • pe파일이 로딩되는 시작 주소를 나타내며
  • 메모리 로딩 후 EIP값을 ImageBase + AddressOfEntryPoint 값을 세팅

#4 SectionAlignment, FileAlignment

  • 파일/섹션의 최소단위를 나타내는 것
  • 해당 최소 단위의 배수로 크기를 가져야함

#5 SizeOfImage

  • pe파일이 메모리에 로딩되었을때 가상 메모리에서 PE Image가 차지하는 크기

#6 SizeOfHeader

  • PE 헤더의 전체 크기를 나타내며, FileAlignment의 배수임

#7 Subsystem

  • 일반 실행 파일인지 시스템 드라이버인지 구분

#8 NumberOfRvaAndSizes

  • 마지막 멤버인 DataDirectory의 배열 개수

#9 DataDirectoy

  • 각 항목마다 정의된 값을 가짐

(5) - 2 / NT Header - IMAGE_OPTIONAL_HEADER64

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;
  • IMAGE_OPTIONAL_HEADER32 큰 차이를 보이지는 않음
  • 해당하는 PE파일의 비트를 통해 파악 가능

  • 현재 분석하는 메모장은 64비트 형식 사용
  • 첫 멤버인 Magic이 20B = 64비트

(6) Section Header - IMAGE_SECTION_HEADER

  • 각 섹션의 속성을 정의
  • code, data, resource 등 각각의 섹션 표현
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;

#1 주요멤버

#2 Name

  • pe 파일에서의 어떠한 명시적 규칙x
  • 참고용으로 적어놓는 것
  • 총 7개의 섹션으로 이루어져있음

'Security > Reversing' 카테고리의 다른 글

실행 압축 & UPX 패킹  (1) 2023.07.23
리버싱 / 악성코드 분석을 위한 Windows 10 Defender 비활성화  (0) 2023.07.11
Calling Convention (함수 호출 규약)  (0) 2023.07.06
스택프레임  (0) 2023.07.05
PE파일  (1) 2023.07.03