亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 編程設計 > 正文

PE文件結構詳解

2020-02-02 19:00:25
字體:
來源:轉載
供稿:網友

1、PE文件的結構

1、什么是可執行文件?

可執行文件 (executable file) 指的是可以由操作系統進行加載執行的文件。

可執行文件的格式:

    - Windows平臺:PE(Portable Executable)文件結構

    - Linux平臺:ELF(Executable and Linking Format)文件結構

PE和ELF非常相似,它們都是源于同一種可執行文件格式 COFF

    - COFF 是由Unix System V Release 3首先提出并且使用的格式規范,

    - 微軟基于COFF格式,制定了PE格式標準,并將其用于當時的Windows NT系統

    - System V Release 4在COFF的基礎上引入了ELF格式。 

事實上,在Windows平臺,VISUAL C++編譯器產生的目標文件仍然使用COFF格式,而可執行文件為PE格式

微軟對64位Windows平臺上的PE文件結構叫做PE32+,就是把那些原來32位的字段變成了64位。

2、PE文件的特征

識別一個文件是不是PE文件不應該只看文件后綴名,還應該通過PE指紋

使用UE打開一個exe文件,發現文件的頭兩個字節都是MZ,0x3C位置保存著一個地址,查該地址處發現保存著“PE”,這樣基本可以認定改文件是一個PE文件

通過這些重要的信息(“MZ”和“PE”)驗證文件是否為PE文件,這些信息即PE指紋。

3、PE文件的整體結構

這里將一個PE文件的主要部分列為4部分,這里可以先有模糊概念,后面會詳細解釋

“節”或“塊”或”區塊“都是一個意思,后文會穿插使用

下面從二進制層面整體把握其結構,看看一個PE文件的組成

4、PE文件到內存的映射

PE文件存儲在磁盤時的結構和加載到內存后的結構有所不同。

當PE文件通過Windows加載器載入內存后,內存中的版本稱為模塊(Module)。

映射文件的起始地址稱為模塊句柄(hModule),也稱為基地址(ImageBase)。

(模塊句柄是不是和其他句柄不太一樣呢?)

文件數據一般512字節(1扇區)對齊(現也多4k),32位內存一般4k(1頁)對齊,512D = 200H,4096D = 1000H

文件中塊的大小為200H的整數倍,內存中塊的大小為1000H的整數倍,映射后實際數據的大小不變,多余部分可用0填充

PE文件頭部(DOS頭+PE頭)到塊表之間沒有間隙,然而他們卻和塊之間有間隙,大小取決于對齊參數

VC編譯器默認編譯時,exe文件基地址是0x400000,DLL文件基地址是0x10000000

VA:虛擬內存地址

RVA:相對虛擬地址即相對于基地址的偏移地址

FOA: 文件偏移地址

5、DOS部分

DOS MZ文件頭實際是一個結構體(IMAGE_DOS_HEADER),占64字節

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;

  DOS頭用于16位系統中,在32位系統中DOS頭成為冗余數據,但還存在兩個重要成員e_magic字段(偏移 0x0)和                            e_lfanew字段(偏移 0x3C)

e_magic保存“MZ”字符,e_lfanew保存PE文件頭地址,通過這個地址找到PE文件頭,得到PE文件標識“PE”。

e_magic和e_lfanew是驗證PE指紋的重要字段,其他字段現基本不使用(可填充任意數據)

“DOS Stub”區域的數據由鏈接器填充(可自己填充如意數據),是一段可以在DOS下運行的一小段代碼,

這段代碼的唯一作用是向終端輸出一行字:“This program cannot be run in DOS”(“e_cs”和“e_ip”指向)

然后退出程序,表示該程序不能在DOS下運行。 

6、PE文件頭(PE Header)

PE文件頭是一個結構體(IMAGE_NT_HEADERS32),里面還包含兩個其它結構體,占用4B + 20B + 224B 

