IAT Hook

iBinary發表於2019-06-04

IAT hook

一丶IAT

1.什麼是 IAT表.

熟悉PE結構的應該知道.IAT 是匯入表.
其IAT表如下:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            
        DWORD   OriginalFirstThunk;       指向INT表 4個位元組一組.是RVA指向名字跟序號  
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;           
       

    DWORD   ForwarderChain;                 
    DWORD   Name;
    DWORD   FirstThunk;                  在檔案中跟INT表一樣.這是IAT                 
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

我們知道PE有兩種狀態.第一種.在檔案中的狀態. 所以才有 VA 轉 FOA等等的互相轉換.
扯多了.
在檔案狀態. IAT表(firstThunk)跟 INT表一樣.都是指向一個很大的表.這個表裡面是4個位元組進行儲存.儲存的是Rva. 這些RVA分別指向 匯入序號以及以0結尾的字串.

如果在記憶體狀態.則INT表跟上面說的檔案狀態一樣指向 匯入序號.以及匯入的函式名字.
而IAT此時不同了.IAT此時就是儲存著INT指向的匯入函式的地址了.

如果還不理解.看下以前關於IAT部落格.
https://www.cnblogs.com/iBinary/p/9740757.html

如下圖所示:
IAT Hook

其實IAT就是儲存函式地址.

2.怎麼進行HOOK

熟悉了IAT 那麼HOOK就很簡單了.首先你要會解析PE.
原理就是:
1.編寫DLL.注入到你想HOOK的程式中.
2.編寫DLL,DLL裡面獲取你HOOK程式的 ImageBase以及各種頭(DOS,NT,FILE,OPT)
3.DLL 裡面通過OPT的資料目錄第一項.得到匯入表RVA.加上ImageBase定位到匯入表
4.迴圈遍歷匯入表.匯入表是一行04個位元組.最後一項為0
5.通過匯入表找到IAT表.繼續遍歷IAT表.
6.判斷IAT中的函式地址,是否是你要進行HOOK的函式地址.
是: 則進行替換函式地址.比如替換為你的.一定要注入呼叫約定.
不是: 繼續迴圈.
在IAT表中沒找到.說明沒在這個匯入表中.匯入表+1(一個匯入表結構大小)
繼續迴圈 4 5 6步.
說的比較複雜,其實原理很簡單.

首先第一步.準備一個測試程式.測試程式呼叫MessageBoxA.
且載入我們的DLL(當然你編寫的DLL一般是注入的別的程式中.我這裡演示就直接載入自己進行HOOK自己).

測試程式如下:



#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main()
{
    getchar();
    ::MessageBoxA(NULL, "沒有HOOK", NULL, NULL);
    LoadLibrary("IAThook.dll");      //載入HOOK的DLL
    getchar();
    
//HOOK後進行測試的程式.
    __asm
    {
        push 0
        push 0
        push 0
        push 0
        call dword ptr ds : [MessageBoxA] ; 
    }

    __asm
    {
        push 0
        push 0
        push 0
        push 0
        call dword ptr ds : [MessageBoxA] ;
    }

    __asm
    {
        push 0
        push 0
        push 0
        push 0
        call dword ptr ds : [MessageBoxA] ;
    }
    
}

上面的內斂彙編是因為我自己HOOK自己了可以.但是都在第一次呼叫MessageBox函式的時候編譯器會儲存這個MessageBox函式的地址.就算我HOOK完畢了.它再次呼叫還是呼叫的以前的.所以我先改成下面這樣.

HOOK的DLL

// dllmain.cpp : 定義 DLL 應用程式的入口點。
#include "framework.h"
#include <windows.h>


//建立相同函式指標.
typedef int (WINAPI *PfnMsgA)(
    _In_opt_ HWND hWnd,
    _In_opt_ LPCSTR lpText,
    _In_opt_ LPCSTR lpCaption,
    _In_ UINT uType);

PfnMsgA g_OldPfnMsgA = nullptr;


