windowsHOOK

// HOOK_IAT.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include 

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" )
             );

    // 调用原版函数
    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的地址都保存到了导入表中.

    // 程序调用一个API时, 先会从导入表中得到API
    // 的地址, 再调用这个地址.
    // 如果将导入表中的API地址替换掉, 那么调用
    // API时, 就会调用被替换的地址.

    // HOOK IAT的步骤:
    // 1. 解析PE文件,找到导入表
    // 2. 找到导入表中对应的模块
    // 3. 找到对应模块的对应函数.
    // 4. 修改函数地址.

    // 导入表中有两张表, 一张是导入名称表, 一张是导入
    // 地址表, 这两张表的元素一一对应的.
    // 导入名称表中存放的是函数名
    // 导入地址表中存放的是函数地址.

    HANDLE hProc = GetCurrentProcess( );
    
    PIMAGE_DOS_HEADER           pDosHeader; // Dos头
    PIMAGE_NT_HEADERS           pNtHeader;  // Nt头
    PIMAGE_IMPORT_DESCRIPTOR    pImpTable;  // 导入表
    PIMAGE_THUNK_DATA           pInt;       // 导入表中的导入名称表
    PIMAGE_THUNK_DATA           pIat;       // 导入表中的导入地址表
    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 );


    // 获取导入表
    pImpTable = ( PIMAGE_IMPORT_DESCRIPTOR )
        (hModule + pNtHeader->OptionalHeader.DataDirectory[1].VirtualAddress);

    // 遍历导入表
    while( pImpTable->FirstThunk != 0 && pImpTable->OriginalFirstThunk != 0 ) {


        // 判断是否找到了对应的模块名
        if( _stricmp( (char*)(pImpTable->Name+hModule) , pszDllName ) != 0 ) {
            ++pImpTable;
            continue;
        }
        
        // 遍历名称表,找到函数名
        pInt = (PIMAGE_THUNK_DATA)( pImpTable->OriginalFirstThunk + hModule );
        pIat = (PIMAGE_THUNK_DATA)( pImpTable->FirstThunk + hModule );

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

            // 判断是以名称导入还是以需要导入
            if( pInt->u1.Ordinal & 0x80000000 == 1 ) {
                // 以序号导入

                // 判断是否找到了对应的函数序号
                if( pInt->u1.Ordinal == ( (DWORD)pszFunction ) & 0xFFFF ) {
                    // 找到之后,将钩子函数的地址写入到iat
                    VirtualProtect( &pIat->u1.Function ,
                                    4 ,
                                    PAGE_READWRITE ,
                                    &dwOldProtect
                                    );

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

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

                // 判断是否找到了对应的函数名
                if( strcmp( pszFunction , pFunctionName ) == 0 ) {

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

                    // 找到之后,将钩子函数的地址写入到iat
                    pIat->u1.Function = (DWORD)pNewFunction;

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

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


        ++pImpTable;
    }

    return false;
    
}

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

// HOOK_内联HOOK.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include 

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
                      );


    // 恢复函数原来的内容
    WriteProcessMemory( GetCurrentProcess( ) ,
                        &MessageBox ,
                        g_oldOpcode ,
                        sizeof( g_jmpShellCode ) ,
                        &dwWrite
                        );

    // 调用原版函数
    MessageBox( 0 , L"在盗版的MessageBox中弹出此框" , L"提示" , 0 );



    // 调用完原版函数之后,再次HOOK这个函数
    WriteProcessMemory( GetCurrentProcess( ) ,
                        &MessageBox ,
                        g_jmpShellCode ,
                        sizeof( g_jmpShellCode ) ,
                        &dwWrite
                        );

    //  6. 恢复内存分页的属性
    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[])
{

    // 内联HOOK的示意:
    // 未被拦截的函数
    // MessageBox:
    //      +--------------+
    // ==>  | mov edi,edi  |
    //      | push ebp     |
    //      | mov ebp,esp  |
    //      |              |
    //      |  XXXXXXXXX   |
    //      +--------------+
    // 
    // 被拦截的函数   
    //                                      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的真正代码被执行前拦截它
    // 跳转到另一个地方执行代码, 执行后再跳转回去,或不跳转.
    // HOOK的概念比简单, 但一些细节需要注意
    // 例如:
    // 1. 原函数是没有跳转到XXX地址的代码的,jmp XXX是我们通过修改内存
    //    加入进去的, 在修改内存时,需要注意的是,一般能够被执行的内存分页
    //    都没有可写的属性,因此,在修改内存前,需要修改内存分页属性.
    // 2. jmp 指令需要用到一个跳转偏移, 而非一个绝对的地址. 将jmp XXX的
    //    opcode写入到内存时, 必须知道怎么计算出这个跳转偏移. 一般跳转
    //    偏移需要用到以下公式来计算:
    //      跳转偏移 = 目标地址 - 当前跳转指令所在地址 - 跳转指令的总体长度
    //    如:  
    //      指令要跳转到 011AB61F
    //      jmp 011AB61F 这条跳转指令所在地址为 011AB618
    //      jmp 011AB61F 这条指令的总体长度为2, 因为它的opcode是EB 05,只有2个字节
    //      所以它的偏移为: 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 


    // 内联HOOK步骤:
    //  1. 设置要修改的内存的分页属性为可写
    //  2. 准备跳转的shellcode
    //  3. 计算跳转偏移,并将计算好的跳转偏移写入到shellcode中
    //  4. 将函数开始地址处的opcode备份,字节数和shellcode等长.
    //  5. 将opcode写入到要HOOK的函数
    //  6. 恢复内存分页的属性

    // 1. 设置要修改的内存的分页属性为可写
    DWORD dwOldProtect = 0;
    VirtualProtectEx( GetCurrentProcess( ) ,
                      &MessageBox ,
                      4 ,
                      PAGE_EXECUTE_READWRITE ,
                      &dwOldProtect
                      );

    // 2. 准备跳转的shellcode
    //  BYTE    g_jmpShellCode[5] = { "\xe9" };


    // 3. 计算跳转偏移,并将计算好的跳转偏移写入到shellcode中
    DWORD dwJmpOffset = (DWORD)&MyMessageBox - (DWORD)&MessageBoxW - 5;
    *(DWORD*)( g_jmpShellCode + 1 ) = dwJmpOffset;



    // 4. 将函数开始地址处的opcode备份,字节数和shellcode等长.
    //    BYTE  g_oldOpcode[ 5 ] = { 0 };
    DWORD   dwRead = 0;
    ReadProcessMemory( GetCurrentProcess( ) ,
                       &MessageBox ,
                       g_oldOpcode ,
                       sizeof( g_jmpShellCode ) ,
                       &dwRead
                       );


    //  5. 将opcode写入到要HOOK的函数
    WriteProcessMemory( GetCurrentProcess( ) ,
                        &MessageBox ,
                        g_jmpShellCode ,
                        sizeof( g_jmpShellCode ) ,
                        &dwRead
                        );

    //  6. 恢复内存分页的属性
    VirtualProtectEx( GetCurrentProcess( ) ,
                      &MessageBox ,
                      4 ,
                      dwOldProtect ,
                      &dwOldProtect
                      );


    // 函数被调用后,将跑到钩子函数中执行代码
    MessageBox( NULL , L"我是正版MessageBox" , L"提示" , 0 );
    return 0;
}

你可能感兴趣的:(windowsHOOK)