C/C++:Windows编程—Inline Hook内联钩子(上)
前言
先介紹下Windows中的Hook技術。Hook是Windows中提供的一種用以替換DOS下“中斷”的系統機制,中文譯為“掛鉤”或“鉤子”。在對特定的系統事件進行hook后,一旦發生已hook事件,對該事件進行hook的程序就會收到系統的通知,這時程序就能在第一時間對該事件做出響應。Windows中的Hook技術的方法較多,常見的有Inline Hook、IAT Hook、EAT Hook 、Windows 鉤子…等。本片博文將主要介紹 Inline Hook(內聯鉤子)。本篇博文介紹的是5字節的Inline Hook,7字節的Inline Hook點擊這里。
Inline Hook介紹
Windows的API函數都是保存在系統提供的DLL文件中。當程序調用某個API函數并運行程序的時候,程序會將DLL文件加載到進程中。舉例,比如當我們調用CreateFile這個函數的時候,如下圖
而我們的內聯鉤子的流程如下圖
當我們想要勾住CreateFile函數,首先要找到CreateFile函數地址,將其首地址代碼改為jmp MyHookProc,這樣當調用CreateFile函數時 就會跳轉到我們寫的 MyHookProc函數了,當執行我們自己寫的代碼后 如果還需要繼續執行之前的CreateFile,這時就需要將 CreateFile函數地址的首地址代碼改回來,這樣我們再次調用CreateFile時 就會走原來的流程了。
Inline Hook實現
在Inline Hook的時候我們介紹了,Hook過程主要是修改 被hook函數的首地址處的代碼,將其改為jmp 目標地址即可。那么問題來了,怎樣改?
我們用OD隨便打開一個exe程序,如下圖,我們看到的jmp匯編指令是占5個字節的,其中jmp對應的機器碼 是E9(針對長轉移了說) 占一個字節,其后的目標地址占4字節。
我們知道內存的內容 都是以二進制存儲的,程序在編譯的時候 先會通過匯編階段 轉為匯編語言,然后再通過編譯階段轉為 機器碼,最后是鏈接階段。這里直接說結論了,jmp 目標地址 經過編譯之后 會變為 E9 偏移量。那么偏移量怎么算?
前面我們知道這里的jmp指令是占5字節,**E9后的偏移量 = 目標地址 - 源地址 -5 ;**知道這個公式之后,我們就可以梳理一下 內聯鉤子的流程了。
示例效果
這是卸載鉤子后,正常彈框
下鉤子后
代碼實現
InlineHook.h
#pragma once#include <Windows.h>class CInlineHook { public:CInlineHook(void);~CInlineHook(void);bool Hook(LPSTR strModuleName,LPSTR strHookFnName,FARPROC strHookCallFnName);bool UnHook();bool ReHook(); private:FARPROC m_pFnOrign; // 要Hook的函數地址BYTE m_bOld[5]; // 要Hook的函數 前5個字節BYTE m_bNew[5]; // 要Hook的函數 修改后的5個字節 };InlineHook.cpp
#include "StdAfx.h" #include "InlineHook.h"CInlineHook::CInlineHook(void) {m_pFnOrign = NULL;memset(m_bOld,0,5);memset(m_bNew,0,5); }CInlineHook::~CInlineHook(void) {UnHook(); }bool CInlineHook::Hook(LPSTR strModuleName,LPSTR strHookFnName,FARPROC strTargetFnAddr) {bool ret = false;HMODULE hModule = GetModuleHandleA(strModuleName);if( hModule == NULL )goto end;m_pFnOrign = (FARPROC)GetProcAddress(hModule,strHookFnName);if( m_pFnOrign == NULL )goto end;SIZE_T numByte;// 保存被Hook函數的前5個字節if( ReadProcessMemory(GetCurrentProcess(),m_pFnOrign,m_bOld,5,&numByte) == 0 )goto end;// 構造jmp指令:jmp 目標地址,對應的機器碼:e9 4字節的偏移量m_bNew[0] = 0xe9;// 剩余4字節 放偏移量* (DWORD*)(&m_bNew[0]+1) = (DWORD)strTargetFnAddr - (DWORD)m_pFnOrign -5;// 修改被Hook函數的前5個字節 改變其執行流程if( WriteProcessMemory(GetCurrentProcess(),m_pFnOrign,m_bNew,5,&numByte) == 0 ) goto end;end:return ret; }bool CInlineHook::UnHook() {bool ret = false;// 卸載鉤子,實際上即是將更改的5個字節還原SIZE_T numByte;if( m_pFnOrign != NULL && WriteProcessMemory(GetCurrentProcess(),m_pFnOrign,m_bOld,5,&numByte) != 0)ret = true;return ret; }bool CInlineHook::ReHook() {bool ret = true;// 再次裝鉤子,更改要Hook的函數的前5字節 讓其跳轉到目標函數SIZE_T numByte;if( m_pFnOrign != NULL && WriteProcessMemory(GetCurrentProcess(),m_pFnOrign,m_bNew,5,&numByte) != 0)ret = true;return ret; }目標函數代碼
// WINAPI 一定要聲明,指明函數調用方式,這里和MessageBox保持一致 int WINAPI MyMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) {g_inlineHookObj.UnHook(); // 必須先卸載鉤子 再才可以再次調用被Hook的函數,不然會進入死循環::MessageBoxW(hWnd ,_T("進入MyMessageBox了"),_T("被Hook了,5字節InlineHook"),MB_OK);::MessageBoxW(hWnd ,lpText,lpCaption,MB_OK);g_inlineHookObj.ReHook();return 0; }完整項目
完整項目請在這里下載,沒分也可以在這里github下載最新代碼,如果可以的話,幫忙點個星星喲。
總結
以上是生活随笔為你收集整理的C/C++:Windows编程—Inline Hook内联钩子(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows驱动—Windows应用程
- 下一篇: Mac OS开发—Xcode给Mac应用