typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; // 未使用,總為0 DWORD TimeDateStamp; // 文件創建時間戳 WORD MajorVersion; // 未使用,總為0 WORD MinorVer... ...
typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; // 未使用,總為0
DWORD TimeDateStamp; // 文件創建時間戳
WORD MajorVersion; // 未使用,總為0
WORD MinorVersion; // 未使用,總為0 DWORD Name; // 指向一個代表此 DLL名字的 ASCII字元串的 RVA DWORD Base; // 函數的起始序號 DWORD NumberOfFunctions; // 導出函數的總數
DWORD NumberOfNames; // 以名稱方式導出的函數的總數
DWORD AddressOfFunctions; // 指向輸出函數地址的RVA DWORD AddressOfNames; // 指向輸出函數名字的RVA DWORD AddressOfNameOrdinals; // 指向輸出函數序號的RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
AddressOfFunctions 所指向內容是以 4 位元組為一個單位的數組元素,每個元素代表函數入口
AddressOfNames 所指向內容是以 4 位元組為一個單位的數組元素,每個元素代表一個指向字元串的 RVA
AddressOfNamesOrdinals 所指向內容是以 2 位元組為一個單位的數組元素,每個元素代表對應名字在 AddressOfFunctions 中的序號數。
AddressOfNames 和 AddressOfNamesOrdinals 的數目肯定是一樣的,不是一樣那麼就出錯了。
主要要掌握兩種尋找函數入口地址的方法:
A. 從序號查找函數入口地址
1. 定位到PE 文件頭
2. 從PE 文件頭中的 IMAGE_OPTIONAL_HEADER32 結構中取出數據目錄表,並從第一個數據目錄中得到導出表的RVA
3. 從導出表的 Base 欄位得到起始序號
4. 將需要查找的導出序號減去起始序號Base,得到函數在入口地址表中的索引,檢測索引值是否大於導出表的 NumberOfFunctions 欄位的值,如果大於後者的話,說明輸入的序號是無效的
5. 用這個索引值在 AddressOfFunctions 欄位指向的導出函數入口地址表中取出相應的項目,這就是函數入口地址的RVA 值,當函數被裝入記憶體的時候,這個RVA 值加上模塊實際裝入的基地址,就得到了函數真正的入口地址
B. 從函數名稱查找入口地址
我想通的地方,記錄下來:用函數名來查找的話,Base 的值現在沒有任何意義
1. 首先得到導出表的地址
2. 從導出表的 NumberOfNames 欄位得到已命名函數的總數,並以這個數字作為迴圈的次數來構造一個迴圈,從 AddressOfNames 欄位指向得到的函數名稱地址表的第一項開始,在迴圈中將每一項定義的函數名與要查找的函數名相比較,如果沒有任何一個函數名是符合的,表示文件中沒有指定名稱的函數。
3. 如果某一項定義的函數名與要查找的函數名符合,那麼記下這個函數名在字元串地址表中的索引值,然後在AddressOfNamesOrdinals 指向的數組中以同樣的索引值取出數組項的值,我們這裡假設這個值是 x
4. 最後,以 x 的值作為索引值在 AddressOfFunctions 欄位指向的函數入口地址表中獲取 RVA 。此 RVA 就是函數的入口地址。
附上圖片: