標籤:
EAT與IAT比較類似,我相信會IAT的肯定很多,起初我想寫在C#上面 不過與
C# 遍曆DLL匯出函數 的方法很相似,只是兩者在記憶體中的映射方式不同而已
Heh,首先我們需要把DLL映射到地址記憶體空間去 否則沒有辦法去置換函數,
當然EAT有一些缺點,它必須在軟體調用GetProcAddress函數之前替換DLL中
的函數,所以則出現了對GetProcAddress函數的一個Hook,否則只可改變
GetProcAddress返回的內容、是不是感到很惆悵
EAT全稱為“Export address table”其核心則是利用PE與DLL的一些相關特性
實現的一個技術 首先我們通過LoadLibraryA函數把一個有效DLL映射到內
存中,PE檔案中第一個位元組是MS-DOS資訊頭即IMAGE_DOS_HEADER
IMAGE_NT_HEADER = (pDosHearder + pDosHearder->e_lfanew);
// IMAGE_NT_HEADER::OptionalHeader
pOptionalHeader = (pDosHearder + pDosHearder->e_lfanew + 24);
上面是x86的一個OptionalHeader的對稱,這裡是不想再多做一次轉換直接
跳過去會很快捷、所以加上24但恰好指向OptionalHeader、而我們只需要
它其它的對我們而言根本沒有任何用、所以跳過就可以
#include "stdafx.h"#include <Windows.h>BOOL EATHook(LPCSTR strLibraryName, LPCSTR strMethodName, PVOID newMethodAddress){HMODULE hRemoteModule = LoadLibraryA(strLibraryName);PIMAGE_DOS_HEADER pDosHearder = (PIMAGE_DOS_HEADER)hRemoteModule;PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)hRemoteModule + pDosHearder->e_lfanew + 24);PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pDosHearder +pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);LPDWORD pAddressOfNames = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfNames);LPDWORD pAddressOfFunctions = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfFunctions);PUSHORT pAddressOfNameOrdinals = (PUSHORT)((DWORD)hRemoteModule + pExportDirectory->AddressOfNameOrdinals);for (int i = 0; i < pExportDirectory->NumberOfNames; i++){LPCSTR strInsideName = (LPCSTR)((DWORD)hRemoteModule + pAddressOfNames[i]);if (_stricmp(strInsideName, strMethodName) == 0){DWORD pflOldProtect;VirtualProtect(&pAddressOfFunctions[*pAddressOfNameOrdinals], 0x1000, PAGE_READWRITE, &pflOldProtect);pAddressOfFunctions[*pAddressOfNameOrdinals] = (DWORD)newMethodAddress - (DWORD)hRemoteModule;break;}pAddressOfNameOrdinals++;}return FALSE;}
既然是EAT,我們看第一個字母全名為Export那麼我們肯定要去找包含匯出的DLL函數的
PE中的資料目錄,恰好有那麼一個IMAGE_DIRECTORY_ENTRY_EXPORT但是拿出來
的地址是RVA / 相對虛擬位址所以你必須要ToVA / 到虛擬位址、但是DLL已經被映射到虛
擬記憶體中 所以我們不需要使用RvaToVa、我們只需要
(DWORD)pDosHearder + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
(DWORD)hRemoteModule + DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
則可以得到,PIMAGE_EXPORT_DIRECTORY pExportDirectory函數匯出目錄,但是我
們得到了它卻不可以耍花架子,我們還有繼續下面的代碼否則沒法混、
LPDWORD pAddressOfNames = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfNames);LPDWORD pAddressOfFunctions = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfFunctions);PUSHORT pAddressOfNameOrdinals = (PUSHORT)((DWORD)hRemoteModule + pExportDirectory->AddressOfNameOrdinals);
pAddressOfNames // 匯出函數名
pAddressOfFunctions // 匯出函數地址
pAddressOfNameOrdinals // 匯出函數序列
上面定義三種不同的指標,但是都預先把地址進行了一個處理,這是為了方便後面的使用
而不必一直加上(DOWRD)hRemoteModule那樣子會很累的、
pExportDirectory->NumberOfNames // 匯出函數計數(Length)
LPCSTR strInsideName = (LPCSTR)((DWORD)hRemoteModule + pAddressOfNames[i]);
上面是擷取DLL匯出的函數名,pAddressOfNames[i] 返回的地址為虛擬相對位址(Rva)
在這裡只需要如下去做、則可以得到DLL匯出的函數名是什麼,當然這個是c_str風格字
符串,你懂的‘\0‘結尾,_strlen對其有效,不過在本代碼中也用不到_strlen、
下面是首先比較兩個字串是否相等,主要看看需要具體去Hook那個函數
for (int i = 0; i < pExportDirectory->NumberOfNames; i++){LPCSTR strInsideName = (LPCSTR)((DWORD)hRemoteModule + pAddressOfNames[i]);if (_stricmp(strInsideName, strMethodName) == 0){DWORD pflOldProtect;VirtualProtect(&pAddressOfFunctions[*pAddressOfNameOrdinals], 0x1000, PAGE_READWRITE, &pflOldProtect);pAddressOfFunctions[*pAddressOfNameOrdinals] = (DWORD)newMethodAddress - (DWORD)hRemoteModule;break;}pAddressOfNameOrdinals++;}
&pAddressOfFunctions[*pAddressOfNameOrdinals] 從上面的代碼你應該看出了端倪
對的,DLL函數匯出的地址你不可以直接通過&pAddressOfFunctions[i]解決問題,因
為在PE中,它是通過Ordinals定位函數位置,但是它與i=0; i++是相序的、有點繞哈
VirtualProtect(&pAddressOfFunctions[*pAddressOfNameOrdinals],
0x1000, PAGE_READWRITE, &pflOldProtect);
上面是修改函數的記憶體保護,0x1000沒有太大意義、填4 / 8也是可以的
pAddressOfFunctions[*pAddressOfNameOrdinals] =
(DWORD)newMethodAddress - (DWORD)hRemoteModule;
好吧,這裡填寫相對虛擬位址、指的是新的函數地址與這個模組的地址做一個加減
不管返回的是負數還是正數都可以,沒有那麼多講究、後面這句話也是必要的
pAddressOfNameOrdinals++; 你可以任性的把使用 *pAddressOfNameOrdinals
修改為pAddressOfNameOrdinals[i]但是絕對不可以不寫,這是原則性上的問題、
HANDLE WINAPI CB_GetModuleHandleW(LPCTSTR strLibraryName){return NULL;}
我們寫一個Hook的處理函數,GetModuleHandleW函數的一個處理函數,什麼都不
用寫,只需要打個斷點看看有沒有執行就可以了,因為沒有太大的意義重點在這裡
int _tmain(int argc, _TCHAR* argv[]){EATHook("kernel32.dll", "GetModuleHandleW", CB_GetModuleHandleW);LPDWORD pfnGetModuleHandleW = (LPDWORD)GetProcAddress(GetModuleHandleA("kernel32"), "GetModuleHandleW");_asm{push 0call pfnGetModuleHandleW}return 0;}
上面是利用EAT先把GetModuleHandleW函數進行掛鈎,然後通過GetProcAddress
函數得到pfnGetModuleHandleW,為了方便不想使用函數指標,直接嵌入兩行彙編
代碼進去直接解決問題,然後F5調試,如果CB_GetModuleHandleW函數被執行 那
麼恭喜你成功了,如果沒有說明你出錯了、哈哈
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
C++ EAT / Hook