windowsHOOK

// HOOK_IAT.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。
//

#include "stdafx.h"
#include <windows.h>

bool    hookIat( const char* pszDllName , const char* pszFunction , LPVOID pNewFunction );


// 盜版的MessageBox
DWORD WINAPI MyMessageBox( HWND hWnd , TCHAR* pText , TCHAR* pTitle , DWORD type ) {

    // 還原IAT
    hookIat( "User32.dll" , 
             "MessageBoxW" , 
             GetProcAddress(GetModuleHandleA("User32.dll"),"MessageBoxW" )
             );

    // 調(diào)用原版函數(shù)
    MessageBox( 0 , L"在盜版的MessageBox中彈出此框" , L"提示" , 0 );


    // HOOK IAT
    hookIat( "User32.dll" , "MessageBoxW" , &MyMessageBox );

    return 0;
}



int _tmain(int argc, _TCHAR* argv[])
{
    MessageBox( 0 , L"正版API" , L"提示" , 0 );
    // HOOK IAT
    hookIat( "User32.dll" , "MessageBoxW" , &MyMessageBox );

    MessageBox( 0 , L"正版API" , L"提示" , 0 );
    MessageBox( 0 , L"正版API" , L"提示" , 0 );
    return 0;
}


bool hookIat( const char* pszDllName ,
              const char* pszFunction ,
              LPVOID pNewFunction ) {

    // PE文件中,所有的API的地址都保存到了導(dǎo)入表中.

    // 程序調(diào)用一個(gè)API時(shí), 先會(huì)從導(dǎo)入表中得到API
    // 的地址, 再調(diào)用這個(gè)地址.
    // 如果將導(dǎo)入表中的API地址替換掉, 那么調(diào)用
    // API時(shí), 就會(huì)調(diào)用被替換的地址.

    // HOOK IAT的步驟:
    // 1. 解析PE文件,找到導(dǎo)入表
    // 2. 找到導(dǎo)入表中對(duì)應(yīng)的模塊
    // 3. 找到對(duì)應(yīng)模塊的對(duì)應(yīng)函數(shù).
    // 4. 修改函數(shù)地址.

    // 導(dǎo)入表中有兩張表, 一張是導(dǎo)入名稱表, 一張是導(dǎo)入
    // 地址表, 這兩張表的元素一一對(duì)應(yīng)的.
    // 導(dǎo)入名稱表中存放的是函數(shù)名
    // 導(dǎo)入地址表中存放的是函數(shù)地址.

    HANDLE hProc = GetCurrentProcess( );
    
    PIMAGE_DOS_HEADER           pDosHeader; // Dos頭
    PIMAGE_NT_HEADERS           pNtHeader;  // Nt頭
    PIMAGE_IMPORT_DESCRIPTOR    pImpTable;  // 導(dǎo)入表
    PIMAGE_THUNK_DATA           pInt;       // 導(dǎo)入表中的導(dǎo)入名稱表
    PIMAGE_THUNK_DATA           pIat;       // 導(dǎo)入表中的導(dǎo)入地址表
    DWORD                       dwSize;
    DWORD                       hModule;
    char*                       pFunctionName;
    DWORD                       dwOldProtect;

    hModule = ( DWORD)GetModuleHandle( NULL );

    // 讀取dos頭
    pDosHeader = (PIMAGE_DOS_HEADER)hModule;

    // 讀取Nt頭
    pNtHeader = (PIMAGE_NT_HEADERS)( hModule + pDosHeader->e_lfanew );


    // 獲取導(dǎo)入表
    pImpTable = ( PIMAGE_IMPORT_DESCRIPTOR )
        (hModule + pNtHeader->OptionalHeader.DataDirectory[1].VirtualAddress);

    // 遍歷導(dǎo)入表
    while( pImpTable->FirstThunk != 0 && pImpTable->OriginalFirstThunk != 0 ) {


        // 判斷是否找到了對(duì)應(yīng)的模塊名
        if( _stricmp( (char*)(pImpTable->Name+hModule) , pszDllName ) != 0 ) {
            ++pImpTable;
            continue;
        }
        
        // 遍歷名稱表,找到函數(shù)名
        pInt = (PIMAGE_THUNK_DATA)( pImpTable->OriginalFirstThunk + hModule );
        pIat = (PIMAGE_THUNK_DATA)( pImpTable->FirstThunk + hModule );

        while( pInt->u1.AddressOfData != 0 ) {

            // 判斷是以名稱導(dǎo)入還是以需要導(dǎo)入
            if( pInt->u1.Ordinal & 0x80000000 == 1 ) {
                // 以序號(hào)導(dǎo)入

                // 判斷是否找到了對(duì)應(yīng)的函數(shù)序號(hào)
                if( pInt->u1.Ordinal == ( (DWORD)pszFunction ) & 0xFFFF ) {
                    // 找到之后,將鉤子函數(shù)的地址寫入到iat
                    VirtualProtect( &pIat->u1.Function ,
                                    4 ,
                                    PAGE_READWRITE ,
                                    &dwOldProtect
                                    );

                    pIat->u1.Function = (DWORD)pNewFunction;

                    VirtualProtect( &pIat->u1.Function ,
                                    4 ,
                                    dwOldProtect ,
                                    &dwOldProtect
                                    );
                    return true;
                }
            }
            else {
                // 以名稱導(dǎo)入
                pFunctionName = (char*)( pInt->u1.Function + hModule + 2);

                // 判斷是否找到了對(duì)應(yīng)的函數(shù)名
                if( strcmp( pszFunction , pFunctionName ) == 0 ) {

                    VirtualProtect( &pIat->u1.Function ,
                                    4 ,
                                    PAGE_READWRITE ,
                                    &dwOldProtect
                                    );

                    // 找到之后,將鉤子函數(shù)的地址寫入到iat
                    pIat->u1.Function = (DWORD)pNewFunction;

                    VirtualProtect( &pIat->u1.Function ,
                                    4 ,
                                    dwOldProtect ,
                                    &dwOldProtect
                                    );

                    return true;
                }
            }
            
            ++pIat;
            ++pInt;
        }


        ++pImpTable;
    }

    return false;
    
}

