要實現(xiàn)修改導(dǎo)入段來攔截API必須對PE文件格式有很好的了解。網(wǎng)上關(guān)于它的資料鋪天蓋地,,自己搜吧,。此處不打算介紹。
為了修改要攔截的函數(shù)在導(dǎo)入段的地址,,首先要獲得PE文件導(dǎo)入段的地址,。這可以調(diào)用ImageDirectoryEntryToData.函數(shù)。
- PVOID WINAPI ImageDirectoryEntryToData(
-
- __in PVOID Base,
-
- __in BOOLEAN MappedAsImage,
-
- __in USHORT DirectoryEntry,
-
- __out PULONG Size );
Base為要獲得導(dǎo)入段所在模塊的基地址,。它一般是GetModuleHandle的返回值,。
MappedAsImage,它為true時,,系統(tǒng)將該模塊以映像文件的形式映射,,否則以數(shù)據(jù)文件的形式映射。
DirectoryEntry,,要獲得的段的索引,。此函數(shù)不僅僅能夠獲得導(dǎo)入段的地址,根據(jù)此索引的不同,,該函數(shù)返回對應(yīng)段的地址,。此處我們使用 IMAGE_DIRECTORY_ENTRY_IMPORT表示我們要獲得導(dǎo)入段的地址。
Size返回表項的大小。注意其類型,。
看代碼:
- ULONG size;
-
- HMODULE hModule=GetModuleHandle(NULL);
-
- PIMAGE_IMPORT_DESCRIPTOR pImport=
-
- ImageDirectoryEntryToData(hModule,true,IMAGE_DIRECTORY_ENTRY_IMPORT,&size);
-
- while(pImport->FirstThunk)
-
- {
-
- int i=0;
-
- char *ModuleName=(char*)((BYTE*)hModule+pImport->Name);
-
- PIMAGE_THUNK_DATA pThunk=(PIMAGE_THUNK_DATA)
-
- ((BYTE*)hModule+pImport->FirstThunk);
-
- while(pThunk->u1.Function)
-
- {
-
- char*Func=(char*)((BYTE*)hModule+pThunk->u1.AddressOfData+2);
-
- If(Func=="MessageBoxA")
-
- {
-
- MessageBox("找到函數(shù)");
-
- }
-
- }
-
- }
IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu)是導(dǎo)入段的基本結(jié)構(gòu),,導(dǎo)入段是由此類型組成的數(shù)組構(gòu)成,每個DLL對應(yīng)數(shù)組中的一項,。數(shù)組的最后一項為NULL,。每一項中有兩個關(guān)鍵成員,指出IMAGEA_THUNK_DATA類型的數(shù)組的偏移量,,這兩個數(shù)組分別列出了從此DLL導(dǎo)入的函數(shù)名稱,,以及它它們在本進(jìn)程地址空間的RVA。
以下是其定義:
- typedef struct _IMAGE_IMPORT_DESCRIPTOR {
- union {
- DWORD Characteristics;
- DWORD OriginalFirstThunk;// 指向一個 IMAGE_THUNK_DATA 結(jié)構(gòu)數(shù)組的RVA
- }
- DWORD TimeDateStamp;// 文件生成的時間
- DWORD ForwarderChain;// 這個數(shù)據(jù)一般為0,,可以不關(guān)心
- DWORD Name; // RVA,,指向DLL名字的指針,ASCII字符串
- DWORD FirstThunk; //指向一個 IMAGE_THUNK_DATA 結(jié)構(gòu)數(shù)組的RVA,這個數(shù)據(jù)與IAT所指向的地址一致
- }IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR
OriginalFirstThunk 和FirstThunk是此結(jié)構(gòu)中的關(guān)鍵成員,。它們指出這兩個IMAGE_THUNK_DATA類型的數(shù)組,。
Name 是指向DLL名稱的指針。
pImport指向模塊的導(dǎo)入段,,可以使用它來訪問IMAGE_IMPORT_DESCRIPTOR結(jié)構(gòu)的每一個成員,。
注意:以上所有偏移量都是相對于模塊基地址的,因此在實際使用中一定要加上模塊的地址,。
OriginalFirstThunk指向NAT,,NAT列出了從該DLL導(dǎo)入的所有導(dǎo)入函數(shù)的名稱。
FirstThunk 指向IAT,,IAT列出了與NAT相對應(yīng)的導(dǎo)入函數(shù)的地址,。
看此結(jié)構(gòu)定義:
- typedef struct _IMAGE_THUNK_DATA32 {
-
- union {
-
- PBYTE ForwarderString;
-
- PDWORD Function;
-
- DWORD Ordinal;
-
- PIMAGE_IMPORT_BY_NAME AddressOfData;
-
- } u1;
-
- } IMAGE_THUNK_DATA32;
上述代碼中,外層循環(huán)while(pImport->FirstThunk)用于遍歷所有DLL,,內(nèi)層循環(huán)用于遍歷查找每個DLL中的導(dǎo)入函數(shù)名稱,。我們以查找MessageBoxA函數(shù)為例來做介紹。此處我們通過對NAT進(jìn)行掃描,,將其與我們給出的字符串類型的函數(shù)名MessageBoxA進(jìn)行比較,。注意要使用大小寫不敏感的函數(shù):
- int WINAPI lstrcmpi(
- __in LPCTSTR lpString1,
- __in LPCTSTR lpString2
- );
這是一種方法,,另一種方法是比較函數(shù)地址,。我們可以對IAT進(jìn)行掃描,將得到的地址與我們給出的函數(shù)地址進(jìn)行比較,。這兩種方法是經(jīng)常使用的方法,。
下面給出使用地址比較的方法。
- HMODULE hModule=GetModuleHandle(NULL);
-
- ULONG size;
-
- PIMAGE_IMPORT_DESCRIPTOR pImport=(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData
-
- (hModule,true,IMAGE_DIRECTORY_ENTRY_IMPORT,&size);
-
- UpdateData();
- //獲得MessageBoxA的地址,。
- PROC pfnOld=GetProcAddress(GetModuleHandle("User32.dll"),"MessageBoxA");
-
-
- while(pImport->FirstThunk)
-
- {
-
- char *ModuleName=(char*)((BYTE*)hModule+pImport->Name);
-
- PIMAGE_THUNK_DATA pThunk=(PIMAGE_THUNK_DATA)((BYTE*)hModule+pImport->FirstThunk);
-
- while(pThunk->u1.Function)
-
- {
-
- PROC *ppfn=(PROC*)&(pThunk->u1.Function);
-
- if(*ppfn==pfnOld)pfnOld為要查詢的函數(shù)地址,。
-
- {
-
- MessageBox("找到給定的函數(shù)");
-
- return ;
-
- }
-
- pThunk++;
-
- }
-
- pImport++;
-
- }
當(dāng)然以上僅僅是找到相應(yīng)的函數(shù)。要用我們期望的函數(shù)代替它,,還需要我們自己定義函數(shù),。
此處有一點要特別注意,當(dāng)我們對某函數(shù)進(jìn)行攔截時,,要替換的函數(shù)必須與被替換的函數(shù)的原型完全相同,。
比如此時我們要對MessageBoxA進(jìn)行攔截,我們定義自己的函數(shù)的原型與MessageBoxA原型是完全一致的:
- int WINAPI MyMessageBoxA(HWND hWnd,LPSTR str,LPSTR caption ,UINT type)
-
- {
-
- //定義函數(shù)的行為,。
-
- }
我們可以在函數(shù)中調(diào)用被攔截的函數(shù),,也可以不對攔截的函數(shù)進(jìn)行調(diào)用而進(jìn)行其他操作。
注意:在攔截后,,原來的函數(shù)地址已經(jīng)被我們自定義的函數(shù)的地址覆蓋,,如果之后需要調(diào)用被攔截的函數(shù),就必須在覆蓋之前保存被攔截函數(shù)的地址,。如果我們沒有保存而直接調(diào)用,,調(diào)用的只是替換后的函數(shù)。
以下代碼將對MessageBox的調(diào)用進(jìn)行攔截,,并用我們的自定義函數(shù)MyMessageBox替代,。
g_addr用于存儲攔截之前MessageBox的地址
- Typedef int (WINAPI *PFNMESSAGEBOX(HWND,LPSTR,LPSTR,UINT);//定義函數(shù)指針。
-
- PFNMESSAGEBOX g_addr=(PFNMESSAGEBOX)MessageBoxA;//存儲MessageBoxA的地址,。
-
- //自定義的函數(shù),。
-
- int WINAPI MyMessage(HWND hWnd,LPSTR a,LPSTR b,UINT type)
-
- {
-
- return ((PFNMESSAGEBOX)g_addr)(hWnd,a,"替換后的函數(shù)!?",MB_YESNO);
-
- }
-
- //對 MessageBox進(jìn)行攔截,,用MyMessageBox進(jìn)行替換,。
-
- Void ReplaceOneFunc(PCSTR ModuleName,PFNMESSAGEBOX pfnOld,PFNMESSAGEBOX pfnNew)
-
- {
-
- pfnOld=(PFNMESSAGEBOX)MessageBoxA;
-
- pfnNew=(PFNMESSAGEBOX)MyMessageBox;
-
- HMODULE hModule=GetModuleHandle(NULL);
-
- ULONG size;
-
- PIMAGE_IMPORT_DESCRIPTOR pImport=(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData
-
- (hModule,true,IMAGE_DIRECTORY_ENTRY_IMPORT,&size);
-
- UpdateData();
-
- while(pImport->FirstThunk)
-
- {
-
- char *ModuleName=(char*)((BYTE*)hModule+pImport->Name);
-
- PIMAGE_THUNK_DATA pThunk=(PIMAGE_THUNK_DATA)((BYTE*)hModule+pImport->FirstThunk);
-
- while(pThunk->u1.Function)
-
- {
-
- PFNMESSAGEBOX *ppfn=(PFNMESSAGEBOX*)&(pThunk->u1.Function);
-
- if(*ppfn==pfnOld)
-
- {
-
- MessageBox("找到函數(shù)!恭喜你,!");
-
- SIZE_T num;
-
- WriteProcessMemory(GetCurrentProcess(),ppfn,&pfnNew,4,&num);
-
- MessageBox(NULL,"現(xiàn)在調(diào)用的是替換后的API!","",MB_OK);
-
- ((PFNMESSAGEBOX)g_addr)(NULL,"哈哈,,現(xiàn)在終于輪到你了","",MB_OK);
-
- return ;
-
- }
-
- pThunk++;
-
- }
-
- pImport++;
-
- }
-
- MessageBox("沒有找到該函數(shù)!");
-
- }
WriteProcessMemory(GetCurrentProcess(),ppfn,&pfnNew,4,&num);
此函數(shù)用于修改內(nèi)存。實現(xiàn)用MyMessagebox的地址替換MessageBox的地址,。關(guān)于此函數(shù)的功能請參考MSDN,。
((PFNMESSAGEBOX)g_addr)(NULL,"哈哈,現(xiàn)在終于輪到你了","",MB_OK);
此句用于調(diào)用在攔截之前MessageBox的地址,。此處的調(diào)用是調(diào)用的MessageBox函數(shù),,而不是MyMessageBox函數(shù)。
至此關(guān)于修改導(dǎo)入段攔截API介紹完畢,。
以上僅僅是個人總結(jié),。參考自《windows核心編程》第五版 ,《加密與解密》第三版 段鋼著,,《windows程序設(shè)計》第二版王艷平著,。如有紕漏,請不吝賜教。