<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    【技術分享】Hook_IAT實現調包Win32API函數

    VSole2022-08-10 08:45:07

    說明

    如何調包Win32API函數?其實就是HookPE文件自己的IAT表。

    PE文件在加載到內存后,IAT中存儲對應函數名(或函數序號)的地址,所以我們只需要把用作替換的函數地址,覆蓋掉IAT中對應函數名(或函數序號)的地址,就能實現調包導入模塊的函數。(不僅包括Win32API,包括所有通過dll模塊導入的函數,在exe中都有一塊導入表與之對應)。

    下面先回顧一下PE文件導入表知識,再操作hook IAT。

    環境:Win10
    語言:C
    編譯:VS2019-x86

    1、導入表及IAT大致工作原理

    這部分涉及到PE文件導入表的知識,所以又回顧了一下PE文件的導入表及IAT大致工作原理。

    導入表在目錄項中的第二項(導出表之后)。對應目錄項中的VirtualAddress(RVA)即指向的導入表。

    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
        union {
            DWORD   Characteristics;            // 0 for terminating null import descriptor
            DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
        } DUMMYUNIONNAME;
        DWORD   TimeDateStamp;                  // 0 if not bound,
                                                // -1 if bound, and real date\time stamp
                                                //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                                // O.W. date/time stamp of DLL bound to (Old BIND)
        DWORD   ForwarderChain;                 // -1 if no forwarders
        DWORD   Name;
        DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)} IMAGE_IMPORT_DESCRIPTOR;typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
    

    這個結構體有5個4字節數據,占20字節,但只需特別記住這三個RVA即可,下面會分別詳細說明。

    三個RVA所指向的地址大概是這樣的:

    注意這是PE文件在加載內存前的樣子!

    上面涉及到的IMAGE_THUNK_DATA這個結構數組,其實就是一個4字節數,本來是一個union類型,能表示4個數,但我們只需掌握兩種即可,其余兩種已經成為歷史遺留了。

    (1)OriginalFirstThunk

    OriginalFirstThunk這個RVA所指向的是INT表(Import Name Table),這個表每個數據占4個字節。顧名思義就是表示要導入的函數的名字表。

    但是之前學導出表有了解到,導出函數可以以名字導出,亦可以序號導出。所以為了方便區分,就將這INT表的每個值做了細微調整:

    INT:如果這個4字節數的最高位(二進制)為1,那么抹去這個最高位之后,所表示的數就是要導入的函數的序號(即這個函數通過序號導入);如果最高位是0,那這個數就也是一個RVA,指向IMAGE_IMPORT_BY_NAME結構體(包含真正的導入函數的名字字符串,以0結尾)。INT表以4字節0結尾。

    IMAGE_IMPORT_BY_NAME:前兩個字節是一個序號,不是導入序號,一般無用,后面接著就是導入函數名字的字符串,以0結尾。

    (2)Name

    這個結構體變量也是一個RVA,直接指向一個字符串,這個字符串就是這個導入表對應的DLL的名字。說到這,大家明白,一個導入表只對應一個DLL。那肯定會有多個導入表。所以對應目錄項里的VirtualAddress(RVA)指向的是所有導入表的首地址,每個導入表占20字節,挨著。最后以一個空結構體作為結尾(20字節全0結構體)。

    (3)FirstAddress

    FirstAddress(RVA)指向的就是IAT表!IAT表也是每個數據占4個字節。最后以4字節0結尾。

    注意上圖PE文件加載內存前,IAT表和INT表的完全相同的,所以此時IAT表也可以判斷函數導出序號,或指向函數名字結構體。

    而在加載內存后,差別就是IAT表發生變化,系統會先根據結構體變量Name加載對應的dll(拉伸),讀取dll的導出表,對應原程序的INT表,匹配dll導出函數的地址,返回其地址,貼在對應的IAT表上,挨個修正地址(也就是GetProcAddress的功能)。

    所以上文說到,IAT表會存儲dll的函數的地址,方便調用該函數時,直接取IAT表這個地址內的值,作為函數地址,去CALL。

    (這是PE文件加載內存后的樣子,注意IAT表發生變化!)

    2、根據函數名Hook IAT表

    上面大概回顧了一下PE文件導入表的知識,現在就直接嘗試寫hook IAT的代碼,把這一塊封裝成一個函數。

    (1)函數定義

    #include<Windows.h>//hook自己pe文件的IAT導入表//參數1:自己進程的句柄//參數2:要Hook的函數名稱指針//參數3:需要覆蓋的新的函數指針。//返回值:為0則代表失敗(不是PE文件則返回0且彈MessageBox,沒有找到被hook函數僅僅返回0),//返回值:正常返回被hook函數的原始地址。int Hook_IAT_By_FuncName(HANDLE hMyProcess, PBYTE pOldFuncName, PDWORD pNewFuncAddr);
    

    (2)定位到導入表

        PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hMyProcess;
        PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pNtHeader + 4 + IMAGE_SIZEOF_FILE_HEADER);    //判斷參數一句柄指向的模塊是否為PE文件
        if (*(PWORD)pDosHeader != 0x5A4D || *(PDWORD)pNtHeader != 0x4550) {
            MessageBox(NULL, L"Not PE File!!", L"error!", NULL);        return 0;
        }    //定位到可選頭目錄項
        PIMAGE_DATA_DIRECTORY pDateDirectory = (PIMAGE_DATA_DIRECTORY)pOptionHeader->DataDirectory;    //定位到導入表
        PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDosHeader + pDateDirectory[1].VirtualAddress);
    

    (3)遍歷每塊導入表

    這一部分,遍歷每塊導入表的每個函數名,根據導入表中INT表指向的函數名,逐個對比。

    直到找到我們尋找的函數名,切記先更改IAT表中對應地址內存的讀寫權限,再寫入hook函數的地址。

        
    //遍歷每塊導入表
        while (pImportDescriptor->Name)
        {        //指向INT
            PDWORD pThunkINT = (PDWORD)((DWORD)pDosHeader + pImportDescriptor->OriginalFirstThunk);        //指向IAT
            PDWORD pThunkIAT = (PDWORD)((DWORD)pDosHeader + pImportDescriptor->FirstThunk);        while (*pThunkINT)
            {            //因為們是根據函數名Hook,所以默認排除以序號導入的函數
                if (*pThunkINT & 0x80000000) {
                    ;
                }            else
                {    //尋址函數名字結構體
                    PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pDosHeader + *pThunkINT);                //比較導入表中的函數名和我們參數2提供的函數名
                    //下面這行代碼:如果pOldFuncName指向“MessageBox”,但導入表中只有"MessageBoxW",也比較成功,進入if內。
                    if (!memcmp(pOldFuncName, pImportByName->Name, strlen((char*)pOldFuncName))) {                       //找到對應函數名后,
                        //先更改IAT表中對應地址內存的讀寫權限
                        DWORD lpflOldProtect;
                        BOOL flag = VirtualProtect((LPVOID)pThunkIAT, sizeof(DWORD), PAGE_EXECUTE_READWRITE, &lpflOldProtect);                    //記錄被hook函數的原始地址
                        DWORD OldAddr = *pThunkIAT;                    //寫入hook函數的地址,即第三個參數
                        *pThunkIAT = (DWORD)pNewFuncAddr;                    //返回被hook函數的原始地址
                        return OldAddr;
                    }
                }            //每次循環,如果函數名不對應,那么這兩個指針同時增加。
                //為滿足pThunkINT指向的名字和pThunkIAT指向的地址是一一對應的!
                pThunkINT++;
                pThunkIAT++;
            }           //結構體指針自增,表示指向下一塊導入表。
            pImportDescriptor++;
        }    //最后跳出while循環,表示沒有找到對應函數名的導入函數
        //則直接return 0;
        return 0;
    

     3、測試

    上述封裝好hookIAT的函數,現在編寫main函數調用測試一下,

    我們選擇測試hook MessageBox函數。

    (1)編寫hook函數

    編寫hook函數用于調包被hook函數,即替換掉,表面代碼是調用MessageBox函數,彈出框,實際上并不會執行MessageBox函數,而是執行我們的hook函數。

    所以這里有一個細節:hook函數的定義最好與被hook函數一致。

    未避免報錯,最好連調用約定都定義為一樣的,否則很可能會報如下錯誤:

    由于MessageBox的函數定義為:

    WINUSERAPIintWINAPIMessageBoxW(
        _In_opt_ HWND hWnd,
        _In_opt_ LPCWSTR lpText,
        _In_opt_ LPCWSTR lpCaption,
        _In_ UINT uType);#define MessageBox  MessageBoxW
    

    且上網查了一下MessageBox的調用約定為__stdcall

    所以進行如下定義hook函數(函數內容只是簡單測試一下):

    int __stdcall NewFunc(HWND x, LPCWSTR y, LPCWSTR z, UINT m) {    printf("\n\n");    printf("x=%d\n", x);    printf("y=%s\n", y);    printf("z=%s\n", z);    printf("m=%d\n", m);       printf("\n\n");    printf("Sorry! :\"MessageBox\" Function has been hooked!\n ");    return 1;
    }
    

    (2)編寫main函數進行調用測試

    int main() {    //我們要hook函數的函數名
        char FuncName[] = "MessageBox";    //參數字符串
        char str[] = "Hello World!\n";    //定義函數指針類型
        typedef int(__stdcall* MessageBoxFunc)(HWND, LPCWSTR, LPCWSTR, UINT);    //先調用正常MessageBox函數
        MessageBox(NULL,L"HOOK IAT",L"Tip",NULL);    //調用先前編寫的hookIAT函數,進行hook
        //同時返回被hook函數的地址,定義函數指針變量接收
        MessageBoxFunc OldFunc = (MessageBoxFunc)Hook_IAT_By_FuncName(GetModuleHandle(NULL), (PBYTE)FuncName, (PDWORD)NewFunc);    //測試函數指針變量接收的函數地址
        OldFunc(NULL, L"MessageBox is here", L"Tip", NULL);    //此時MessageBox函數已經被hook,不會再彈出框,
        //用于調包的hook函數是在控制臺輸出
        MessageBox((HWND)1, (LPCWSTR)FuncName, (LPCWSTR)str, (UINT)2);    printf("Got it !\n");    return 0;
    }
    

    (3)測試

    第一個正常的MessageBox

    點擊確認后執行hookIAT函數

    第二個MessageBox是定義的函數指針變量接收的hookIAT函數返回的地址。

    點擊確認后,再次執行MessageBox,但此時已經被hook調包了,在控制臺輸出語句。

    看來MessageBox已經被hook了。

    4、所有源碼

    因為代碼量并不多,所以直接寫到一個cpp文件里即可。

    環境:Win10
    語言:C
    編譯:VS2019-x86
    #include<Windows.h>#include<stdio.h>//hook自己pe文件的IAT導入表//參數1:自己進程的句柄//參數2:要Hook的函數名稱指針//參數3:需要覆蓋的新的函數指針。//返回值:為0則代表失敗(不是PE文件則返回0且彈MessageBox,沒有找到被hook函數僅僅返回0),//返回值:正常返回被hook函數的原始地址。int Hook_IAT_By_FuncName(HANDLE hMyProcess, PBYTE pOldFuncName, PDWORD pNewFuncAddr);int __stdcall NewFunc(HWND x, LPCWSTR y, LPCWSTR z, UINT m);int main() {    char FuncName[] = "MessageBox";    char str[] = "Hello World!\n";    typedef int(__stdcall* MessageBoxFunc)(HWND, LPCWSTR, LPCWSTR, UINT);
        MessageBox(NULL,L"HOOK IAT",L"Tip",NULL);
        MessageBoxFunc OldFunc = (MessageBoxFunc)Hook_IAT_By_FuncName(GetModuleHandle(NULL), (PBYTE)FuncName, (PDWORD)NewFunc);
        OldFunc(NULL, L"MessageBox is here", L"Tip", NULL);
        MessageBox((HWND)1, (LPCWSTR)FuncName, (LPCWSTR)str, (UINT)2);    printf("Got it !\n");    return 0;
    }int __stdcall NewFunc(HWND x, LPCWSTR y, LPCWSTR z, UINT m) {    printf("\n\n");    printf("x=%d\n", x);    printf("y=%s\n", y);    printf("z=%s\n", z);    printf("m=%d\n", m);    printf("\n\n");    printf("Sorry! :\"MessageBox\" Function has been hooked!\n ");    return 1;
    }//hook自己pe文件的IAT導入表//參數1:自己進程的句柄//參數2:要Hook的函數名稱指針//參數3:需要覆蓋的新的函數指針。//返回值:為0則代表失敗(不是PE文件則返回0且彈MessageBox,沒有找到被hook函數僅僅返回0),//返回值:正常返回被hook函數的原始地址。int Hook_IAT_By_FuncName(HANDLE hMyProcess, PBYTE pOldFuncName, PDWORD pNewFuncAddr) {
        PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hMyProcess;
        PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        PIMAGE_OPTIONAL_HEADER pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pNtHeader + 4 + IMAGE_SIZEOF_FILE_HEADER);    //判斷參數一句柄指向的模塊是否為PE文件
        if (*(PWORD)pDosHeader != 0x5A4D || *(PDWORD)pNtHeader != 0x4550) {
            MessageBox(NULL, L"Not PE File!!", L"error!", NULL);        return 0;
        }    //定位到可選頭目錄項
        PIMAGE_DATA_DIRECTORY pDateDirectory = (PIMAGE_DATA_DIRECTORY)pOptionHeader->DataDirectory;    //定位到導入表
        PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDosHeader + pDateDirectory[1].VirtualAddress);    while (pImportDescriptor->Name)
        {
            PDWORD pThunkINT = (PDWORD)((DWORD)pDosHeader + pImportDescriptor->OriginalFirstThunk);
            PDWORD pThunkIAT = (PDWORD)((DWORD)pDosHeader + pImportDescriptor->FirstThunk);        while (*pThunkINT)
            {            if (*pThunkINT & 0x80000000) {
                    ;
                }            else
                {    //尋址函數名字結構體
                    PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pDosHeader + *pThunkINT);                if (!memcmp(pOldFuncName, pImportByName->Name, strlen((char*)pOldFuncName))) {
                        DWORD lpflOldProtect;
                        BOOL flag = VirtualProtect((LPVOID)pThunkIAT, sizeof(DWORD), PAGE_EXECUTE_READWRITE, &lpflOldProtect);
                        DWORD OldAddr = *pThunkIAT;
                        *pThunkIAT = (DWORD)pNewFuncAddr;                    return OldAddr;
                    }
                }
                pThunkINT++;
                pThunkIAT++;
            }
            pImportDescriptor++;
        }    return 0;
    } 
    
    pe文件messagebox
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    前言在PE文件中,存在iat導入表,記錄了PE文件使用的API以及相關的dll模塊。可以看到使用了MessageBox這個API殺軟會對導入表進行查殺,如果發現存在惡意的API,比如VirtualAlloc,CreateThread等,就會認為文件是一個惡意文件。自定義API函數FARPROC GetProcAddress;定義:typedef int ();HMODULE LoadLibraryA; // 成功返回句柄 失敗返回NULL. 這里GetModuleHandle和LoadLibrary作用是一樣的,獲取dll文件。HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );printf; pMessageBox MyMessageBox = GetProcAddress; MyMessageBox; return 0;}. 程序可以正常運行:查看其導入表:User32.dll和MessageBox都不存在。實戰測試用創建進程的方式加載shellcode。
    最近無意間發現了cpl文件,之前對該類型的文件了解幾乎為零,由于觸及到我的知識盲區,于是決定探究。
    如何調包Win32API函數?其實就是HookPE文件自己的IAT表。
    Hook技術入門
    2021-07-20 16:26:02
    要說在Hook技術里面最基礎的,那就是IAT Hook,它的原理就是通過修改PE結構中的IAT表,將其替換成我們自己定義的函數,最終實現Hook,所以在進行Hook之前,我們得很清楚的PE結構,接下來我們先講解一下怎么索引到IAT表。
    DLL劫持的防御策略
    常規api創建進程通過常用的api來創建進程是常規啟動進程的方式,最常用的幾個api有WinExec、ShellExecute、CreateProcess,我們一個一個來看一下WinExec首先是WinExec,這個api結構如下,這個api只能夠運行exe文件
    結果分析Hook前Hook后,我們的彈窗本該是hello的但是hook后,程序流程被我們修改了。760D34B2 55 push ebp760D34B3 8BEC mov ebp,esp通過這兩條指令,函數就可以在堆棧中為局部變量分配存儲空間,并在函數執行過程中保存和恢復現場。這樣做的好處是可以避免局部變量和其他函數之間的沖突,同時也可以提高函數的可讀性和可維護性。
    x32TLS回調函數實驗
    2023-05-31 09:34:55
    TLS回調函數介紹TLS回調函數是在程序運行時由操作系統自動調用的一組函數,用于在進程加載和卸載時執行一些初始化和清理操作。在TLS回調函數中,可以訪問當前線程的TLS數據,并對其進行修改或檢查。值得一提的是TLS回調可以用來反調試,原理實為在實際的入口點代碼執行之前執行檢測調試器代碼。為了棧平衡,我們要把傳進這個回調函數的參數所占用的
    點擊確定后就無任何反應。 二 靜態分析 1、程序信息 MD5 fdd9fd0249d48d8c6d991741c67fcfeb SHA-1 ff0181242825b5bb8cac1d4d17e8377352e3aa55 SHA-256 6a9bdabc4599618513de5c963972929de9322c486e84e101e177c0868e7c5fb7 File size
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类