萌新逆向?qū)W習(xí)筆記——IAT鉤取

前言

在上一篇文章中介紹了以模擬調(diào)試器的方式來對特定的API地址進(jìn)行鉤取。而這次本篇文章介紹的是修改IAT來同樣達(dá)到鉤取API的目的,這也是一種“劫持”API的方法。

本文為個人學(xué)習(xí)《逆向工程核心原理》書籍的學(xué)習(xí)筆記,如果想深入學(xué)習(xí)逆向工程,推薦去觀摩一下該本書籍。因為它實在是十分的易懂。

原理

當(dāng)程序調(diào)用函數(shù)的時候,它會使用函數(shù)所在模塊名稱進(jìn)行一個查表操作。這個表就是俗稱的IID(IMAGE_IMPORT_DESCRIPTOR)。表上記載著程序調(diào)用目標(biāo)函數(shù)的地址又稱為IAT。待查詢到目標(biāo)函數(shù)地址后,程序才會真正的通過這個函數(shù)地址來進(jìn)行函數(shù)調(diào)用:

1.png

而我們只要修改IID中記錄的對應(yīng)的函數(shù)地址IAT,使它指向我們自己定義的另一個MessageBoxA,這樣函數(shù)查表返回的地址就不是原來程序中定義的地址,而是我們修改后指向自己定義函數(shù)的地址了,這就完成了一次IAT“劫持”。

1.png

實現(xiàn)思路

整體思路:將修改函數(shù)地址的實現(xiàn),以及新函數(shù)的實現(xiàn)寫在一個DLL里,再編寫一個注入器,將該DLL注入到目標(biāo)程序中。DLL注入成功后便會修改IAT函數(shù)地址,達(dá)到IAT鉤取的目的。

細(xì)節(jié)思路:主要是如何找到該程序的IID里對應(yīng)的IAT。我們可以先使用getModuleHandle(NULL)函數(shù)找到當(dāng)前被注入程序的基址pfile,該基址即是指向當(dāng)前PE文件DOS頭的起始地址。通過DOS頭結(jié)構(gòu)體的e_lfanew屬性,獲取到NT頭的虛擬內(nèi)存空間的偏移量RVA,將該值加上基址pfile即可得到NT頭的虛擬內(nèi)存地址。而最后通過相關(guān)的屬性操作,就可以從NT頭中獲取對應(yīng)的IAT地址。

實踐

獲取IID:

void findIID() {
    // 找到IID所在的內(nèi)存地址
    //獲取當(dāng)前被注入DLL的程序模塊基址
    pfile = (PBYTE)GetModuleHandle(NULL);  
    //轉(zhuǎn)換成DOS頭
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)pfile;  
    //通過DOS頭的e_lfanew找到NT頭
    PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(pfile + dosHeader->e_lfanew); 
    //NT頭中的可選NT頭里的DataDirectory數(shù)組,記載著IID的虛擬內(nèi)存偏移地址
    IMAGE_DATA_DIRECTORY IATSection = (IMAGE_DATA_DIRECTORY)(ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);
    //加上基址,獲取到IID虛擬內(nèi)存地址
    currentIID = PIMAGE_IMPORT_DESCRIPTOR(pfile + IATSection.VirtualAddress);
}

修改IAT

LPCSTR dialogDLL = "user32.dll";
void hookIAT() {
    // 修改IAT
    while (currentIID)
    {
        //去IID中的Name字段,其為IAT對應(yīng)的模塊名稱虛擬內(nèi)存地址偏移量,如MessageBoxA是在系統(tǒng)函數(shù)user32.dll中
        DWORD RVAName = currentIID->Name;
        char* VAName = (char*)(pfile + RVAName);
        //對比是否為user32.dll
        if (strcmp(VAName, dialogDLL))
        {
            // 符合條件,取出IAT列表
            DWORD RVAIAT = currentIID->FirstThunk;
            PIMAGE_THUNK_DATA pthunk = (PIMAGE_THUNK_DATA)(pfile + RVAIAT);
            //循環(huán)IAT函數(shù)地址列表,找到MessageBoxA函數(shù)地址
            while (pthunk->u1.Function)
            {
                DWORD funAddress = pthunk->u1.Function;
                if (funAddress == (DWORD)oriAddress)
                {
                    //修改IAT
                    DWORD oldProtect = NULL;
                    VirtualProtect((LPVOID)&pthunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &oldProtect);
                    pthunk->u1.Function = (DWORD)hookMessageBox;
                    VirtualProtect((LPVOID)&pthunk->u1.Function, 4, oldProtect, &oldProtect);
                    break;
                }
                pthunk++;
            }
            return;
        }
        currentIID++;
    }
}

結(jié)果

例中使用的依舊是上篇文章中編寫的可憐的彈窗程序:


2.png
3.png

結(jié)論

IAT鉤取具有一定的局限性,我們只能鉤取IAT中存在的函數(shù),而不能無中生有。而我們定義的函數(shù)簽名也必須符合鉤取的原函數(shù)前的簽名;如參數(shù)類型,函數(shù)返回等須相同。

總的來說IAT鉤取還是比較簡單快速的,其難點是尋找IAT的過程。倘若我們對PE文件結(jié)構(gòu)毫無所知,不能理解DOS頭,NT頭等重要結(jié)構(gòu),想必就要多費(fèi)一些心思了吧。

源碼及例子

https://share.weiyun.com/iCyzPWuM

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容