typedef struct _IMAGE_NT_HEADERS {  DWORD Signature;             // PE文件標識 4Bytes  IMAGE_FILE_HEADER FileHeader;      // 40 Bytes  IMAGE_OPTIONAL_HEADER32 OptionalHeader; // 224 Bytes  PE32可執行文件,不討論PE32+的情況} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

Signature字段設置為0x00004550,ANCII碼字符是“PE00”,標識PE文件頭的開始,PE標識不能破壞。

1、IMAGE_FILE_HEADER結構體

IMAGE_FILE_HEADER(映像文件頭或標準PE頭)結構包含PE文件的一些基本信息,該結構在微軟的官方文檔中被稱為標準通用對象文件格式(Common Object File Format,COFF)頭

typedef struct _IMAGE_FILE_HEADER {  WORD  Machine;        // 可運行在什么樣的CPU上。0代表任意,Intel 386及后續:0x014C, x64: 0x8664  WORD  NumberOfSections;   // 文件的區塊(節)數  DWORD  TimeDateStamp;     // 文件的創建時間。1970年1月1日以GMT計算的秒數,編譯器填充的,不重要的值  DWORD  PointerToSymbolTable; // 指向符號表(用于調試)  DWORD  NumberOfSymbols;    // 符號表中符號的個數(用于調試)  WORD  SizeOfOptionalHeader; // IMAGE_OPTIONAL_HEADER32結構的大小,可改變,32位為E0,64位為F0  WORD  Characteristics;    // 文件屬性} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

 重要字段:NumberOfSections,SizeOfOptionalHeader

對應結構為下圖紫線部分

0x014C說明運行于x86 CPU;0x0007說明當前exe有7個節;

0x00E0說明IMAGE_OPTIONAL_HEADER32為224字節;

0x030F(0000 0011 0000 1111)代表文件屬性 ,由下列對應位為1的組合

2、IMAGE_OPTIONAL_HEADER結構體

IMAGE_OPTIONAL_HEADER(可選映像頭或擴展PE頭)是一個可選的結構,是IMAGE_FILE_HEADER結構的擴展

大小由IMAGE_FILE_HEADER結構的SizeOfOptionalHeader字段記錄(可能不準確)

typedef struct _IMAGE_OPTIONAL_HEADER {  //  // Standard fields.  //    WORD  Magic;         //說明文件的類型 PE32:10BH PE32+:20BH  Rom映像文件:107H  BYTE  MajorLinkerVersion;   //鏈接器主版本號  BYTE  MinorLinkerVersion;   //鏈接器次版本號  DWORD  SizeOfCode;       //所有代碼節的總和(基于文件對齊) 編譯器填的 沒用  DWORD  SizeOfInitializedData; //包含所有已經初始化數據的節的總大小 編譯器填的 沒用  DWORD  SizeOfUninitializedData;//包含未初始化數據的節的總大小 編譯器填的 沒用   DWORD  AddressOfEntryPoint;  //程序入口RVA  在大多數可執行文件中,這個地址不直接指向Main、WinMain或DIMain函數,而指向運行時的庫代碼并由它來調用上述函數  DWORD  BaseOfCode;       //代碼起始RVA,編譯器填的  沒用  DWORD  BaseOfData;       //數據段起始RVA,編譯器填的  沒用   //  // NT additional fields.  //   DWORD  ImageBase;       //內存鏡像基址 ,可鏈接時自己設置  DWORD  SectionAlignment;    //內存對齊   一般一頁大小4k  DWORD  FileAlignment;     //文件對齊   一般一扇區大小512字節,現在也多4k  WORD  MajorOperatingSystemVersion; //標識操作系統版本號 主版本號  WORD  MinorOperatingSystemVersion; //標識操作系統版本號 次版本號  WORD  MajorImageVersion;   //PE文件自身的主版本號   WORD  MinorImageVersion;   //PE文件自身的次版本號   WORD  MajorSubsystemVersion; //運行所需子系統主版本號  WORD  MinorSubsystemVersion; //運行所需子系統次版本號  DWORD  Win32VersionValue;   //子系統版本的值,必須為0  DWORD  SizeOfImage; //內存中整個PE文件的映射的尺寸,可比實際的值大,必須是SectionAlignment的整數倍    DWORD  SizeOfHeaders;     //所有頭+節表按照文件對齊后的大小,否則加載會出錯  DWORD  CheckSum;        //校驗和,一些系統文件有要求.用來判斷文件是否被修改   WORD  Subsystem;       //子系統	驅動程序(1) 圖形界面(2) 控制臺、DLL(3)  WORD  DllCharacteristics;   //文件特性 不是針對DLL文件的  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;

 重要字段:

AddressOfEntryPoint:程序入口地址(RVA),下圖為32C40H

ImageBase:內存鏡像基地址,下圖為400000H

FileAlignment:文件對齊,下圖為200H

SectionAlignment:內存對齊,下圖為1000H

DataDirectory[16]:數據目錄表,由數個相同的IMAGE_DATA_DIRECTORY結構組成,

                                指向輸出表、輸入表、資源塊,重定位表等(后面詳解這里先跳過)

typedef struct _IMAGE_DATA_DIRECTORY {  DWORD  VirtualAddress;  //對應表的起始RVA  DWORD  Size;       //對應表長度} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

 

ImageBase + AddressOfEntryPoint = 程序實際運行入口地址(實際加載地址等于ImageBase)

0x400000 + 0x32C40 = 0x432C40 (使用OD運行程序發現就是從這個地址開始運行)

應用:在PE文件空白區添加代碼,讓程序執行先執行添加的代碼再跳轉程序入口

思路:

① 在PE的空白區構造一段代碼(call  -> E8)

② 修改入口地址為新增代碼(IMAGE_OPTIONAL_HEADER.AddressOfEntryPoint)

③ 新增代碼執行后,跳回入口地址(jmp   -> E9)

 7、塊表

塊表是一個IMAGE_SECTION_HEADER的結構數組,每個IMAGE_SECTION_HEADER結構40字節。

每個IMAGE_SECTION_HEADER結構包含了它所關聯的區塊的信息,例如位置、長度、屬性。

#define IMAGE_SIZEOF_SHORT_NAME       8 typedef struct _IMAGE_SECTION_HEADER {  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME]; //塊名。多數塊名以一個“.”開始(例如.text),這個“.”不是必需的  union {      DWORD  PhysicalAddress; //常用第二個字段      DWORD  VirtualSize;   //加載到內存實際區塊的大?。▽R前),為什么會變呢?可能是有時未初始化的全局變量不放bss段而是通過擴展這里  } Misc;  DWORD  VirtualAddress;  //該塊裝載到內存中的RVA(內存對齊后,數值總是SectionAlignment的整數倍)  DWORD  SizeOfRawData;  //該塊在文件中所占的空間(文件對齊后),VirtualSize的值可能會比SizeOfRawData大 例如bss節(SizeOfRawData為0),data節(關鍵看未初始化的變量放哪)  DWORD  PointerToRawData; //該塊在文件中的偏移(FOA)   /*調試相關,忽略*/  DWORD  PointerToRelocations; //在“.obj”文件中使用,指向重定位表的指針  DWORD  PointerToLinenumbers;  WORD  NumberOfRelocations;  //重定位表的個數(在OBJ文件中使用)。  WORD  NumberOfLinenumbers;   DWORD  Characteristics; //塊的屬性 該字段是一組指出塊屬性(例如代碼/數據、可讀/可寫等)的標志} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

重要字段:Name[8],VirtualSize,VirtualAddress,SizeOfRawData,PointerToRawData,Characteristics

IMAGE_FILE_HEADER的NumberOfSections字段是不是記錄著當前文件的節數呢?

31C80H代表載入內存代碼塊對齊前大??;1000H代表代碼塊裝載到內存RVA1000H;

31E00H代表文件對齊后代碼塊大?。?00H代表代碼塊在文件中的偏移

60000020H代表代碼塊屬性(‭0110 0000 0000 0000 0000 0000 0010 0000‬)查下表得到屬性為可讀可執行的代碼

更多屬性參考:https://docs.microsoft.com/zh-cn/windows/win32/api/winnt/ns-winnt-image_section_header

8、RVA與FOA的轉換

RVA:相對虛擬地址,FOA:文件偏移地址。

計算步驟:

① 計算RVA = 虛擬內存地址 - ImageBase

② 若RVA是否位于PE頭:FOA == RVA

③ 判斷RVA位于哪個節:

  RVA >= 節.VirtualAddress (節在內存對齊后RVA )

  RVA <= 節.VirtualAddress + 當前節內存對齊后的大小

  偏移量 = RVA - 節.VirtualAddress;

④ FOA = 節.PointerToRawData + 偏移量;

應用舉例:

有初始值的全局變量初始值會存儲在PE文件中,想要修改文件中全局變量的數據值即

需要找到文件中存儲全局變量值的地方,然后修改即可 

2、輸出表和輸入表

可選PE頭(擴展PE頭)的最后一個字段DataDirectory[16]代表數據目錄表,由16個相同的IMAGE_DATA_DIRECTORY結構組成,成員分別指向輸出表、輸入表、資源塊等

typedef struct _IMAGE_DATA_DIRECTORY {  DWORD  VirtualAddress;  //對應表的起始RVA  DWORD  Size;       //對應表大?。ò颖恚﹠ IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

1、輸出表(導出表)
創建一個DLL時,實際上創建了一組能讓EXE或其他DLL調用的函數

DLL文件通過輸出表(Export Table)向系統提供輸出函數名、序號和入口地址等信息。

數據目錄表的第1個成員指向輸出表。 

找到文件中的輸出表(以DllDemo.dll為例,看圖就行)

成功找到輸出表在文件偏移0C00H處,如下:

特別說明:① 如果文件對齊與內存對齊都是4k則不需要地址轉換 ② 輸出表大小是指輸出表大小與其子表大小和

輸出表實際是一個40字節的結構體(IMAGE_EXPORT_DIRECTORY),輸出表的結構如下

typedef struct _IMAGE_EXPORT_DIRECTORY {  DWORD  Characteristics; //未定義,總是為0。  DWORD  TimeDateStamp; //輸出表創建的時間(GMT時間)  WORD  MajorVersion;  //輸出表的主版本號。未使用,設置為0。  WORD  MinorVersion;  //輸出表的次版本號。未使用,設置為0。   DWORD  Name; //指向一個ASCII字符串的RVA。這個字符串是與這些輸出函數相關聯的DLL的名字(例如"KERNEL32.DLL")   DWORD  Base; //導出函數起始序號(基數)。當通過序數來查詢一個輸出函數時,這個值從序數里被減去,其結果將作為進入輸出地址表(EAT)的索引   DWORD  NumberOfFunctions; //輸出函數地址表(Export Address Table,EAT)中的條目數量(最大序號 - 最小序號)   DWORD  NumberOfNames;   //輸出函數名稱表(Export Names Table,ENT)里的條目數量   DWORD  AddressOfFunctions;   // EAT的RVA(輸出函數地址表RVA)  DWORD  AddressOfNames;     // ENT的RVA(輸出函數名稱表RVA),每一個表成員指向ANCII字符串 表成員的排列順序取決于字符串的排序   DWORD  AddressOfNameOrdinals; // 輸出函數序號表RVA,每個表成員2字節} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

重要字段: Name,Base,NumberOfNames,AddressOfFunctions,AddressOfNames,AddressOfNameOrdinals

過程分析:

//功能:加載動態鏈接庫到內存
HMODULE WINAPI LoadLibrary(
LPCTSTR lpFileName //模塊的文件名
);

/*功能:檢索指定的動態鏈接庫(DLL)中的輸出庫函數地址*/
FARPROC GetProcAddress(
HMODULE hModule, // DLL模塊句柄 (模塊基地址)
LPCSTR lpProcName // 函數名 或者 指定函數的序數值
);

PE裝載器調用GetProcAddress來查找DlIDemo.DLL里的API函數MsgBox,

系統通過定位DlIDemo.DLL的輸出表(IMAGE_EXPORT_DIRECTORY)結構獲得輸出函數名稱表(ENT)的起始地址,

對名字進行二進制查找,直到發現字符串“MsgBox”為止,PE裝載器發現MsgBox是數組的第1個條目后,加載器從輸出序數表

中讀取相應的第1個值,這個值是MsgBox的在函數地址表(EAT)的索引。使用索引在EAT取值得到MsgBox的RVA1008h。

用1008h加DllDemo.DLL的載入地址,得到MsgBox的實際地址。

特別說明:如果lpProcName 是序號,則需要通過字段Base確定起始序號,序號 - Base的差值作為索引得到函數RVA地址(注意這里的序號和索引)

注意:輸出序號表存放的是索引值而不是序號,真正的序號是Base+索引值

例如:寫一個簡單加法函數(int add(int a, int b)),創建一個A.dll

//def文件

EXPORTS

add @12

分析A.dll的導出表

當時用序號(12)獲得函數地址時會拿12-Base = 0作為輸出函數地址表的索引值

使用A.dll

#include <iostream>#include <windows.h> using namespace std; typedef int(*lpAdd)(int, int); lpAdd myAdd; int main(){	//動態加載dll到內存中	HINSTANCE  hModule = LoadLibrary("A.dll"); 	cout << "ImageBase: " << hModule << endl; 	//通過函數名獲取函數地址	myAdd = (lpAdd)GetProcAddress(hModule, "add"); 	cout << "myAdd(10, 20) = " << myAdd(10, 20) << endl; 	//通過序號獲取函數地址	myAdd = (lpAdd)GetProcAddress(hModule, (char*)0x0C); 	cout << "myAdd(10, 20) = " << myAdd(10, 20) << endl; 	FreeLibrary(hModule); 	return 0;}

2、輸入表(導入表)

PE 文件映射到內存后,Windows 將相應的 DLL文件裝入,EXE 文件通過“輸入表”找到相應的 DLL 中的導入函數,從而完成程序的正常運行

數據目錄表的第2個成員指向輸入表。當前文件依賴幾個模塊就會有幾張輸入表且是連續排放的。

如何找到輸入表?

上圖看出當前文件只依賴一個模塊,只有一張導入表,如果有多個會連續存放直到連續出現20個0說明結束。

輸入表實際是個20字節的結構體 IMAGE_IMPORT_DESCRIPTOR

typedef struct _IMAGE_IMPORT_DESCRIPTOR {  union {    DWORD  Characteristics;      // 0 for terminating null import descriptor    DWORD  OriginalFirstThunk;     // RVA to original unbound IAT (PIMAGE_THUNK_DATA)  } DUMMYUNIONNAME;  DWORD  TimeDateStamp;         // 0 if not bound,                      // -1 if bound, and real date/time stamp                      //   in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)                      // O.W. date/time stamp of DLL bound to (Old BIND)   DWORD  ForwarderChain;         // -1 if no forwarders  DWORD  Name;  DWORD  FirstThunk;           // RVA to IAT (if bound this IAT has actual addresses)} IMAGE_IMPORT_DESCRIPTOR;

重要字段:

Name:DLL(依賴模塊)名字的指針。是一個以“00”結尾的ASCII字符的RVA地址。

OriginalFirstThunk:包含指向輸入名稱表(INT)的RVA。

      INT是一個IMAGE_THUNK_DATA結構的數組,數組中的每個IMAGE_THUNK_DATA結構都指向

      IMAGE_IMPORT_BY_NAME結構,數組以一個內容為0的IMAGE_THUNK_DATA結構結束。

FirstThunk:包含指向輸入地址表(IAT)的RVA。IAT是一個IMAGE_THUNK_DATA結構的數組。 

IMAGE_THUNK_DATA結構實際只占4字節

typedef struct _IMAGE_THUNK_DATA32 {  union {    DWORD ForwarderString;   // 指向一個轉向者字符串的RVA    DWORD Function;       // 被輸入的函數的內存地址    DWORD Ordinal;       // 被輸入的API的序數    DWORD AddressOfData;    // 指向IMAGE_IMPORT BY NAME  } u1;} IMAGE_THUNK_DATA32;

如果IMAGE_THUNK_DATA32的最高位為1,則低31位代表函數的導出序號,

否則4個字節是一個RVA,指向IMAGE_IMPORT_BY_NAME結構

IMAGE_IMPORT_BY_NAME結構字面僅有4個字節,存儲了一個輸入函數的相關信息

typedef struct _IMAGE_IMPORT_BY_NAME {  WORD  Hint;  // 輸出函數地址表的索引(不是導出序號),(究竟是啥沒試驗,因為看的很多資料說是序號),不必須,鏈接器可能將其置0  CHAR  Name[1]; // 函數名字字符串,以“/0”作為字符串結束標志,大小不確定} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

由上圖,我們是不是通過導入表能夠很輕松獲得當前文件依賴模塊的名字和函數名?

這里INT和IAT完全內容一致,為什么呢?稍后解釋

INT和IAT內容一致其實是PE文件未加載時的狀態,

PE加載器將文件載入內存后會向IAT填入真正的函數地址(GetProcAddress)

例如:

3、重定位表

如果PE文件不在首選的地址(ImageBase)載入,那么文件中的每一個絕對地址都需要被修正。

需要修正的地址有很多,可以在文件中使用重定位表記錄這些絕對地址的位置,在載入內存后若載入基地址與ImageBase不同再進行修正,若相同就不需要修正這些地址。

數據目錄項的第6個結構,指向重定位表(Relocation Table)

重定位表由一個個的重定位塊組成,每個塊記錄了4KB(一頁)的內存中需要重定位的地址。

每個重定位數據塊的大小必須以DWORD(4字節)對齊。它們以一個IMAGE_BASE_RELOCATION結構開始,格式如下

typedef struct _IMAGE_BASE_RELOCATION {  DWORD  VirtualAddress; //記錄內存頁的基址RVA   DWORD  SizeOfBlock;  //當前重定位塊結構的大小。這個值減8就是TypeOffset數組的大小    /*下面字段可加與不加*/  /*數組每項大小為2字節。代表頁內偏移,16位分為高4位和低12位。高4位代表重定位類型;   低12位是重定位地址(12位就可以尋址4k),與VitualAddress相加就是一個完整RVA   */  //WORD  TypeOffset[1]; } IMAGE_BASE_RELOCATION;typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

這些字段可能直接不好理解在后面會看一個實例一切就徹底明白了

雖然有多種重定位類型,但對x86可執行文件來說,所有的基址重定位類型都是IMAGE_REL_BASED_HIGHLOW。

在一組重定位結束的地方會出現一個類型IMAGE_REL_BASED_ABSOLUTE的重定位,這些重定位什么都不做,只用于填充,以便下一個MAGE_BASE_RELOCATION按4字節分界線對齊。

對于IA-64可執行文件,重定位類型似乎總是IMAGE_REL_BASED_DIR64。

有趣的是,盡管IA-64的EXE頁大小是8KB,但基址重定位仍是4KB的塊

所有重定位塊以一個VitualAddress字段為0的MAGE_BASE_RELOCATION結構結束。

//// Based relocation types.// #define IMAGE_REL_BASED_ABSOLUTE       0  // 沒有具體含義,只是為了讓每個段4字節對齊#define IMAGE_REL_BASED_HIGH         1#define IMAGE_REL_BASED_LOW          2#define IMAGE_REL_BASED_HIGHLOW        3  // 重定位指向的整個地址都需要修正,實際上大部分情況下都是這樣的#define IMAGE_REL_BASED_HIGHADJ        4#define IMAGE_REL_BASED_MACHINE_SPECIFIC_5  5#define IMAGE_REL_BASED_RESERVED       6#define IMAGE_REL_BASED_MACHINE_SPECIFIC_7  7#define IMAGE_REL_BASED_MACHINE_SPECIFIC_8  8#define IMAGE_REL_BASED_MACHINE_SPECIFIC_9  9#define IMAGE_REL_BASED_DIR64         10  // 出現在64位PE文件中,對指向的整個地址進行修正

示例分析:

繼續以DllDemo.dll為例

先用工具定位重定位表在文件的位置如下

 查看重定位表信息如下

->Relocation Directory  1. Relocation Block:  VirtualAddress: 0x00001000 ("CODE")  SizeOfBlock:   0x00000010 (0x0004 block entries)   RVA    Type  ---------- -----------------  0x0000100F HIGHLOW  0x00001023 HIGHLOW  n/a    ABSOLUTE  n/a    ABSOLUTE――――――――――――――――

下面實際分析

根據下面判斷出當前RVA在CODE節

所以

100Fh(RVA) → 60Fh(FOA)

1023h(RVA) → 623h(FOA)

60Fh和623h分別指向00402000h和00403030h處,即為所需要重定位的數據

執行PE文件前,加載程序在進行重定位的時候,會用PE文件在內存中的實際映像地址減PE文件所要求的映像地址,根據重定位類型的不同將差值添加到相應的地址數據中。

可以看到重定位表扮演的角色:文件加載到內存后,通過重定位表記錄的RVA找到需要重定位的數據

重定位表通過頁基址RVA+頁內偏移地址方式得到一個完整RVA大大縮小了表大小。

4、資源

Windows程序的各種界面稱為資源,包括加速鍵(Accelerator)、位圖(Bitmap)、光標(Cursor)、對話框(Dialog Box)、圖標(Icon)、菜單(Menu)、串表(String Table)、工具欄(Toolbar)和版本信息(Version Information)等。

定義資源時,既可以使用字符串作為名稱來標識一個資源,也可以通過ID號來標識資源

資源分類

- 標準資源類型

- 非標準資源類型

若資源類型的高位如果為1,說明對應的資源類別是一個非標準的新類型


數據目錄項的第3個結構,指向資源表,不直接指向資源數據,而是以磁盤目錄形式定位資源數據

資源表是一個四層的二叉排序樹結構。

每一個節點都是由資源目錄結構和緊隨其后的數個資源目錄項結構組成的,

兩種結構組成了一個資源目錄結構單元(目錄塊)

資源目錄結構(IMAGE_RESOURCE_DIRECTORY)占16字節,其定義如下

typedef struct _IMAGE_RESOURCE_DIRECTORY {  DWORD  Characteristics;  //理論上是資源的屬性標志,但是通常為0  DWORD  TimeDateStamp;   //資源建立的時間  WORD  MajorVersion;   //理論上是放置資源的版本,但是通常為0  WORD  MinorVersion;   //定義資源時,既可以使用字符串作為名稱來標識一個資源,也可以通過ID號來標識資源。資源目錄項的數量等于兩者之和。  WORD  NumberOfNamedEntries; //以字符串命名的資源數量  WORD  NumberOfIdEntries;  //以整型數字(ID)命名的資源數量// IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

資源目錄項結構(IMAGE_RESOURCE_DIRECTORY_ENTRY),占8字節,包含2個字段,結構定義如下。

//如果看不懂下面的結構建議復習一下C中的union,struct,位域typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {  union {    struct {      DWORD NameOffset:31;      DWORD NameIsString:1;    } DUMMYSTRUCTNAME;    DWORD  Name;    WORD  Id;  } DUMMYUNIONNAME;  union {    DWORD  OffsetToData;    struct {      DWORD  OffsetToDirectory:31;      DWORD  DataIsDirectory:1;    } DUMMYSTRUCTNAME2;  } DUMMYUNIONNAME2;} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

重要字段:

Name字段:定義目錄項的名稱或ID。

     - 當結構用于第1層目錄時,定義的是資源類型;

     - 當結構用于第2層目錄時,定義的是資源的名稱;

     - 當結構用于第3層目錄時,定義的是代碼頁編號。

     - 當最高位為0時,表示字段的值作為ID使用;由該字段的低16位組成整數標識符ID

     - 當最高位為1時,表示字段的低位作為指針使用,資源名稱字符串使用Unicode編碼,

       這個指針不直接指向字符串,而指向一個IMAGE_RESOURCE_DIR_STRING_U結構。

typedef struct _IMAGE_RESOURCE_DIR_STRING_U {  WORD  Length;  //字符串的長度  WCHAR  NameString[ 1 ]; //Unicode字符串,按字對齊,長度可變;由Length 指明 Unicode字符串的長度} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;

OffsetToData字段:是一個指針。

     - 當最高位(位31)為1時,低位數據指向下一層目錄塊的起始地址;

     - 當最高位為0時,指針指向IMAGE_RESOURCE_DATA_ENTRY結構。

第3層目錄結構中的OffsetToData將指向IMAGE_RESOURCE_DATA_ENTRY結構。

該結構描述了資源數據的位置和大小,其定義如下。

typedef struct _IMAGE_RESOURCE_DATA_ENTRY {  DWORD  OffsetToData;  //資源數據的RVA  DWORD  Size;      //資源數據的長度  DWORD  CodePage;    //代碼頁,一般為0  DWORD  Reserved;    //保留字段} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;

重要字段:

OffsetToData:指向資源數據的指針(RVA)

Size:資源數據的長度

實例分析:

定位資源在文件中的位置

由于當前exe文件對齊與內存對齊都是4k,RVA不需要轉FOA

所以:

圖標的真正資源數據RVA為4100h,大小為2E8h。

菜單的真正資源數據RVA為4400h,大小為5Ah。

圖標組的真正資源數據RVA為43E8h,大小為14h。

使用工具驗證

'

可以清晰看到根目錄有3個資源目錄項(Icon,Menu,Icon Group)

第二層為資源ID或資源名稱

第三層為代碼頁ID為2052表簡體中文,1033表美國英語

右下角圖標為真正資源數據

 好了這篇文章暫時就先介紹到這了,希望大家以后多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲性猛交xxxxwww| 91精品视频专区| 亚洲综合中文字幕在线观看| 久久免费精品日本久久中文字幕| 国语自产在线不卡| 成人免费黄色网| 97视频在线免费观看| 精品亚洲一区二区三区在线播放| 亚洲精品成人久久久| 精品一区二区三区三区| 国产suv精品一区二区三区88区| 亚洲石原莉奈一区二区在线观看| 91av在线国产| 欧美成人一区二区三区电影| 自拍偷拍亚洲在线| 日韩在线资源网| 国产日韩av高清| 国产日产久久高清欧美一区| 成人乱色短篇合集| 亚洲第一页在线| 久久久人成影片一区二区三区观看| 视频一区视频二区国产精品| 国产精品最新在线观看| 亚洲欧美日本精品| 91tv亚洲精品香蕉国产一区7ujn| 亚洲图片欧美午夜| 亚洲日本中文字幕| 91精品国产综合久久香蕉| 91精品国产777在线观看| 国产精品毛片a∨一区二区三区|国| 日韩成人网免费视频| 亚洲天堂av图片| 欧美日韩一区二区三区| 2018中文字幕一区二区三区| 欧美精品日韩www.p站| 亚洲国产精品yw在线观看| 91丝袜美腿美女视频网站| 日韩免费av片在线观看| 亚洲а∨天堂久久精品9966| 欧美老妇交乱视频| 色偷偷偷综合中文字幕;dd| 国内免费久久久久久久久久久| 久久91精品国产91久久跳| 亚洲美女中文字幕| 亚洲四色影视在线观看| 91精品国产99| 国产免费观看久久黄| 日产日韩在线亚洲欧美| 91亚洲精品久久久| 日韩福利视频在线观看| 久久在精品线影院精品国产| 57pao国产成人免费| 九色精品美女在线| 亚洲国产精品国自产拍av秋霞| 亚洲国产精品va在线看黑人| 久久精品国产亚洲一区二区| 欧美黑人一级爽快片淫片高清| 国产999精品| 国产精品永久免费视频| 欧美性生活大片免费观看网址| 亚洲精品美女在线| 欧美在线中文字幕| 欧美在线视频在线播放完整版免费观看| 亚洲一区二区久久久久久久| 亚洲欧美另类在线观看| 国产精品女人久久久久久| 中文在线资源观看视频网站免费不卡| www.日韩不卡电影av| 91精品久久久久久久久中文字幕| 欧洲日本亚洲国产区| 欧美精品亚州精品| 最近2019中文字幕一页二页| 国产成人综合av| 日韩福利伦理影院免费| 亚洲精品在线不卡| 亚洲国产精品va在线观看黑人| 国产一区香蕉久久| 日韩高清电影免费观看完整版| 欧美视频中文在线看| 欧美成人精品不卡视频在线观看| 7m精品福利视频导航| 91精品国产91久久久久久不卡| 97婷婷大伊香蕉精品视频| 欧美日韩激情美女| 色悠悠久久88| 亚洲精品自在久久| 国产不卡精品视男人的天堂| 国产一区二区日韩精品欧美精品| 国产成人精品在线| 午夜精品久久久久久99热| 91国自产精品中文字幕亚洲| 2025国产精品视频| 7m第一福利500精品视频| 亚洲高清久久网| 亚洲国内精品视频| 国产精品99一区| 精品高清美女精品国产区| 91国产中文字幕| 亚洲视频专区在线| 欧美有码在线视频| 最新国产成人av网站网址麻豆| 亚洲精品成人久久久| 日韩高清欧美高清| 欧美大荫蒂xxx| 午夜精品久久久久久久白皮肤| 在线观看国产成人av片| 91精品国产91久久| 一区二区三区四区精品| 精品国产欧美一区二区五十路| 国产精品第10页| 国产亚洲精品美女久久久久| 欧美电影在线观看高清| 亚洲欧美日韩一区二区在线| 欧美中文字幕第一页| 亚洲国产精品美女| 欧美日韩一区二区免费在线观看| 国产日韩在线亚洲字幕中文| 国产精品久久久久久久久久小说| 欧美精品一二区| 国产精品久久久久一区二区| 精品视频一区在线视频| 欧美日韩亚洲视频一区| 亚洲人成电影在线播放| 日韩精品电影网| 国产中文日韩欧美| 久久久久久12| 日韩美女福利视频| 亚洲精品国产精品自产a区红杏吧| 欧美电影免费观看| 91国自产精品中文字幕亚洲| 亚洲精品国产精品国自产观看浪潮| 欧美体内谢she精2性欧美| 成人xxxx视频| 久久国产精品久久久久久| 91人人爽人人爽人人精88v| 亚洲激情视频在线播放| 91免费电影网站| 高清欧美性猛交xxxx黑人猛交| 久久精品久久久久电影| 欧美日韩国产一中文字不卡| 久久国产精品99国产精| 国产免费久久av| 91精品国产91久久久久久| 日韩欧美成人免费视频| 91社影院在线观看| 黑人欧美xxxx| 69av视频在线播放| 色偷偷9999www| 国产亚洲精品一区二555| 91高清在线免费观看| 久久人人爽国产| 91夜夜未满十八勿入爽爽影院| 国产99久久精品一区二区| 国产精品入口夜色视频大尺度| 91色琪琪电影亚洲精品久久| 国内精品一区二区三区四区| 久久久噜噜噜久久| 日本精品一区二区三区在线| 精品亚洲夜色av98在线观看| 亚洲色图综合网| 成人激情视频网| 精品无码久久久久久国产| 精品国产一区二区三区久久狼5月| 亚洲爱爱爱爱爱|