Online: 0x039 (57)
haker.info  — Etyczny hacking_
Spreading knowledge like a virus.

Format plików PE/PE32+ dla pocz膮tkuj膮cych

   Dawid Farbaniec    1520 s艂贸w

1. Wprowadzenie

liki wykonywalne, kt贸rych tak cz臋sto si臋 u偶ywa w systemie Windows mog膮 by膰 nieco owiane tajemnic膮 dla os贸b niezajmuj膮cych si臋 programowaniem niskopoziomowym i in偶ynieri膮 odwrotn膮 kodu (ang. RCE). Pliki wykonywalne z rozszerzeniem *.exe, kt贸re tak cz臋sto uruchamiasz jako u偶ytkownik, a mo偶e i budujesz jako programista posiadaj膮 okre艣lony format nazywany PE (z ang. przeno艣ny wykonywalny) oraz zmodyfikowan膮 wersj臋 PE32+. Okre艣lenie Portable Executable (z ang. przeno艣ny wykonywalny) informuje, 偶e format ten jest niezale偶ny od architektury. Warto jednak wspomnie膰, 偶e 32-bitowe wersje Windows korzystaj膮 z formatu PE, natomiast wersje 64-bitowe Windows obs艂uguj膮 dodatkowo format PE32+. Format PE obejmuje nie tylko pliki wykonywalne, ale tak偶e obiektowe oraz biblioteki DLL. Jako, 偶e format PE bazuje na Unixowym formacie plik贸w COFF, to cz臋sto u偶ywa si臋 te偶 okre艣lenia PE/COFF.

1.1. Podstawowe poj臋cia

Przesuni臋cie w pliku (ang. file offset) — liczba wyznaczaj膮ca po艂o偶enie okre艣lonych danych od ustalonego miejsca w pliku. Bardzo cz臋sto miejscem startowym jest pocz膮tek pliku, kt贸ry ma offset r贸wny zero.

Adres wirtualny (ang. Virtual Address, VA) — jest to adres do danych po za艂adowaniu ich do pami臋ci. Prawie zawsze okre艣la si臋 go po prostu adresem. Mo偶na spotka膰 te偶 okre艣lenia: liniowy lub efektywny. Adres wirtualny zawiera w sobie tzw. ImageBase, czyli miejsce w pami臋ci pod kt贸re 艂adowany jest plik.

Relatywny adres wirtualny (ang. Relative Virtual Address, RVA) — ten adres mo偶na otrzyma膰 odejmuj膮c ImageBase danego modu艂u od adresu wirtualnego (VA) w tym module.

1.2. Schemat og贸lny formatu PE

Og贸lny schemat budowy pliku PE przedstawiono na rysunku 1.1. Bardziej szczeg贸艂owy opis okre艣lonych element贸w znajdziesz w dalszej cz臋艣ci wpisu.

Portable Executable File Format
Rysunek 1.1. Format pliku PE/COFF — schemat og贸lny

2. Struktura pliku PE

2.1. Nag艂贸wek MS-DOS (MS-DOS Stub)

Na samym pocz膮tku pliku PE znajduje si臋 nag艂贸wek MS-DOS. Dwa pierwsze bajty to 0x4D i 0x5A, czyli znaki ASCII "MZ". Te dwie litery to inicja艂y Marka Zbikowskiego (Mark Joseph Zbikowski) jednego z wa偶niejszych programist贸w systemu MS-DOS. Sygnatura "MZ" technicznie okre艣la, 偶e nag艂贸wek MS-DOS jest programem typu EXE dla tego podsystemu. Kod aplikacji nazywanej MS-DOS Stub zosta艂 wstawiony na samym pocz膮tku pliku PE w celach informacyjnych. Podczas pr贸by uruchomienia w systemie MS-DOS pliku PE dla systemu Windows program MS-DOS Stub wy艣wietli informacj臋 This program cannot be run in DOS mode. Istnieje mo偶liwo艣膰 zmiany programu MS-DOS Stub za pomoc膮 opcji konsolidatora (ang. linker).

Nag艂贸wek MS-DOS w postaci struktury mo偶na znale藕膰 w pliku winnt.h, kt贸ry zawiera 艣rodowisko Visual Studio (Visual C++).