int WINAPI MyMessageBox(_In_opt_ HWND hWnd,_In_opt_ LPCSTR lpText,_In_opt_ LPCSTR lpCaption,_In_ UINT uType)
{

    char szHookText[] = "IBinary -> Iat Hook";
    if (g_OldPfnMsgA != nullptr)
    {
        return g_OldPfnMsgA(hWnd, szHookText, lpCaption, uType);//呼叫以前的
    }
    return 0;
}
void SetIatHook()
{
    MessageBoxA(NULL, "開始進行HOOK", NULL, NULL);
    PVOID pHookAddress = nullptr;
    pHookAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA"); //你要HOOK的函式.
    if (nullptr == pHookAddress)
    {
        OutputDebugString(TEXT("獲取函式地址失敗"));
        MessageBoxA(NULL, "獲取函式地址失敗HOOK", NULL, NULL);

        return;
    }
    g_OldPfnMsgA =(PfnMsgA) pHookAddress; //儲存舊的函式指標.
    //解析PE頭.尋找IAT.

    HMODULE hModImageBase = GetModuleHandle(NULL);//獲取當前的ImagBase
    PIMAGE_DOS_HEADER pDosHead =(PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //獲取DOS頭
    DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew;
    PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp;
    PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)& pNtHead->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)& pNtHead->OptionalHeader;

    //尋找匯出表的位置.
    DWORD dwExportLocal = pOptHead->DataDirectory[1].VirtualAddress; //找到匯出表偏移.
    //定位到匯出表
    dwTemp = (DWORD)GetModuleHandle(NULL) + dwExportLocal;
    PIMAGE_IMPORT_DESCRIPTOR   pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp;
    PIMAGE_IMPORT_DESCRIPTOR   pCurrent = pImport;
    DWORD *pFirstThunk; //匯入表子表,也就是IAT儲存函式地址的表.
    //遍歷匯入表

    while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
    {
        dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);//找到匯入表
        pFirstThunk = (DWORD *)dwTemp; //加上偏移才是真正的匯入表.
        while (*(DWORD*)pFirstThunk != NULL)
        {
            //遍歷子表
            if (*(DWORD*)pFirstThunk == (DWORD)g_OldPfnMsgA)
            {
                //找到要修改的匯入表了//修改記憶體保護屬性.寫入我們新的函式地址.
                DWORD oldProtected;
                VirtualProtect(pFirstThunk,0x1000, PAGE_EXECUTE_READWRITE,&oldProtected);
                dwTemp = (DWORD)MyMessageBox;
                memcpy(pFirstThunk, (DWORD *)&dwTemp,4); //將變數中儲存的函式地址拷貝到匯入表中.
                VirtualProtect(pFirstThunk,0x1000,oldProtected,&oldProtected);
            }
            pFirstThunk++; //繼續遍歷.
        }
        pCurrent++; //每次是加一個匯入表結構.
    }

}

void UnIatHook()
{
    /*
      1.遍歷匯入表.恢復匯入表即可.
    */

    MessageBoxA(NULL, "開始進行HOOK", NULL, NULL);
    PVOID pHookAddress = nullptr;
    pHookAddress = MyMessageBox;
    if (nullptr == pHookAddress)
    {
        OutputDebugString(TEXT("獲取函式地址失敗"));
        MessageBoxA(NULL, "恢復函式地址失敗HOOK", NULL, NULL);
        return;
    }
    
    //解析PE頭.尋找IAT.

    HMODULE hModImageBase = GetModuleHandle(NULL);//獲取當前的ImagBase
    PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //獲取DOS頭
    DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew;
    PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp;
    PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)& pNtHead->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)& pNtHead->OptionalHeader;

    //尋找匯出表的位置.
    DWORD dwExportLocal = pOptHead->DataDirectory[1].VirtualAddress; //找到匯出表偏移.
    //定位到匯出表
    dwTemp = (DWORD)GetModuleHandle(NULL) + dwExportLocal;
    PIMAGE_IMPORT_DESCRIPTOR   pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp;
    PIMAGE_IMPORT_DESCRIPTOR   pCurrent = pImport;
    DWORD* pFirstThunk; //匯入表子表
    //遍歷匯入表

    while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
    {
        dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);
        pFirstThunk = (DWORD*)dwTemp; //加上偏移才是真正的匯入表.
        while (*(DWORD*)pFirstThunk != NULL)
        {
            //遍歷子表
            if (*(DWORD*)pFirstThunk == (DWORD)MyMessageBox) //如果是我們的函式地址.則進行恢復.
            {
                //找到要修改的匯入表了//修改記憶體保護屬性.寫入我們新的函式地址.
                DWORD oldProtected;
                VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected);
                dwTemp = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
                memcpy(pFirstThunk, (DWORD*)& dwTemp, 4); //將變數中儲存的函式地址拷貝到匯入表中.
                VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
            }
            pFirstThunk++; //繼續遍歷.
        }
        pCurrent++; //每次是加一個匯入表結構.
    }


}
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        SetIatHook();
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:

        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

測試程式碼如下:
IAT Hook

相關文章