日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

导入表编程-枚举导入表

發(fā)布時間:2025/6/17 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 导入表编程-枚举导入表 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

導(dǎo)入表編程-枚舉導(dǎo)入表

思路:

首先導(dǎo)入表的RVA地址,就在optional?HeaderDataDirectory的第二個元素中。通過它我們定位到導(dǎo)入表。

    導(dǎo)入表類似一個二級索引。一級是一個模塊目錄(IMAGE_IMPORT_DESCRIPTOR數(shù)組,這里把目錄理解為一個以全0字節(jié)為結(jié)束的數(shù)組),它的每個元素代表了一個DLL。二級是導(dǎo)入地址表IAT(即?IMAGE_THUNK_DATA?數(shù)組,一個指針數(shù)組),每個元素指向一個?IMAGE_IMPORT_BY_NAME結(jié)構(gòu)(該結(jié)構(gòu)含有一個函數(shù)序號和一個函數(shù)名稱字符串)。

    總結(jié)一下,我們的定位過程:

    (1)通過?NtHeaders.OptionalHeader.DataDirectory[1].VirtualBase?-->?定位到導(dǎo)入表(IID?Table)

    (2)遍歷每個?IID,直到遇到全0為止。

        通過?IID.Name?->?定位到?DLL?名字。

        通過?IID.OriginalFirstThunk?或者?FirstThunk?->?定位到IAT?(?image_thunk_data32[]?)

          遍歷指針目錄,知道遇到NULL為止。

            通過?thunk_data.AddressOfData?->?定位到一個?IMAGE_IMPORT_BY_NAME?的地址,再根據(jù)它尋址到真正的函數(shù)序號和函數(shù)名稱。

?

代碼:

// C++Test.cpp : 定義控制臺應(yīng)用程序的入口點。 //#include "stdafx.h" #include "stdafx.h" #include <stdio.h> #include <windows.h> #include <Dbghelp.h> //ImageRvaToVa #pragma comment(lib, "Dbghelp.lib")int main(int argc, char* argv[]){ int i, j; HANDLE hFile = CreateFile(L"C:\\A.exe", //PE文件名 GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if(hFile == INVALID_HANDLE_VALUE){ printf("Create File Failed.\n"); return 0; }HANDLE hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);if (hFileMapping == NULL || hFileMapping == INVALID_HANDLE_VALUE) { printf("Could not create file mapping object (%d).\n", GetLastError()); return 0; }LPBYTE lpBaseAddress = (LPBYTE)MapViewOfFile(hFileMapping, // handle to map object FILE_MAP_READ, 0, 0, 0);if (lpBaseAddress == NULL) { printf("Could not map view of file (%d).\n", GetLastError()); return 0; }PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)(lpBaseAddress + pDosHeader->e_lfanew);//導(dǎo)入表的rva:0x2a000; DWORD Rva_import_table = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;if(Rva_import_table == 0){ printf("no import table!"); goto UNMAP_AND_EXIT; }//這個雖然是內(nèi)存地址,但是減去文件開頭的地址,就是文件地址了 //這個地址可以直接從里面讀取你想要的東西了PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)ImageRvaToVa( pNtHeaders, lpBaseAddress, Rva_import_table, NULL );//減去內(nèi)存映射的首地址,就是文件地址了。。(很簡單吧) printf("FileAddress Of ImportTable: %p\n", ((DWORD)pImportTable - (DWORD)lpBaseAddress));//現(xiàn)在來到了導(dǎo)入表的面前:IMAGE_IMPORT_DESCRIPTOR 數(shù)組(以0元素為終止) //定義表示數(shù)組結(jié)尾的null元素! IMAGE_IMPORT_DESCRIPTOR null_iid; IMAGE_THUNK_DATA null_thunk; memset(&null_iid, 0, sizeof(null_iid)); memset(&null_thunk, 0, sizeof(null_thunk));//每個元素代表了一個引入的DLL。 for(i=0; memcmp(pImportTable + i, &null_iid, sizeof(null_iid))!=0; i++){ //LPCSTR: 就是 const char* LPCSTR szDllName = (LPCSTR)ImageRvaToVa( pNtHeaders, lpBaseAddress, pImportTable[i].Name, //DLL名稱的RVA NULL);//拿到了DLL的名字 printf("-----------------------------------------\n"); printf("[%d]: %s\n", i, szDllName); printf("-----------------------------------------\n");//現(xiàn)在去看看從該DLL中引入了哪些函數(shù) //我們來到該DLL的 IMAGE_TRUNK_DATA 數(shù)組(IAT:導(dǎo)入地址表)前面 PIMAGE_THUNK_DATA32 pThunk = (PIMAGE_THUNK_DATA32)ImageRvaToVa( pNtHeaders, lpBaseAddress, pImportTable[i].OriginalFirstThunk, //【注意】這里使用的是OriginalFirstThunk NULL);for(j=0; memcmp(pThunk+j, &null_thunk, sizeof(null_thunk))!=0; j++){ //這里通過RVA的最高位判斷函數(shù)的導(dǎo)入方式, //如果最高位為1,按序號導(dǎo)入,否則按名稱導(dǎo)入 if(pThunk[j].u1.AddressOfData & IMAGE_ORDINAL_FLAG32){ printf("\t [%d] \t %ld \t 按序號導(dǎo)入\n", j, pThunk[j].u1.AddressOfData & 0xffff); }else{ //按名稱導(dǎo)入,我們再次定向到函數(shù)序號和名稱 //注意其地址不能直接用,因為仍然是RVA! PIMAGE_IMPORT_BY_NAME pFuncName = (PIMAGE_IMPORT_BY_NAME)ImageRvaToVa( pNtHeaders, lpBaseAddress, pThunk[j].u1.AddressOfData, NULL);printf("\t [%d] \t %ld \t %s\n", j, pFuncName->Hint, pFuncName->Name); } } }UNMAP_AND_EXIT: //關(guān)閉文件,句柄。。 UnmapViewOfFile(lpBaseAddress); CloseHandle(hFileMapping); CloseHandle(hFile); getchar(); return 0; }

【注意】在上面的代碼中使用的是?OriginalFirstThunk?,對于沒有事先綁定的PE文件來說,?OriginalFirstThunk?和?FirstThunk?是并行的內(nèi)容相同的數(shù)組。但是對于已經(jīng)經(jīng)過綁定的PE文件來說,?FirstThunk?數(shù)組中的元素會被設(shè)置成真正的函數(shù)地址(VA)!因此如果這時用?FirstThunk?數(shù)組嘗試獲取函數(shù)名稱是得不到的。所以上面的代碼應(yīng)該使用?OriginalFirstThunk?,這樣無論對于綁定還是未綁定的PE文件,都能夠定向到相應(yīng)的函數(shù)名稱。

?

運行結(jié)果:


對應(yīng)源文件信息:

?


?

?

總結(jié)

以上是生活随笔為你收集整理的导入表编程-枚举导入表的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。