HooK原理简析

来源:互联网 发布:mac电脑磁盘在哪里 编辑:程序博客网 时间:2024/06/06 03:16

通过反汇编代码的跟进,了解Hook的工作原理(使用Detours库实现Hook)

示例代码

#include "stdafx.h"#include <windows.h>#include <detours.h>#pragma  comment(lib,"detours.lib")//1.拿到需要Hook的地址 操作系统里面MessageboxA里面的地址static int (WINAPI* OldMessageBoxA)( HWND, LPCSTR, LPCSTR, UINT) = MessageBoxA;//2.需要跳至的地方int WINAPI NewMessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType){    if (strcmp(lpText, "This is zy") != 0)    {        return OldMessageBoxA(hWnd, "You are ugly!", "Sorry", MB_OK);    }    return OldMessageBoxA(hWnd, "This is zy", "Everyone", MB_OK);}//3.开始进行Hook地址bool Hook() {    // 相关的初始化信息    DetourTransactionBegin();    // 更新线程信息    DetourUpdateThread(GetCurrentThread());    // 挂载我们的hook函数(NeeMessagwBoxA)到MessageBoxA函数的地址上    DetourAttach(&(PVOID&)OldMessageBoxA, NewMessageBoxA);    return NO_ERROR == DetourTransactionCommit();}// 卸载Hookbool UnHook(){    DetourTransactionBegin();    DetourUpdateThread(GetCurrentThread());    DetourDetach(&(PVOID&)OldMessageBoxA, NewMessageBoxA);    return NO_ERROR == DetourTransactionCommit();}int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd ){    //跨进程改不了    //因为我们当前的这个Hook就是我们本进程    //如果想跨进程,在Dll中Hook,然后注入 全局Hook    //Hook在哪个地方     //检测Hook CRC、    //Hook后如何让保证系统的稳定,这个做的Hook实在R3层,R0也能Hook    MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);    Hook();    MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);    UnHook();    MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);//  加载Hook的Dll文件//  HMODULE hModule = LoadLibraryA("HooKDll.dll");//  if (hModule == NULL)//  {//      printf("LoadLibraryA faild!\n");//  }//  MessageBoxA(NULL, "This is Hook", "Hook", MB_OK);//  FreeLibrary(hModule);    return 0;}

运行结果:
这里写图片描述


反汇编解析

接下来我们进入到反汇编格式查看下实现
第一句MessageBoxA(NULL, “This is Hook”, “Hook”, MB_OK);

// 压栈数据,调用MessageBoxA该API0124280E  mov         esi,esp  01242810  push        0  01242812  push        offset string "Hook" (0124AC64h)  01242817  push        offset string "This is Hook" (0124BD88h)  0124281C  push        0  0124281E  call        dword ptr [__imp__MessageBoxA@16 (0124F0C8h)] // 查看MessageBox函数的首地址76EEFDAE  mov         edi,edi  76EEFDB0  push        ebp  76EEFDB1  mov         ebp,esp  76EEFDB3  push        0  76EEFDB5  push        dword ptr [ebp+14h]  76EEFDB8  push        dword ptr [ebp+10h]  76EEFDBB  push        dword ptr [ebp+0Ch]  76EEFDBE  push        dword ptr [ebp+8]  76EEFDC1  call        76EEFD66  76EEFDC6  pop         ebp  76EEFDC7  ret         10h  

具体指令的作用就不一一解释了,这不是重点。
可以看到MessageBox的函数首地址为76A7FDAE,该处的汇编指令为
mov edi,edi->不难看出,该指令的作用就是没有作用!只是为了方面扩展


经过Hook后
第二句MessageBoxA(NULL, “This is Hook”, “Hook”, MB_OK);