========================

// HOOK_內(nèi)聯(lián)HOOK.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。
//

#include "stdafx.h"
#include <windows.h>

BYTE    g_jmpShellCode[5] = { "\xe9" };
BYTE    g_oldOpcode[ 5 ] = { 0 };

#include "hookFunction.h"


// 盜版的MessageBox
DWORD WINAPI MyMessageBox( HWND hWnd , TCHAR* pText , TCHAR* pTitle , DWORD type ) 
{

    DWORD dwOldProtect = 0;
    DWORD dwWrite = 0;
    VirtualProtectEx( GetCurrentProcess( ) ,
                      &MessageBox ,
                      4 ,
                      PAGE_EXECUTE_READWRITE ,
                      &dwOldProtect
                      );


    // 恢復(fù)函數(shù)原來的內(nèi)容
    WriteProcessMemory( GetCurrentProcess( ) ,
                        &MessageBox ,
                        g_oldOpcode ,
                        sizeof( g_jmpShellCode ) ,
                        &dwWrite
                        );

    // 調(diào)用原版函數(shù)
    MessageBox( 0 , L"在盜版的MessageBox中彈出此框" , L"提示" , 0 );



    // 調(diào)用完原版函數(shù)之后,再次HOOK這個(gè)函數(shù)
    WriteProcessMemory( GetCurrentProcess( ) ,
                        &MessageBox ,
                        g_jmpShellCode ,
                        sizeof( g_jmpShellCode ) ,
                        &dwWrite
                        );

    //  6. 恢復(fù)內(nèi)存分頁的屬性
    VirtualProtectEx( GetCurrentProcess( ) ,
                      &MessageBox ,
                      4 ,
                      dwOldProtect ,
                      &dwOldProtect
                      );

    return 0;
}