Sygnatura MZ oraz nag艂贸wek MS-DOS (Visual C++, winnt.h):

#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number WORD e_cblp; // Bytes 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; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial 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;

Warto zaznaczy膰, 偶e w polu e_lfanew (adres w pliku: 0x3C) znajduje si臋 warto艣膰 przesuni臋cia (ang. offset), kt贸ra okre艣la gdzie znajduje si臋 sygnatura "PE", czyli pocz膮tek nag艂贸wka PE.

Na rysunku 2.1 przedstawiono przyk艂adowy plik PE otwarty w edytorze szesnastkowym XVI32. Pokolorowane obszary oznaczaj膮 poszczeg贸lne elementy pliku PE. Na rysunku 2.1 zmie艣ci艂 si臋 nag艂贸wek MS-DOS, program MS-DOS Stub oraz pocz膮tek nag艂贸wka PE.

MS-DOS Header
Rysunek 2.1. Przyk艂adowy plik PE w hex edytorze z obja艣nieniem jego struktury

2.2. Sygnatura PE (PE Signature)

Sygnatura PE (warto艣ci: 0x50 i 0x45, czyli znaki ASCII "PE") okre艣la pocz膮tek nag艂贸wka PE. W pliku winnt.h sygnatur臋 definiuje sta艂a:

#define IMAGE_NT_SIGNATURE 0x00004550 // PE00

2.3. Nag艂贸wek COFF (COFF Header)

Po sygnaturze PE znajduje si臋 nag艂贸wek COFF, kt贸ry posiada nast臋puj膮ce pola:

Nazwa pola Przesuni臋cie (ang. offset) Rozmiar Opis
Machine 0 2 Numer okre艣laj膮cy typ maszyny docelowej. Dla architektury x64 b臋dzie to sta艂a IMAGE_FILE_MACHINE_AMD64 (warto艣膰: 0x8664). Szczeg贸艂y w specyfikacji.
NumberOfSections 2 2 Ilo艣膰 sekcji w pliku PE.
TimeDateStamp 4 4 Znacznik czasowy okre艣laj膮cy kiedy plik zosta艂 utworzony (typ warto艣ci time_t z j臋zyka C).
PointerToSymbolTable 8 4 Adres w pliku do tablicy symboli debugowania. Dla plik贸w wykonywalnych powinno by膰 tu zero.
NumberOfSymbols 12 4 Ilo艣膰 wpis贸w w tablicy symboli debugowania. Dla plik贸w wykonywalnych powinno by膰 tu zero.
SizeOfOptionalHeader 16 2 Rozmiar opcjonalnego nag艂贸wka PE (Optional Header). Wymagane tylko dla plik贸w wykonywalnych.
Characteristics 18 2 Flagi okre艣laj膮ce atrybuty pliku. Szczeg贸艂y w specyfikacji.

Definicja nag艂贸wka COFF w pliku winnt.h prezentuje si臋 nast臋puj膮co:

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;

2.4. Opcjonalny nag艂贸wek PE (PE Optional Header)

Kolejnym elementem, kt贸ry mo偶na wyr贸偶ni膰 w formacie PE jest nag艂贸wek opcjonalny. Jego nazwa informuje, 偶e nie wyst臋puje on zawsze. Pliki wykonywalne musz膮 go posiada膰, natomiast pliki obiektowe nie.

Nag艂贸wek ten r贸偶ni si臋 dla 32-bitowych i 64-bitowych plik贸w wykonywalnych. Struktura IMAGE_OPTIONAL_HEADER32 odpowiada plikom PE (32-bit), natomiast struktura IMAGE_OPTIONAL_HEADER64 jest dla plik贸w PE32+ (64-bit).

Definicja nag艂贸wka opcjonalnego dla plik贸w PE32+ w nag艂贸wku winnt.h prezentuje si臋 nast臋puj膮co:

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;

Warto zaznaczy膰, 偶e sygnatura PE, nag艂贸wek COFF oraz nag艂贸wek opcjonalny zosta艂y obj臋te w struktury o nazwach _IMAGE_NT_HEADERS oraz _IMAGE_NT_HEADERS64. Definicja struktury _IMAGE_NT_HEADERS z pliku winnt.h prezentuje si臋 nast臋puj膮co:

typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

Szczeg贸艂y i opis p贸l nag艂贸wka opcjonalnego w specyfikacji.

2.5. Nag艂贸wki sekcji (Section Headers)

Nag艂贸wki sekcji znajduj膮 si臋 zaraz po nag艂贸wku opcjonalnym. Zawieraj膮 one informacje o sekcjach, jakie zawiera plik PE. W celu otrzymania adresu do nag艂贸wk贸w sekcji nale偶y obliczy膰 przesuni臋cie sumuj膮c rozmiar nag艂贸wk贸w wyst臋puj膮cych wcze艣niej.

Rozmiar nag艂贸wka sekcji to 40 bajt贸w (#define IMAGE_SIZEOF_SECTION_HEADER 40).

Ka偶dy nag艂贸wek sekcji posiada nast臋puj膮ce pola:

Nazwa pola Przesuni臋cie (ang. offset) Rozmiar Opis
Name 0 8 Nazwa sekcji jako napis UTF-8 o maksymalnej d艂ugo艣ci 8 bajt贸w (dla plik贸w wykonywalnych). W przypadku kr贸tszej nazwy puste miejsce jest wype艂niane zerami.
VirtualSize 8 4 Rozmiar wirtualny sekcji, czyli po za艂adowaniu do pami臋ci.
VirtualAddress 12 4 Dla plik贸w wykonywalnych jest to adres pocz膮tku sekcji relatywnie do adresu ImageBase (RVA).
SizeOfRawData 16 4 Dla plik贸w wykonywalnych jest to rozmiar sekcji w pliku na dysku.
PointerToRawData 20 4 Adres w pliku do danych sekcji.
PointerToRelocations 24 4 Dla plik贸w wykonywalnych tutaj zero.
PointerToLinenumbers 28 4 Dla plik贸w wykonywalnych tutaj zero.
NumberOfRelocations 32 2 Dla plik贸w wykonywalnych tutaj zero.
NumberOfLinenumbers 34 2 Dla plik贸w wykonywalnych tutaj zero.
Characteristics 36 4 Flagi okre艣laj膮ce atrybuty sekcji. Szczeg贸艂y w specyfikacji.

Definicja nag艂贸wka sekcji w pliku winnt.h prezentuje si臋 nast臋puj膮co:

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;

2.6. Dane sekcji (Section Data)

Dane sekcji znajduj膮 si臋 po nag艂贸wkach sekcji. Adres do danych okre艣lonej sekcji mo偶na uzyska膰 poprzez odczytanie warto艣ci pola PointerToRawData z nag艂贸wka sekcji. Rozmiar sekcji okre艣la natomiast warto艣膰 pola SizeOfRawData.

2.6.1. Szczeg贸lne typy sekcji

Warte uwagi s膮 zarezerwowane sekcje specjalne dotycz膮ce plik贸w wykonywalnych.

Nazwa sekcji Opis
.bss Dane niezainicjalizowane w wolnym formacie.
.data Dane zainicjalizowane w wolnym formacie.
.edata Tabele eksportu. Zawieraj膮 symbole, kt贸re pozwalaj膮 innym plikom wykonywalnym korzysta膰 z funkcji, kt贸re s膮 eksportowane przez ten modu艂.
.idata Tabele importu. Zawieraj膮 symbole importowanych funkcji przez plik wykonywalny.
.rdata Dane zainicjalizowane tylko do odczytu.
.text Kod do wykonania w wolnym formacie.
... Szczeg贸艂y w specyfikacji

3. Troch臋 kodu dla przyk艂adu

Na listingu 3.1 poni偶ej przedstawiono program, kt贸ry czyta okre艣lony plik PE, a nast臋pnie odczytuje warto艣膰 pola e_magic z nag艂贸wka MS-DOS.

Listing 3.1. Odczytanie warto艣ci pola e_magic z nag艂贸wka MS-DOS pliku PE (Visual C++)
#include <Windows.h> #include <strsafe.h> int wmain(int argc, TCHAR* argv[]) { HANDLE hFile = NULL; DWORD dwSize = NULL; DWORD dwRead = NULL; LPVOID fileBytes = NULL; PIMAGE_DOS_HEADER dosHeader = { }; TCHAR infoText[64] = TEXT(""); // otw贸rz plik PE hFile = CreateFile(TEXT("D:\\masm64\\prog1.exe"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return EXIT_FAILURE; // pobierz rozmiar pliku i zaalokuj pami臋膰 dwSize = GetFileSize(hFile, NULL); fileBytes = new BYTE[dwSize]; // czytaj plik if (ReadFile(hFile, fileBytes, dwSize, &dwRead, NULL) == FALSE) return EXIT_FAILURE; // odczytaj nag艂贸wek MS-DOS z pliku PE dosHeader = (PIMAGE_DOS_HEADER)fileBytes; // wklej magiczn膮 liczb臋 do napisu StringCchPrintf(infoText, 64, TEXT("dosHeader->e_magic = 0x%x"), dosHeader->e_magic); // wy艣wietl komunikat z warto艣ci膮 pola e_magic MessageBox(0, infoText, TEXT("haker.info"), 0); return EXIT_SUCCESS; }

Dzia艂anie programu z listingu 3.1 przedstawiono na rysunku 3.1.

Odczytanie nag艂贸wka pliku PE
Rysunek 3.1. Program odczytuje i wy艣wietla magiczn膮 liczb臋 z nag艂贸wka pliku PE (Visual C++)

4. R贸偶nice pomi臋dzy PE, a PE32+

R贸偶nice w strukturze IMAGE_OPTIONAL_HEADER32, a IMAGE_OPTIONAL_HEADER64 przedstawiono w tabeli poni偶ej.

Nazwa pola Format PE Format PE32+
BaseOfData ULONG Usuni臋to
ImageBase ULONG ULONGLONG
SizeOfStackReserve ULONG ULONGLONG
SizeOfStackCommit ULONG ULONGLONG
SizeOfHeapReserve ULONG ULONGLONG
SizeOfHeapCommit ULONG ULONGLONG

5. Zako艅czenie

Mam nadziej臋, 偶e artyku艂 ten przybli偶y艂 pocz膮tkuj膮cym format PE/COFF. Nie chcia艂em tego wprowadzenia za bardzo obci膮偶a膰 du偶膮 ilo艣ci膮 szczeg贸艂owych opis贸w. Z tego powodu jako kolejny krok polecam dokument Microsoft Portable Executable and Common Object File Format Specification firmy Microsoft.
Dzi臋kuj臋 za przeczytanie!

Dawid Farbaniec

Wykaz literatury (bibliografia)

  • Microsoft Corporation, 2019 鈥 https://docs.microsoft.com/en-us/windows/win32/debug/pe-format (dost臋p: 28-07-2020)

Tagi:  reverse-engineering  pe-file-format 

Komentarze czytaj膮cych

Czytelnik napisa艂:
Polecisz jaki艣 edytor do pracy z plikami PE?
 rok temu (14 lipca 2019 godz. 17:18)
Dawid (haker.info) napisa艂:
Znane mi narz臋dzia to PEview i CFF Explorer. Ostatnio znalaz艂em PE Internals.
 rok temu (15 lipca 2019 godz. 14:38)



Wszystkie tre艣ci umieszczone na tej witrynie s膮 chronione prawem autorskim. Surowo zabronione jest kopiowanie i rozpowszechnianie zawarto艣ci tej witryny bez zgody autora. Wszelkie opublikowane tutaj tre艣ci (w tym kody 藕r贸d艂owe i inne) s艂u偶膮 wy艂膮cznie celom informacyjnym oraz edukacyjnym. W艂a艣ciciele tej witryny nie ponosz膮 odpowiedzialno艣ci za ewentualne niezgodne z prawem wykorzystanie zasob贸w dost臋pnych w witrynie. U偶ytkownik tej witryny o艣wiadcza, 偶e z zamieszczonych tutaj danych korzysta na w艂asn膮 odpowiedzialno艣膰. Wszelkie znaki towarowe i nazwy zastrze偶one zosta艂y u偶yte jedynie w celach informacyjnych i nale偶膮 wy艂膮cznie do ich prawnych w艂a艣cicieli. Korzystaj膮c z zasob贸w witryny haker.info o艣wiadczasz, 偶e akceptujesz powy偶sze warunki oraz polityk臋 prywatno艣ci.