Window安全之PE結構(一)詳解DOS頭

PE文件(*.exe/ *.sys / *.dll)
PE文件是在WINDOWS平台下一個十分重要的數據結構,在本地較小此類文檔,特此記錄一下
PE32 是WINDOWS 32BIT下的PE格式
PE32+ 是WINDOWS 64BIT下的PE格式
不過64BIT與32BIT的分別只在於把原本32位的值改為64位而已

為了對齊所有段,實現內存分頁機制 PE加載器 會把 PE文件由硬盤映射到內存(要注意的是在每段之間 相距512字節(byte),4096字節(byte) = 4KB)

內存分頁機制 主要由於磁盤與內存的傳輸單位以頁為主。

PE文件到內存的映射

CPU不會直接從硬盤讀取數據, 而是從物理內存讀取數據, 因此PE文件需要映射到內存供CPU執行

WINDOWS不會一次把整個PE文件映射到內存

CPU需要讀到什麼資源,才從磁盤提交到物理內存

映射後完全相同

預處理 重定位

PE格式大致分為以下段落:



















基本名詞:

Imagebase // 表示PE文件加載後的原始地址 加載器會首先嘗試加載到0X40000000H地址
// 可以透過GetModuleHandle(LPCTSTR lp)獲取地址,該參數為路徑
VA //虛擬地址(Virtual address) 在PE文件加載後的4GB虛擬內存的任意地址
RVA //相對偏移地址(relative virtual address) 由任一虛擬地址與ImageBase 之差
//RVA = VA - ImageBase
留意:
DWORD 是四個字節(4Byte)
WORD 是兩個字節(2Byte)

//DOS頭, 不是全部定義也需要理會, 需要留意的只有兩個成員變量
typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
0   WORD   e_magic;                     // Magic number
2   WORD   e_cblp;                      // Bytes on last page of file
4   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
24   WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
26   WORD   e_oeminfo;                   // OEM information; e_oemid specific
28   WORD   e_res2[10];                  // Reserved words
3c   LONG   e_lfanew;                    // File address of new exe header             
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; 
在此IMAGE_DOS_HEADER,不是所有成員變量都重要 

WORD e_magic // "MZ" DOS頭特微碼 ,4D5Ah (h為16進制表示) , 在記憶體中代表該PE文件的DOS頭部;
WORD e_lfanew // NT_HEADER基地址的偏移量, 加載後存放在DOS頭後的0x00000003Ch位置

Address of PE_HEADER = Imagebase+e_lfanew // 由於PE文件在加載後,東西是以線性形式保存在4GB虛擬地址中,因此要把基址(ImageBase)加上偏移量 得出PE_HEADER的地址

----------------------------------------------------------------------------------------------------------
順著記憶體下去接著的結構就是 IMAGE_NT_HEADER
IMAGE_NT_HEADER STRUCT{
DWORD Signature  // "PE00" NT頭特微碼, 50 45 00 00
IMAGE_FILE_HEADER FileHeader; 
IMAGE_OPTIONAL_HEADER32 OptionalHeader ; 
}
IMAGE_FILE_HEADER{
WORD Machine; //運行平台, 目標CPU類型 X86,X64...等等
WORD NumberOfSection; //文件的區塊數目(區塊表是緊接著IMAGE_NT_HEADER後面的)
DWORD TimeDateStamp; //文件創建時間和日期
DWORD PointerToSymbolTable; //指向符向表(主要用於調試)
DWORD NumberOfSymbols;     //符號總數(同於調試)
WORD SizeOfOptionalHeader; //IMAGE_OPTIONAL_HEADER32 結構大小
WORD Characteristics; //文件屬性
}IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
typedef struct _IMAGE_OPTIONAL_HEADER32
{
+10h WORD Magic; //標志號 ROM映像 (0107H) 普通可執行文件(010BH)
+1Ah BYTE MajorLinkerVersion; //連接器版本號
+1Bh BYTE MinorLinkerVersion; //連接器次版本號
+1Ch DWORD SizeOfCode; //所有代碼的節的總大小
+20h DWORD SizeOfInitializedData; //所有已初始化的數據的節的總大小
+24h DWORD SizeOfUninitializedData; //所有未被初始化的節的大小
+28h DWORD AddressOfEntryPoint; //程序執行入口RVA
DWORD BaseOfCode;  //代碼的區塊的起始RVA
DWORD BaseOfData;  //數據的區塊的起始RVA

// NT 外加的領域

DWORD ImageBase; //程序優先的裝載基址
DWORD SectionAlignment; // 內存中的區塊的對齊大小
DWORD FileAlignment; //文件中的區塊的對齊大小

WORD MajorOperatingSystemVersion; //OS版本號
WORD MinorOperatingSystemVersion; //OS2之版本號

DWORD SizeOfImage ; //映像裝入內存後的呎寸
DWORD SizeOfHeaders; //所有頭+區塊表的呎寸大小
DWORD CheckSum; //映像的校驗和
WORD  Subsystem; //可執行文件期望的子系統
WORD DllCharacteristics; //默認為0 DLLmain函數何時被調用
DWORD SizeOfStackReserve; //初始化時的Stack大小
DWORD SizeOfStackCommit;  //初始化時實際提交的Stack大小
DWORD SizeOfHeapReserve;  //初始化時Heap大小
DWORD SizeOfHeapCommit;   //初始化時實際提交的Heap大小
DWORD LoaderFlags; //與調試有關 默認為0
DWORD NumberOfRvaAndSizes; //下邊數據目錄的項數 這個字段自win NT發布一直留為16
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];   //十分重要的一個成員變量

}IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

AddressOfEntryPoint 字段 - 這是一個RVA, 如果需要附加一個代碼 只需要修改這個入口地址 指向附加代碼 就可以改變了;
ImageBase - DLL與EXE不同 EXE每個文件都有獨立的4GB虛擬空間, 不需要重定位(Relocation) 因為EXE類的PE文件都是有獨立的空間, 但DLL就需要重定位, 因為不可能確保每

一次在4GB內的虛擬地址 沒有被其他人所佔用, 一般exe文件預設加載地址為00400000h,dll文件預設為10000000h

SectionAlignment 和 FileAlignment

IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 存放十六個重要地址的數組 當我們需要在exe檔中獲取任何信息 在這個數組裡可獲取基址
DataDirectory[0] - 導出表 (Export Address Table,EAT) //把函數導出給其他程序使用
DataDirectory[1] - 導入表 (Import Address Table,IAT) //把外面函數導入自身程序使用
DataDirectory[5] - 重定位表 (IMAGE_DIRECTORY_ENTRY_RELOCATION)

IMAGE_DATA_DIRECTORY {
 VirtualAddress DWORD ?, RVA
 iszie DWORD?, 長度
}


說了一大堆的理論,定義和機制... 還是隨手找個Firefox.exe來證實一下以後的章節也使用Firefox.exe作為例子...






幾個重點
1. 4D5AH 作為整個PE文件最先加載的內容,為MZ 即DOS頭
2. 在0x0000003C中(這是一定)存放了NT_HEADER的RVA(相對地址)
3. 結構體也是線性型式表示



Comments

Post a Comment

Popular posts from this blog

Android Kernel Development - Kernel compilation and Hello World

How does Nested-Virtualization works?

Understanding ACPI and Device Tree