// 盜版的MessageBox
DWORD WINAPI MyMessageBox2( HWND hWnd , TCHAR* pText , TCHAR* pTitle , DWORD type ) {
    //MessageBox( hWnd , pText , pTitle , type );
    MessageBox( hWnd , L"--------------", pTitle , type );
    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{

    // 內(nèi)聯(lián)HOOK的示意:
    // 未被攔截的函數(shù)
    // MessageBox:
    //      +--------------+
    // ==>  | mov edi,edi  |
    //      | push ebp     |
    //      | mov ebp,esp  |
    //      |              |
    //      |  XXXXXXXXX   |
    //      +--------------+
    // 
    // 被攔截的函數(shù)   
    //                                      function
    //  MessageBox:               +--->>-------------------+            
    //      +--------------+      |    |    push ebp       |
    // ==>  | jmp function>>------+    |   mov ebp,esp     |
    //      | push ebp     |           |                   |
    //      | mov ebp,esp  |           |     XXXXXX        |
    //      |              |           |     XXXXXX        |
    //      |  XXXXXXXXX   |           |     XXXXXX        |
    //      +--------------+           |     XXXXXX        |
    //                                 | jmp  MessageBox+5 |
    //                                 +-------------------+
    //
    // 所謂HOOK, 就是攔截, HOOK API就是在API的真正代碼被執(zhí)行前攔截它
    // 跳轉(zhuǎn)到另一個(gè)地方執(zhí)行代碼, 執(zhí)行后再跳轉(zhuǎn)回去,或不跳轉(zhuǎn).
    // HOOK的概念比簡(jiǎn)單, 但一些細(xì)節(jié)需要注意
    // 例如:
    // 1. 原函數(shù)是沒有跳轉(zhuǎn)到XXX地址的代碼的,jmp XXX是我們通過修改內(nèi)存
    //    加入進(jìn)去的, 在修改內(nèi)存時(shí),需要注意的是,一般能夠被執(zhí)行的內(nèi)存分頁
    //    都沒有可寫的屬性,因此,在修改內(nèi)存前,需要修改內(nèi)存分頁屬性.
    // 2. jmp 指令需要用到一個(gè)跳轉(zhuǎn)偏移, 而非一個(gè)絕對(duì)的地址. 將jmp XXX的
    //    opcode寫入到內(nèi)存時(shí), 必須知道怎么計(jì)算出這個(gè)跳轉(zhuǎn)偏移. 一般跳轉(zhuǎn)
    //    偏移需要用到以下公式來計(jì)算:
    //      跳轉(zhuǎn)偏移 = 目標(biāo)地址 - 當(dāng)前跳轉(zhuǎn)指令所在地址 - 跳轉(zhuǎn)指令的總體長(zhǎng)度
    //    如:  
    //      指令要跳轉(zhuǎn)到 011AB61F
    //      jmp 011AB61F 這條跳轉(zhuǎn)指令所在地址為 011AB618
    //      jmp 011AB61F 這條指令的總體長(zhǎng)度為2, 因?yàn)樗膐pcode是EB 05,只有2個(gè)字節(jié)
    //      所以它的偏移為: 011AB61F - 011AB61F - 2  => 5
    //         
    //  +-<011AB618 | EB 05 | JMP 011AB61F 
    //  |  011AB61A | 31 C0 | XOR EAX , EAX 
    //  |  011AB61C | 31 DB | XOR EBX , EBX 
    //  |  011AB61E | 40    | INC EAX 
    //  +> 011AB61F | 43    | INC EBX 


    // 內(nèi)聯(lián)HOOK步驟:
    //  1. 設(shè)置要修改的內(nèi)存的分頁屬性為可寫
    //  2. 準(zhǔn)備跳轉(zhuǎn)的shellcode
    //  3. 計(jì)算跳轉(zhuǎn)偏移,并將計(jì)算好的跳轉(zhuǎn)偏移寫入到shellcode中
    //  4. 將函數(shù)開始地址處的opcode備份,字節(jié)數(shù)和shellcode等長(zhǎng).
    //  5. 將opcode寫入到要HOOK的函數(shù)
    //  6. 恢復(fù)內(nèi)存分頁的屬性

    // 1. 設(shè)置要修改的內(nèi)存的分頁屬性為可寫
    DWORD dwOldProtect = 0;
    VirtualProtectEx( GetCurrentProcess( ) ,
                      &MessageBox ,
                      4 ,
                      PAGE_EXECUTE_READWRITE ,
                      &dwOldProtect
                      );

    // 2. 準(zhǔn)備跳轉(zhuǎn)的shellcode
    //  BYTE    g_jmpShellCode[5] = { "\xe9" };


    // 3. 計(jì)算跳轉(zhuǎn)偏移,并將計(jì)算好的跳轉(zhuǎn)偏移寫入到shellcode中
    DWORD dwJmpOffset = (DWORD)&MyMessageBox - (DWORD)&MessageBoxW - 5;
    *(DWORD*)( g_jmpShellCode + 1 ) = dwJmpOffset;



    // 4. 將函數(shù)開始地址處的opcode備份,字節(jié)數(shù)和shellcode等長(zhǎng).
    //    BYTE  g_oldOpcode[ 5 ] = { 0 };
    DWORD   dwRead = 0;
    ReadProcessMemory( GetCurrentProcess( ) ,
                       &MessageBox ,
                       g_oldOpcode ,
                       sizeof( g_jmpShellCode ) ,
                       &dwRead
                       );


    //  5. 將opcode寫入到要HOOK的函數(shù)
    WriteProcessMemory( GetCurrentProcess( ) ,
                        &MessageBox ,
                        g_jmpShellCode ,
                        sizeof( g_jmpShellCode ) ,
                        &dwRead
                        );

    //  6. 恢復(fù)內(nèi)存分頁的屬性
    VirtualProtectEx( GetCurrentProcess( ) ,
                      &MessageBox ,
                      4 ,
                      dwOldProtect ,
                      &dwOldProtect
                      );


    // 函數(shù)被調(diào)用后,將跑到鉤子函數(shù)中執(zhí)行代碼
    MessageBox( NULL , L"我是正版MessageBox" , L"提示" , 0 );
    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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