今天把導入函數表弄明白了,在昨天代碼的基礎上增長列出導入函數部分。c++
主程序部分略有改動:如今電腦 CPU 快、內存大,操做內存比硬盤快多了,因此直接把整個文件拷貝到內存再分析!編程
/* Typedef.h 數據類型 四彩 2015-11-29 */ #ifndef _TYPEDEF_H #define _TYPEDEF_H #ifndef _STDBOOL_H typedef char BOOL; #define TRUE 1 #define FALSE 0 #endif typedef unsigned char BYTE; // 無符號 1 字節 typedef unsigned short int WORD; // 無符號 2 字節 typedef unsigned int DWORD; // 無符號 4 字節 // 獲得一個 DWORD 的高位 WORD 和低位 WORD #define LWordOfDW(value) ((WORD)((DWORD)(value) & 0xFFFF)) #define HWordOfDW(value) ((WORD)((DWORD)(value) >> 16)) // 獲得一個 WORD 的高位 BYTE 和低位 BYTE #define LByteOfW(value) ((BYTE)((WORD)(value) & 0xFF)) #define HByteOfW(value) ((BYTE)((WORD)(value) >> 8)) // 把兩個 WORD 轉化爲一個 DWORD #define Words2DW(HighWord, LowWord) ((((DWORD)(HighWord)) << 16) + (LowWord)) // 把兩個 BYTE 轉化爲一個 WORD #define Bytes2W(HighByte, LowByte) ((((WORD)(HighByte)) << 8) + (LowByte)) #endif
/* PE.h PE 文件格式 四彩 2015-12-02 */ /* PE 文件的整體結構:(內存地址有低到高) IMAGE_DOS_HEADER(DOS 頭) DOS stub "PE00"(PE 標誌) IMAGE_FILE_HEADER(文件頭) IMAGE_OPTIONAL_HEADER(可選頭) IMAGE_DATA_DIRECTORY(數據塊目錄) IMAGE_SECTION_HEADER(節表) .text節區 .data節區 其它節區 不能被映射的其餘數據 */ /* 虛擬地址:Virtual Address(VA),保護模式下訪問內存所使用的邏輯地址。 裝載基址:Image Base,文件裝入內存的基址。 默認狀況下,EXE 文件的基址爲 0x00400000,DLL 文件的基址爲 0x10000000。 相對虛擬地址:Relative Virtual Address(RVA),在內存中相對於裝載基址的偏移量。 文件偏移地址:File Offset Address(FOA),文件在磁盤上存放時相對於文件開頭的偏移量。 文件偏移地址從文件的第一個字節開始計數,起始值爲 0。 */ /* IMAGE_DATA_DIRECTORY 直接定義在 IMAGE_NT_HEADER 裏面,而不在 IMAGE_OPTIONAL_HEADER 裏, 這致使 IMAGE_FILE_HEADER 中的 SizeOfOptionalHeader 字段數值不許。 */ #ifndef _PE_H #define _PE_H #include "Typedef.h" // ======================================================================================= // DOS 頭結構:偏移地址 = 0 // #define IMAGE_DOS_SIGNATURE 0x5A4D // e_magic 預約義值,即字符 "MZ" typedef struct tag_IMAE_DOS_HEADER { // 偏移 說明 WORD e_magic; // 0x00 DOS 可執行文件標識符(= "MZ") WORD e_cblp; // 0x02 WORD e_cp; // 0x04 WORD e_crlc; // 0x06 WORD e_cparhdr; // 0x08 WORD e_minalloc; // 0x0A WORD e_maxalloc; // 0x0C WORD e_ss; // 0x0E WORD e_sp; // 0x10 WORD e_csum; // 0x12 WORD e_ip; // 0x14 WORD e_cs; // 0x16 WORD e_lfarlc; // 0x18 WORD e_ovno; // 0x1A WORD e_res[4]; // 0x1C WORD e_oemid; // 0x24 WORD e_oeminfo; // 0x26 WORD e_res2[10]; // 0x28 DWORD e_lfanew; // 0x3C PE 簽名的文件偏移地址 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; // *************************************************************************************** // ======================================================================================= // NT 頭結構:偏移地址 = IMAGE_DOS_HEADER.e_lfanew // // --------------------------------------------------------------------------------------- // NT 頭結構 —— File 頭結構 // Machine 字段預約義值: Value Meaning #define IMAGE_FILE_MACHINE_UNKNOWN 0 #define IMAGE_FILE_MACHINE_I386 0x014c // x86 #define IMAGE_FILE_MACHINE_AMD64 0x8664 // x64 #define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel Itanium /* Characteristics 是一個標誌的集合 位 預約義值 含義 0 IMAGE_FILE_RELOCS_STRIPPED 文件中沒有重定向信息。在可執行文件中沒有使用。 可執行文件用基址重定向目錄表來表示重定向信息。 1 IMAGE_FILE_EXECUTABLE_IMAGE 可執行文件。 2 IMAGE_FILE_LINE_NUMS_STRIPPED 沒有行數信息。在可執行文件中沒有使用。 3 IMAGE_FILE_LOCAL_SYMS_STRIPPED 沒有局部符號信息。在可執行文件中沒有使用。 4 IMAGE_FILE_AGGRESIVE_WS_TRIM 已無效。 5 IMAGE_FILE_LARGE_ADDRESS_AWARE 應用程序能夠處理超過 2 GB 的地址。 6 未使用 7 IMAGE_FILE_BYTES_REVERSED_LO 已無效。 8 IMAGE_FILE_32BIT_MACHINE 但願機器爲32位機。這個值永遠爲 1。 9 IMAGE_FILE_DEBUG_STRIPPED 沒有調試信息。在可執行文件中沒有使用。 10 IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 該程序不能運行於可移動介質中(如軟驅或CD)。 11 IMAGE_FILE_NET_RUN_FROM_SWAP 程序不能在網上運行。(必須拷貝到內存中執行) 12 IMAGE_FILE_SYSTEM 系統文件,如驅動程序。在可執行文件中沒有使用。 13 IMAGE_FILE_DLL 動態連接庫(DLL)。 14 IMAGE_FILE_UP_SYSTEM_ONLY 不能運行於多處理器系統中。 15 IMAGE_FILE_BYTES_REVERSED_HI 已無效。 */ // Characteristics 字段預約義值 #define IMAGE_FILE_RELOCS_STRIPPED 0x0001 #define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 #define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 #define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 #define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 #define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 #define IMAGE_FILE_32BIT_MACHINE 0x0100 #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 #define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 #define IMAGE_FILE_SYSTEM 0x1000 #define IMAGE_FILE_DLL 0x2000 #define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 #define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 typedef struct tag_IMAGE_FILE_HEADER { WORD Machine; // 0x04 運行所要求的 CPU 類型。 WORD NumberOfSections; // 0x06 節數。 DWORD TimeDateStamp; // 0x08 文件建立日期和時間 DWORD PointerToSymbolTable; // 0x0C 指向符號表(用於調試) DWORD NumberOfSymbols; // 0x10 符號表中符號個數(用於調試) WORD SizeOfOptionalHeader; // 0x14 IMAGE_OPTIONAL_HEADER 結構大小 WORD Characteristics; // 0x16 屬性 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; // --------------------------------------------------------------------------------------- // NT 頭結構 —— NT 可選頭結構 typedef struct tag_IMAGE_OPTIONAL_HEADER { // 偏移 說明 // Standard fields. WORD Magic; // 0x18 標誌字(Win32 下老是 0x010B) BYTE MajorLinkerVersion; // 0x1A 連接程序的主版本號 BYTE MinorLinkerVersion; // 0x1B 連接程序的次版本號 DWORD SizeOfCode; // 0x1C 全部含代碼的節的總大小 DWORD SizeOfInitializedData; // 0x20 全部含已初始化數據的節的總大小 DWORD SizeOfUninitializedData; // 0x24 全部含未初始化數據的節的大小 DWORD AddressOfEntryPoint; // 0x28 程序執行入口地址(RVA) DWORD BaseOfCode; // 0x2C 代碼的區塊的起始地址(RVA) DWORD BaseOfData; // 0x30 數據的區塊的起始地址(RVA) // NT additional fields. DWORD ImageBase; // 0x34 首選裝載基址 DWORD SectionAlignment; // 0x38 內存中節的對齊單位(32 位下爲 4K) DWORD FileAlignment; // 0x3C 磁盤文件中節的對齊單位(一個扇區大小) WORD MajorOperatingSystemVersion; // 0x40 要求操做系統最低版本號的主版本號 WORD MinorOperatingSystemVersion; // 0x42 要求操做系統最低版本號的副版本號 WORD MajorImageVersion; // 0x44 可運行於操做系統的主版本號 WORD MinorImageVersion; // 0x46 可運行於操做系統的次版本號 WORD MajorSubsystemVersion; // 0x48 要求最低子系統版本的主版本號 WORD MinorSubsystemVersion; // 0x4A 要求最低子系統版本的次版本號 DWORD Win32VersionValue; // 0x4C 不明。通常爲 0 。 DWORD SizeOfImage; // 0x50 裝入內存後的總大小 DWORD SizeOfHeaders; // 0x54 全部頭 + 節表的總大小 DWORD CheckSum; // 0x58 校檢和 WORD Subsystem; // 0x5C 可執行文件指望的界面子系統 WORD DllCharacteristics; // 0x5E DllMain() 函數什麼時候被調用,默認爲 0 DWORD SizeOfStackReserve; // 0x60 初始化時的棧大小 DWORD SizeOfStackCommit; // 0x64 初始化時實際提交的棧大小 DWORD SizeOfHeapReserve; // 0x68 初始化時保留的堆大小 DWORD SizeOfHeapCommit; // 0x6C 初始化時實際提交的堆大小 DWORD LoaderFlags; // 0x70 與調試有關,默認爲 0 DWORD NumberOfRvaAndSizes; // 0x74 數據目錄表的項數 } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER; // --------------------------------------------------------------------------------------- // NT 頭結構 —— 數據目錄結構 typedef struct tag_IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; // 數據的相對虛擬地址(RVA) DWORD Size; // 數據的長度 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; // --------------------------------------------------------------------------------------- // NT 頭結構 #define IMAGE_NT_SIGNATURE 0x00004550 // Signature 字段預約義值,即字符"PE00" #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 // 數據目錄表的項數一直是 16 /* 數據目錄表各項的含義 索引號 預約義值 對應的數據塊 0 IMAGE_DIRECTORY_ENTRY_EXPORT 導出函數表,主要用於 DLL 中的導出函數 1 IMAGE_DIRECTORY_ENTRY_IMPORT 導入函數表,使用外部函數的數據表 2 IMAGE_DIRECTORY_ENTRY_RESOURCE 資源數據表 3 IMAGE_DIRECTORY_ENTRY_EXCEPTION 異常處理表(具體資料不詳) 4 IMAGE_DIRECTORY_ENTRY_SECURITY 安全處理數據表(具體資料不詳) 5 IMAGE_DIRECTORY_ENTRY_BASERELOC 重定位信息表,通常和 DLL 相關 6 IMAGE_DIRECTORY_ENTRY_DEBUG 調試信息表 7 IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 版權信息表 8 IMAGE_DIRECTORY_ENTRY_GLOBALPTR 機器值(MIPS GP)(具體資料不詳) 9 IMAGE_DIRECTORY_ENTRY_TLS 線程信息本地存儲表 10 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 裝配信息表(具體資料不詳) 11 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 輸入函數綁定信息表(具體資料不詳) 12 IMAGE_DIRECTORY_ENTRY_IAT 導入函數地址表(與 ImportTable 對應, 由 Loader 填寫的輸入函數地址) 13 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 延遲裝入的函數信息表(具體資料不詳) 14 IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 公用組件信息表(具體資料不詳) 15 未使用 */ #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 #define IMAGE_DIRECTORY_ENTRY_TLS 9 #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 #define IMAGE_DIRECTORY_ENTRY_IAT 12 #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 typedef struct tag_IMAGE_NT_HEADERS { // 偏移 說明 DWORD Signature; // 0x00 PE 文件簽名(= "PE00") IMAGE_FILE_HEADER FileHeader; // 0x04 文件頭結構 IMAGE_OPTIONAL_HEADER OptionalHeader; // 0x18 可選 NT 頭結構 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 0x78 數據目錄表 } IMAGE_NT_HEADER, *PIMAGE_NT_HEADER; // *************************************************************************************** // ======================================================================================= // 節表:偏移地址 = IMAGE_DOS_HEADER.e_lfanew + sizeof(IMAGE_NT_HEADER) // 項數由 IMAGE_NT_HEADER 中的 FileHeader.NumberOfSections 指定 /* 一個節中的數據只是屬性相同,並不必定是同一種用途的內容,所以僅依靠節表是沒法肯定和定位 的,而要由數據目錄表來定位。 節表是節的目錄,數據目錄表是存儲在節裏的邏輯元素的目錄。 不管是在內存中仍是在磁盤文件中,節都是按基址排列的,並且都要對齊,但對齊值通常不一樣。 映射到內存後,全部頭和節表的偏移位置與大小均沒有變化,而各節基址的偏移位置發生了變化。 無論節是在文件中仍是被加載到內存中,節中數據的位置相對該節的基址是不變的。 在內存中:數據相對節的起始位置的偏移h = 數據的RVA - 節的RVA 在文件中:數據相對節的起始位置的偏移h = 數據的FOA – 節的FOA */ // // 節(區塊)頭結構 // #define IMAGE_SIZEOF_SHORT_NAME 8 // Name 字段最長 8 字節 // Characteristics 字段預約義值 #define IMAGE_SCN_CNT_CODE 0x20 // 包含代碼 #define IMAGE_SCN_CNT_INITIALIZED_DATA 0x40 // 包含已初始化數據 #define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x80 // 包含未初始化數據 #define IMAGE_SCN_MEM_DISCARDABLE 0x2000000 // 數據在進程開始後將被丟棄 #define IMAGE_SCN_MEM_NOT_CACHED 0x4000000 // 數據不通過緩存 #define IMAGE_SCN_MEM_NOT_PAGED 0x8000000 // 數據不被交換出內存 #define IMAGE_SCN_MEM_SHARED 0x10000000 // 數據可共享 #define IMAGE_SCN_MEM_EXECUTE 0x20000000 // 可執行 #define IMAGE_SCN_MEM_READ 0x40000000 // 可讀 #define IMAGE_SCN_MEM_WRITE 0x80000000 // 可寫 typedef struct tag_IMAGE_SECTION_HEADER { // 偏移 說明 BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 0x00 節表名稱(僅供編程參考) union // 0x08 通常是取後一個,即節的真實長度 { DWORD PhysicalAddress; // 物理地址(在目標文件中使用,重定位到的地址) DWORD VirtualSize; // 真實長度(在可執行文件中使用,未作對齊處理的實際大小) } Misc; DWORD VirtualAddress; // 0x0C 裝載到內存的基址(內存對齊後的 RVA) DWORD SizeOfRawData; // 0x10 在文件中的大小(在磁盤中對齊後的大小) DWORD PointerToRawData; // 0x14 在文件中的偏移量(從文件頭開始算起) DWORD PointerToRelocations; // 0x18 重定位的偏移(在 OBJ 文件中供調試用) DWORD PointerToLinenumbers; // 0x1C 行號表的偏移(同上) WORD NumberOfRelocations; // 0x20 重定位項的數目(同上) WORD NumberOfLinenumbers; // 0x22 行號表中行號的數目(同上) DWORD Characteristics; // 0x24 節的屬性(如可讀、可寫、可執行等) } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; // *************************************************************************************** // ======================================================================================= // --------------------------------------------------------------------------------------- // 導入函數表:偏移地址 = IMAGE_NT_HEADER.IMAGE_DATA_DIRECTORY[1].VirtualAddress // 以一個全 0 成員的數組項結尾。 /* 一、導入函數是被某模塊調用,但又不在調用者模塊中(實際位於別的 DLL 文件裏)的函數。 調用者模塊裏只保留一些函數信息,包括函數名及其駐留的 DLL 文件名。 二、導入函數表就是一個 IMAGE_IMPORT_DESCRIPTOR 數組。 每一個數組項(一個 IMAGE_IMPORT_DESCRIPTOR 結構)對應一個導入的 DLL。 三、每一個 IMAGE_IMPORT_DESCRIPTOR 結構指向兩個不一樣的 IMAGE_THUNK_DATA 結構數組。 加載前 OriginalFirstThunk 與 FirstThunk 指向的數組內容是相同的,都包含導入信息; 加載後 FirstThunk 指向的數組指向實際的函數地址。 */ // // 導入函數表數組項結構 // typedef struct tag_IMAGE_IMPORT_BY_NAME { WORD Hint; // 本函數在其所駐留 DLL 的導出表中的索引號(非必須) char Name[1]; // 函數名(可變尺寸域) } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME; typedef struct tag_IMAGE_THUNK_DATA { union { DWORD ForwarderString; DWORD Function; // 函數地址 DWORD Ordinal; // 函數序號 DWORD AddressOfData; // 指向一個 IMAGE_IMPORT_BY_NAME 結構的指針 } u1; } IMAGE_THUNK_DATA, *PIMAGE_THUNK_DATA; /* 經過二進制的最高位判斷是否按序號導入:1,按序號導入(Ordinal 的低16位就是導入序號); 0,按名字導入(AddressOfData 指向名字信息)。 */ #define IMAGE_SNAP(Ordinal) ((Ordinal) >> 31) // 判斷是否按序號導入 #define IMAGE_ORDINAL(Ordinal) ((Ordinal) & 0xFFFF) // 獲取導入序號 typedef struct tag_IMAGE_IMPORT_DESCRIPTOR { union // 0x00 { DWORD Characteristics; DWORD OriginalFirstThunk; // 指向一個 IMAGE_THUNK_DATA 數組 } DUMMYUNIONNAME; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; // 0x0C 指向 DLL 文件名的指針 DWORD FirstThunk; // 0x10 指向另外一個 IMAGE_THUNK_DATA 數組(RVA) } IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR; // *************************************************************************************** // ======================================================================================= // 輸出函數表 // --------------------------------------------------------------------------------------- /* 函數功能: 相對虛擬地址轉換爲文件偏移地址 參數表: pBase = 將整個文件都拷貝到內存的緩衝區基址 dwRva = 相對虛擬地址 返回值: 文件偏移地址 */ DWORD Rva2Foa(BYTE *pBase, DWORD dwRva); /* 函數功能: 文件偏移地址轉換爲相對虛擬地址 參數表: pBase = 將整個文件都拷貝到內存的緩衝區基址 dwFoa = 文件偏移地址 返回值: 相對虛擬地址 */ DWORD Foa2Rva(BYTE *pBase, DWORD dwFoa); // --------------------------------------------------------------------------------------- /* 函數功能: 統計 PE 文件所有導入函數的個數 參數表: pBase = 將整個文件都拷貝到內存的緩衝區基址 返回值: 所有導入函數的個數 */ DWORD GetNumOfImpFun(BYTE *pBase); /* 函數功能: 取得 PE 文件中指定順序號的導入函數的函數名或導入序號 入口參數表: pBase = 將整個文件都拷貝到內存的緩衝區基址 iSN = 順序號(從 0 開始) 出口參數表: sDllName = 包含該函數的 dll 模塊名 sFunName = 函數名 wOrd = 序號 返回值: 函數的導入方式:0 = 函數名導入,1 = 序號導入 -1 = 順序號超過導入函數總數 */ int GetSnapOfImpFun(BYTE *pBase, DWORD iSN, char *sDllName, char *sFunName, WORD *wOrd); // *************************************************************************************** #endif
/* PE.c PE 文件格式輸出函數 四彩 2015-12-01 */ #include <stdlib.h> #include <string.h> #include "PE.h" // ======================================================================================= // RVA 與 FOA 的互相轉換 // --------------------------------------------------------------------------------------- /* 相對虛擬地址轉換爲文件偏移地址的轉換思路: (1)掃描節表,將每一個節在內存中的基址加上節的大小獲得節的終址, 根據基址、終址判斷 dwRva 是否在該節內範圍。 (2)用 dwRva 減去該節的內存基址,獲得 dwRva 相對於該節內存基址的偏移量 RVA'。 將該節在文件中的基址加上 RVA',獲得文件偏移地址。 */ /* 函數功能: 相對虛擬地址轉換爲文件偏移地址 參數表: pBase = 將整個文件都拷貝到內存的緩衝區基址 dwRva = 相對虛擬地址 返回值: 文件偏移地址 */ DWORD Rva2Foa(BYTE *pBase, DWORD dwRva) { WORD i; IMAGE_NT_HEADER *pNtH; IMAGE_SECTION_HEADER *pSH; pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew); if(dwRva < pNtH->OptionalHeader.SizeOfHeaders) // 在頭部 return dwRva; // 遍歷各節 pSH = (IMAGE_SECTION_HEADER *)((BYTE *)pNtH + sizeof(IMAGE_NT_HEADER)); for(i = 0; i < pNtH->FileHeader.NumberOfSections; i++) { if(dwRva >= pSH->VirtualAddress && dwRva < pSH->VirtualAddress + pSH->Misc.VirtualSize) return pSH->PointerToRawData + dwRva - pSH->VirtualAddress; pSH++; } return 0; } // --------------------------------------------------------------------------------------- /* 文件偏移地址轉換爲相對虛擬地址的轉換思路: (1)掃描節表,將每一個節在文件中的基址加上節的大小獲得節的終址, 根據基址、終址判斷 dwFoa 是否在該節範圍內。 (2)用 dwFoa 減去該節的文件基址,獲得 dwFoa 相對於該節文件基址的偏移量 FOA'。 將該節在內存中的基址加上 FOA',獲得相對虛擬地址。 */ /* 函數功能: 文件偏移地址轉換爲相對虛擬地址 參數表: pBase = 將整個文件都拷貝到內存的緩衝區基址 dwFoa = 文件偏移地址 返回值: 相對虛擬地址 */ DWORD Foa2Rva(BYTE *pBase, DWORD dwFoa) { WORD i; IMAGE_NT_HEADER *pNtH; IMAGE_SECTION_HEADER *pSH; pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew); if(dwFoa < pNtH->OptionalHeader.SizeOfHeaders) return dwFoa; pSH = (IMAGE_SECTION_HEADER *)((BYTE *)pNtH + sizeof(IMAGE_NT_HEADER)); for(i = 0; i < pNtH->FileHeader.NumberOfSections; i++) { if(dwFoa >= pSH->PointerToRawData && dwFoa < pSH->PointerToRawData + pSH->Misc.VirtualSize) return pSH->VirtualAddress + dwFoa - pSH->PointerToRawData; pSH++; } return 0; } // *************************************************************************************** // ======================================================================================= // 導出函數與導入函數 // // --------------------------------------------------------------------------------------- /* 獲取全部導入函數的函數名或序號: 載入 PE 文件到內存,用內存緩衝區基址定位 IMAGE_NT_HEADER 地址。 用數據目錄表中第二項的 VirtualAddress 值,定位第一個 IMAGE_IMPORT_DESCRIPTOR 地址。 檢查 FirstThunk :若不爲 0,用 OriginalFirstThunk 定位第一個 IMAGE_THUNK_DATA 數組。 檢查 u1.Ordinal 的二進制最高位: 若爲 1,那麼函數是由序號導入的,能夠從 Ordinal 的低字提取序號; 若爲 0,那麼函數是由函數名導入的,能夠由 AddressOfData 指向的 IMAGE_IMPORT_BY_NAME 取得函數名。 */ /* 函數功能: 統計 PE 文件所有導入函數的個數 參數表: pBase = 將整個文件都拷貝到內存的緩衝區基址 返回值: 所有導入函數的個數 */ DWORD GetNumOfImpFun(BYTE *pBase) { DWORD i = 0; IMAGE_NT_HEADER *pNtH; IMAGE_IMPORT_DESCRIPTOR *pImpDsc; IMAGE_THUNK_DATA *pThk; pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew); if(pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size == 0) return 0; pImpDsc = (IMAGE_IMPORT_DESCRIPTOR *)(pBase + Rva2Foa(pBase, pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)); while(pImpDsc->Name) { pThk = (IMAGE_THUNK_DATA *)(pBase + Rva2Foa(pBase, pImpDsc->FirstThunk)); while(pThk->u1.Ordinal) { pThk++; i++; } pImpDsc++; } return i; } /* 函數功能: 取得 PE 文件中指定順序號的導入函數的函數名或導入序號 入口參數表: pBase = 將整個文件都拷貝到內存的緩衝區基址 iSN = 順序號(從 0 開始) 出口參數表: sDllName = 包含該函數的 dll 模塊名 sFunName = 函數名 wOrd = 序號 返回值: 函數的導入方式:0 = 函數名導入,1 = 序號導入 -1 = 順序號超過導入函數總數 */ int GetSnapOfImpFun(BYTE *pBase, DWORD iSN, char *sDllName, char *sFunName, WORD *wOrd) { DWORD i = 0; IMAGE_NT_HEADER *pNtH; IMAGE_IMPORT_DESCRIPTOR *pImpDsc; IMAGE_THUNK_DATA *pThk; pNtH = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew); if(pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size == 0) return 0; pImpDsc = (IMAGE_IMPORT_DESCRIPTOR *)(pBase + Rva2Foa(pBase, pNtH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)); while(pImpDsc->Name) { pThk = (IMAGE_THUNK_DATA *)(pBase + Rva2Foa(pBase, pImpDsc->FirstThunk)); while(pThk->u1.Ordinal) { if(i == iSN) // 找到了 { strcpy(sDllName, (char *)(pBase + Rva2Foa(pBase, (pImpDsc->Name)))); if(IMAGE_SNAP(pThk->u1.Ordinal)) // 序號導入 { *wOrd = IMAGE_ORDINAL(pThk->u1.Ordinal); return 1; } else // 函數名導入 { strcpy(sFunName, ((IMAGE_IMPORT_BY_NAME *)(pBase + Rva2Foa(pBase, pThk->u1.AddressOfData)))->Name); return 0; } } pThk++; i++; } pImpDsc++; } return -1; } // ***************************************************************************************
/* PEM.c 顯示 PE 文件格式信息 四彩 2015-11-30 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "PE.h" #define MAX_PATH 260 // 最長字符串長度 void PrintDosHeader(IMAGE_DOS_HEADER *pDosHeader); void PrintFileHeader(IMAGE_FILE_HEADER FileHeader); void PrintOptionalHeader(IMAGE_OPTIONAL_HEADER OptHeader); void PrintDataDircrory(IMAGE_DATA_DIRECTORY DataDirectory); void PrintSectionHeader(IMAGE_SECTION_HEADER *pSectionHeader); int main(int argc, char **argv) { FILE *fp; DWORD i, dwFileLen ; BYTE *pBase; IMAGE_DOS_HEADER *pDosHeader; IMAGE_NT_HEADER *pNtHeader; IMAGE_SECTION_HEADER *pFstSctHeader, *pSctHeader; DWORD dwNumOfImptFun; int snap; char sDllName[MAX_PATH], sFunName[MAX_PATH]; WORD wOrd; if(argc != 2) { printf("Usage : %s exeFileName\n", argv[0]); return -1; } // 將整個文件都拷貝到內存 fp = fopen(argv[1], "rb"); if (fp == NULL) { printf("Error : open file\n"); return -1; } fseek(fp, 0, SEEK_END); dwFileLen = ftell(fp); pBase = (BYTE *)malloc(dwFileLen); if(pBase == NULL) { printf("Error : malloc memory\n"); return -1; } fseek(fp, 0, SEEK_SET); fread(pBase, dwFileLen, 1, fp); fclose(fp); // 檢查是否爲 PE 文件,並給相關結構指針賦值 pDosHeader = (IMAGE_DOS_HEADER *)pBase; if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { printf("Error : NOT DOS file\n"); free(pBase); return -1; } pNtHeader = (IMAGE_NT_HEADER *)(pBase + ((IMAGE_DOS_HEADER *)pBase)->e_lfanew); if(pNtHeader->Signature != IMAGE_NT_SIGNATURE) { printf("Error : NOT PE file\n"); free(pBase); return -1; } pFstSctHeader = (IMAGE_SECTION_HEADER *)((BYTE *)pNtHeader + sizeof(IMAGE_NT_HEADER)); // 顯示 IMAGE_DOS_HEADER 信息 printf("IMAGE_DOS_HEADER :\n"); PrintDosHeader(pDosHeader); // 顯示 IMAGE_NT_HEADER 信息 printf("\nIMAGE_NT_HEADER :\n"); // Signature printf("Signature : %c%c%d%d\n", // 低低高高 LByteOfW(LWordOfDW(pNtHeader->Signature)), HByteOfW(LWordOfDW(pNtHeader->Signature)), LByteOfW(HWordOfDW(pNtHeader->Signature)), HByteOfW(HWordOfDW(pNtHeader->Signature))); printf("\nIMAGE_FILE_HEADER :\n"); // IMAGE_FILE_HEADER PrintFileHeader(pNtHeader->FileHeader); printf("\nIMAGE_OPTIONAL_HEADER :\n"); // IMAGE_OPTIONAL_HEADER PrintOptionalHeader(pNtHeader->OptionalHeader); printf("\nIMAGE_DATA_DIRECTORY : "); // 遍歷所有 IMAGE_DATA_DIRECTORY printf("\n NO. VirtualAddress Size"); for(i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) { printf("\n %2d", i); PrintDataDircrory(pNtHeader->DataDirectory[i]); } // 遍歷顯示所有 IMAGE_SECTION_HEADER 信息 pSctHeader = pFstSctHeader; printf("\n\nIMAGE_SECTION_HEADER :"); for(i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++) { printf("\nSECTION_HEADER NO : %02d\nFOA : 0x%08X\n", i, Rva2Foa(pBase, (DWORD)((BYTE *)pSctHeader - pBase))); PrintSectionHeader(pSctHeader); pSctHeader++; } // 遍歷顯示所有導入函數信息 dwNumOfImptFun = GetNumOfImpFun(pBase); printf("\nIMAGE_IMPORT_DESCRIPTOR : %d", dwNumOfImptFun); printf("\n SN DllName FunName"); for(i = 0; i < dwNumOfImptFun; i++) { snap = GetSnapOfImpFun(pBase, i, sDllName, sFunName, &wOrd); if(snap == 0) // 函數名導入 { printf("\n %02d % 12s %s", i, sDllName, sFunName); } else if(snap == 1) // 序號導入 { printf("\n %02d % 12s 0x%04X", i, sDllName, wOrd); } } free(pBase); return 0; } // 顯示 IMAGE_DOS_HEADER 信息 void PrintDosHeader(IMAGE_DOS_HEADER *pDosHeader) { int i; printf("e_magic : %c%c\n", LByteOfW(pDosHeader->e_magic), HByteOfW(pDosHeader->e_magic)); printf("e_cblp : 0x%04X\n", pDosHeader->e_cblp); printf("e_cp : 0x%04X\n", pDosHeader->e_cp); printf("e_crlc : 0x%04X\n", pDosHeader->e_crlc); printf("e_cparhdr : 0x%04X\n", pDosHeader->e_cparhdr); printf("e_minalloc : 0x%04X\n", pDosHeader->e_minalloc); printf("e_maxalloc : 0x%04X\n", pDosHeader->e_maxalloc); printf("e_ss : 0x%04X\n", pDosHeader->e_ss); printf("e_sp : 0x%04X\n", pDosHeader->e_sp); printf("e_csum : 0x%04X\n", pDosHeader->e_csum); printf("e_ip : 0x%04X\n", pDosHeader->e_ip); printf("e_cs : 0x%04X\n", pDosHeader->e_cs); printf("e_lfarlc : 0x%04X\n", pDosHeader->e_lfarlc); printf("e_ovno : 0x%04X\n", pDosHeader->e_ovno); printf("e_res : "); for(i = 0; i < 4; i++) printf("0x%X, ", pDosHeader->e_res[i]); printf("\ne_oemid : 0x%04X\n", pDosHeader->e_oemid); printf("e_oeminfo : 0x%04X\n", pDosHeader->e_oeminfo); printf("e_res2 : "); for(i = 0; i < 10; i++) printf("0x%X, ", pDosHeader->e_res2[i]); printf("\ne_lfanew : 0x%04X\n", pDosHeader->e_lfanew); } // 顯示 IMAGE_FILE_HEADER 信息 void PrintFileHeader(IMAGE_FILE_HEADER FileHeader) { printf("Machine : 0x%04X\n", FileHeader.Machine); printf("NumberOfSections : 0x%04X\n", FileHeader.NumberOfSections); printf("TimeDateStamp : 0x%08X\n", FileHeader.TimeDateStamp); printf("PointerToSymbolTable : 0x%08X\n", FileHeader.PointerToSymbolTable); printf("NumberOfSymbols : 0x%08X\n", FileHeader.NumberOfSymbols); printf("SizeOfOptionalHeader : 0x%04X\n", FileHeader.SizeOfOptionalHeader); printf("Characteristics : 0x%04X\n", FileHeader.Characteristics); } // 顯示 IMAGE_OPTIONAL_HEADER 信息 void PrintOptionalHeader(IMAGE_OPTIONAL_HEADER OptHeader) { // Standard fields. printf("Magic : 0x%04X\n", OptHeader.Magic); printf("MajorLinkerVersion : 0x%02X\n", OptHeader.MajorLinkerVersion); printf("MinorLinkerVersion : 0x%02X\n", OptHeader.MinorLinkerVersion); printf("SizeOfCode : 0x%08X\n", OptHeader.SizeOfCode); printf("SizeOfInitializedData : 0x%08X\n", OptHeader.SizeOfInitializedData); printf("SizeOfUninitializedData : 0x%08X\n", OptHeader.SizeOfUninitializedData); printf("AddressOfEntryPoint : 0x%08X\n", OptHeader.AddressOfEntryPoint); printf("BaseOfCode : 0x%08X\n", OptHeader.BaseOfCode); printf("BaseOfData : 0x%08X\n", OptHeader.BaseOfData); // NT additional fields. printf("ImageBase : 0x%08X\n", OptHeader.ImageBase); printf("SectionAlignment : 0x%08X\n", OptHeader.SectionAlignment); printf("FileAlignmen : 0x%08X\n", OptHeader.FileAlignment); printf("MajorOperatingSystemVersion : 0x%04X\n", OptHeader.MajorOperatingSystemVersion); printf("MinorOperatingSystemVersion : 0x%04X\n", OptHeader.MinorOperatingSystemVersion); printf("MajorImageVersion : 0x%04X\n", OptHeader.MajorImageVersion); printf("MinorImageVersion : 0x%04X\n", OptHeader.MinorImageVersion); printf("MajorSubsystemVersion : 0x%04X\n", OptHeader.MajorSubsystemVersion); printf("MinorSubsystemVersion : 0x%04X\n", OptHeader.MinorSubsystemVersion); printf("Win32VersionValue : 0x%08X\n", OptHeader.Win32VersionValue); printf("SizeOfImage : 0x%08X\n", OptHeader.SizeOfImage); printf("SizeOfHeaders : 0x%08X\n", OptHeader.SizeOfHeaders); printf("CheckSum : 0x%08X\n", OptHeader.CheckSum); printf("Subsystem : 0x%04X\n", OptHeader.Subsystem); printf("DllCharacteristics : 0x%04X\n", OptHeader.DllCharacteristics); printf("SizeOfStackReserve : 0x%08X\n", OptHeader.SizeOfStackReserve); printf("SizeOfStackCommit : 0x%08X\n", OptHeader.SizeOfStackCommit); printf("SizeOfHeapReserve : 0x%08X\n", OptHeader.SizeOfHeapCommit); printf("SizeOfHeapCommit : 0x%08X\n", OptHeader.SizeOfHeapCommit); printf("LoaderFlags : 0x%08X\n", OptHeader.LoaderFlags); printf("NumberOfRvaAndSizes : 0x%08X\n", OptHeader.NumberOfRvaAndSizes); } // 顯示 IMAGE_DATA_DIRECTORY 信息 void PrintDataDircrory(IMAGE_DATA_DIRECTORY DataDirectory) { printf(" 0x%08X 0x%08X", DataDirectory.VirtualAddress, DataDirectory.Size); } // 顯示 IMAGE_SECTION_HEADER 信息 void PrintSectionHeader(IMAGE_SECTION_HEADER *pSectionHeader) { if(pSectionHeader->Name) printf("Name : %s\n", pSectionHeader->Name); else printf("Name : NULL\n"); printf("Misc.VirtualSize : 0x%08X\n", pSectionHeader->Misc.VirtualSize); printf("VirtualAddress : 0x%08X\n", pSectionHeader->VirtualAddress); printf("SizeOfRawData : 0x%08X\n", pSectionHeader->SizeOfRawData); printf("PointerToRawData : 0x%08X\n", pSectionHeader->PointerToRawData); printf("PointerToRelocations : 0x%08X\n", pSectionHeader->PointerToRelocations); printf("PointerToLinenumbers : 0x%08X\n", pSectionHeader->PointerToLinenumbers); printf("NumberOfRelocations : 0x%04X\n", pSectionHeader->NumberOfRelocations); printf("NumberOfLinenumbers : 0x%04X\n", pSectionHeader->NumberOfLinenumbers); printf("Characteristics : 0x%08X\n", pSectionHeader->Characteristics); }
照例貼出本程序的分析結果(受字數限制,只貼了今天新增的):數組
IMAGE_IMPORT_DESCRIPTOR : 59 SN DllName FunName 00 KERNEL32.dll DeleteCriticalSection 01 KERNEL32.dll EnterCriticalSection 02 KERNEL32.dll FreeLibrary 03 KERNEL32.dll GetCurrentProcess 04 KERNEL32.dll GetCurrentProcessId 05 KERNEL32.dll GetCurrentThreadId 06 KERNEL32.dll GetLastError 07 KERNEL32.dll GetModuleHandleA 08 KERNEL32.dll GetProcAddress 09 KERNEL32.dll GetStartupInfoA 10 KERNEL32.dll GetSystemTimeAsFileTime 11 KERNEL32.dll GetTickCount 12 KERNEL32.dll InitializeCriticalSection 13 KERNEL32.dll LeaveCriticalSection 14 KERNEL32.dll LoadLibraryA 15 KERNEL32.dll QueryPerformanceCounter 16 KERNEL32.dll SetUnhandledExceptionFilter 17 KERNEL32.dll Sleep 18 KERNEL32.dll TerminateProcess 19 KERNEL32.dll TlsGetValue 20 KERNEL32.dll UnhandledExceptionFilter 21 KERNEL32.dll VirtualProtect 22 KERNEL32.dll VirtualQuery 23 msvcrt.dll __dllonexit 24 msvcrt.dll __getmainargs 25 msvcrt.dll __initenv 26 msvcrt.dll __lconv_init 27 msvcrt.dll __set_app_type 28 msvcrt.dll __setusermatherr 29 msvcrt.dll _acmdln 30 msvcrt.dll _amsg_exit 31 msvcrt.dll _cexit 32 msvcrt.dll _fmode 33 msvcrt.dll _initterm 34 msvcrt.dll _iob 35 msvcrt.dll _lock 36 msvcrt.dll _onexit 37 msvcrt.dll _unlock 38 msvcrt.dll abort 39 msvcrt.dll calloc 40 msvcrt.dll exit 41 msvcrt.dll fclose 42 msvcrt.dll fopen 43 msvcrt.dll fprintf 44 msvcrt.dll fread 45 msvcrt.dll free 46 msvcrt.dll fseek 47 msvcrt.dll ftell 48 msvcrt.dll fwrite 49 msvcrt.dll malloc 50 msvcrt.dll memset 51 msvcrt.dll memcpy 52 msvcrt.dll printf 53 msvcrt.dll puts 54 msvcrt.dll signal 55 msvcrt.dll strcpy 56 msvcrt.dll strlen 57 msvcrt.dll strncmp 58 msvcrt.dll vfprint