// 1、压栈数据,调用MessageBoxA该API01242830  mov         esi,esp  01242832  push        0  01242834  push        offset string "Hook" (0124AC64h)  01242839  push        offset string "This is Hook" (0124BD88h)  0124283E  push        0  01242840  call        dword ptr [__imp__MessageBoxA@16 (0124F0C8h)]  // 2、跟进查看MessageBox函数的首地址,此处原本应该是 mov edi,edi76EEFDAE  jmp         NewMessageBoxA (01241424h)  76EEFDB3  push        0  76EEFDB5  push        dword ptr [ebp+14h]  76EEFDB8  push        dword ptr [ebp+10h]  76EEFDBB  push        dword ptr [ebp+0Ch]  76EEFDBE  push        dword ptr [ebp+8]  76EEFDC1  call        76EEFD66  76EEFDC6  pop         ebp  76EEFDC7  ret         10h  // 3、在01241424h地址处,进行跳转到012426C0h(我们真正的函数首地址)01241424h  jmp         NewMwssageBoxA (012426C0h) int WINAPI NewMwssageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType){012426C0  push        ebp  012426C1  mov         ebp,esp  012426C3  sub         esp,0C0h  012426C9  push        ebx  012426CA  push        esi  012426CB  push        edi  012426CC  lea         edi,[ebp-0C0h]  012426D2  mov         ecx,30h  012426D7  mov         eax,0CCCCCCCCh  012426DC  rep stos    dword ptr es:[edi]      if (strcmp(lpText, "This is zy") != 0)012426DE  push        offset string "This is zy" (0124AC30h)  012426E3  mov         eax,dword ptr [lpText]  012426E6  push        eax  012426E7  call        _strcmp (01241136h)  012426EC  add         esp,8  012426EF  test        eax,eax  012426F1  je          NewMwssageBoxA+54h (01242714h)      {        return OldMwssageBoxA(hWnd, "You are ugly!", "Sorry", MB_OK);//进行数据的压栈012426F3  mov         esi,esp  012426F5  push        0  012426F7  push        offset string "Sorry" (0124AC40h)  012426FC  push        offset string "You are ugly!" (0124AC48h)  01242701  mov         eax,dword ptr [hWnd]  01242704  push        eax  // 我们的程序将从此处跳转01242705  call        dword ptr [OldMwssageBoxA (0124E2D0h)] // 进行 RTC 运行时检错0124270B  cmp         esi,esp  0124270D  call        __RTC_CheckEsp (012411AEh)  01242712  jmp         NewMwssageBoxA+73h (01242733h)      }    return OldMwssageBoxA(hWnd, "This is zy", "Everyone", MB_OK);01242714  mov         esi,esp  01242716  push        0  01242718  push        offset string "Everyone" (0124AC58h)  0124271D  push        offset string "This is zy" (0124AC30h)  01242722  mov         eax,dword ptr [hWnd]  01242725  push        eax  01242726  call        dword ptr [OldMwssageBoxA (0124E2D0h)]  0124272C  cmp         esi,esp  0124272E  call        __RTC_CheckEsp (012411AEh)  }01242733  pop         edi  01242734  pop         esi  01242735  pop         ebx  01242736  add         esp,0C0h  0124273C  cmp         ebp,esp  0124273E  call        __RTC_CheckEsp (012411AEh)  01242743  mov         esp,ebp  01242745  pop         ebp  01242746  ret         10h// 4、经过在NewMwssageBoxA中的跳转,进入到36ED00D8地址处36ED00D8  mov         edi,edi  36ED00DA  push        ebp  36ED00DB  mov         ebp,esp  36ED00DD  jmp         76EEFDB3 // 5、很明显,此时我们又跳转回来了(此处和第二点是同一位置)76EEFDB3  push        0  76EEFDB5  push        dword ptr [ebp+14h]  76EEFDB8  push        dword ptr [ebp+10h]  76EEFDBB  push        dword ptr [ebp+0Ch]  76EEFDBE  push        dword ptr [ebp+8]  76EEFDC1  call        76EEFD66  76EEFDC6  pop         ebp  76EEFDC7  ret         10h  

通过该汇编指令的理解,可以发现:经过Hook后,改变了原来的函数首地址的指令mov edi,edi,使得该地址先跳转到我们Hook后的地址上,先实现我们的函数,完成之后再跳转到原来的地址上执行(当然,你也可以不用再跳回到原来的地址中执行),这也是Hook的原理

注意的是,该Hook仅仅是在本进程进行的,而且是Hook的ring3层


经过UnHook后,将会将我们的地址改变为原来的mov edi,edi,和第三句
MessageBoxA(NULL, “This is Hook”, “Hook”, MB_OK);一样

// 压栈数据,调用MessageBoxA该API01242852  mov         esi,esp  01242854  push        0  01242858  push        offset string "Hook" (0124AC64h)  0124285D  push        offset string "This is Hook" (0124BD88h)  01242863  push        0  01242865  call        dword ptr [__imp__MessageBoxA@16 (0124F0C8h)] // 查看MessageBox函数的首地址76EEFDAE  mov         edi,edi  76EEFDB0  push        ebp  76EEFDB1  mov         ebp,esp  76EEFDB3  push        0  76EEFDB5  push        dword ptr [ebp+14h]  76EEFDB8  push        dword ptr [ebp+10h]  76EEFDBB  push        dword ptr [ebp+0Ch]  76EEFDBE  push        dword ptr [ebp+8]  76EEFDC1  call        76EEFD66  76EEFDC6  pop         ebp  76EEFDC7  ret         10h  

总结下(Hook前后的对比):
这里写图片描述

示例代码需要Detours库的支持,Detours下载地址
本文难免有所错误,如有问题欢迎留言