1. 程式人生 > >學習windows 應用層 inline hook 原理總結

學習windows 應用層 inline hook 原理總結

inline hook 實際上就是指 通過改變目標函式頭部的程式碼來使改變後的程式碼跳轉到我們自己設定的一個函式裡,產生hook。

今天就拿MessageBoxA這個api函式來做實驗。功能就是當程式呼叫MessageBoxA 時,我們打印出MessageBoxA的引數

大概程式碼結構應該是這樣

複製程式碼
typedef int (WINAPI
    *MessageBox_type) (
    __in_opt HWND hWnd,
    __in_opt LPCSTR lpText,
    __in_opt LPCSTR lpCaption,
    __in UINT uType) ;

MessageBox_type RealMessageBox;

//我們自己的MessageBox,每呼叫MessageBox都要跳到myMessageBox來處理 int WINAPI myMessageBox( __in_opt HWND hWnd, __in_opt LPCSTR lpText, __in_opt LPCSTR lpCaption, __in UINT uType) { //下面列印MessageBox引數 printf("hwnd:%8X lpText:%s lpCaption:%s,uType:%8X",hWnd,lpText,lpCaption,uType); return
RealMessageBox(hWnd,lpText,lpCaption,uType); //現在開始呼叫真正的MessageBox } VOID HookMessageBoxA() { } int _tmain(int argc, _TCHAR* argv[]) { HookMessageBoxA(); //hook操作 ::MessageBoxA(NULL,"hook test","tip",MB_OK); //執行api MessageBox return 0; }
複製程式碼

我們先看看彙編是怎樣呼叫MessageBoxA的

MessageBox流程

首先看到,MessageBoxA裡面

mov edi,edi 
mov ebp 
mov ebp,esp 
剛好是5個位元組,5個位元組可以做一個遠jmp

直接彙編改成我們自己的jmp

改後結果如下

image

單步執行發現hook成功。但程式崩潰。原因主要是由於

我們破壞了真正的MessageBox使們想要呼叫真正的MessageBox時也會呼叫失敗了,所以我們要呼叫真正的MessageBox時要加上頭部被我們換掉的部分,我們要內聯彙編,裡面不能含有編譯器自動新增的程式碼,所以在myMessageBox頭部要加上 _declspec(naked)

vs2010的debug版本每執行一個函式都要 cmp esi,esp 來驗證堆疊的。所以還要加一句push esi 和pop esi

複製程式碼
//我們自己的MessageBox,每呼叫MessageBox都要跳到myMessageBox來處理
_declspec(naked)  void WINAPI
    myMessageBox(
    __in_opt HWND hWnd,
    __in_opt LPCSTR lpText,
    __in_opt LPCSTR lpCaption,
    __in UINT uType)
{
        __asm
    {
        PUSH ebp
        mov ebp,esp

        /*
        vs2010 debug 編譯後的程式碼由於要cmp esi esp來比較堆疊。
        所以這裡在呼叫非__asm函式前push一下esi
        */
        push esi
    }
    //下面列印MessageBox引數
    printf("hwnd:%8X lpText:%s lpCaption:%s,uType:%8X",hWnd,lpText,lpCaption,uType); 

    __asm
    {
        /*
        vs2010 debug 編譯後的程式碼由於要cmp esi esp來比較堆疊。
        所以這裡在呼叫非__asm函式前push一下esi
        */
        pop esi

        mov ebx,RealMessageBox
        add ebx,5
        jmp ebx
    }
}
複製程式碼

下面說一下用程式碼來寫MessageBoxA著呢5個位元組

先宣告一個JMP結構體。注意前面加 #pragma pack(1)來避免記憶體對齊的一些規則

#pragma pack(1)
typedef struct _JMPCODE
{
    BYTE jmp;
    DWORD addr;
}JMPCODE,*PJMPCODE;

接下來寫hook函式

複製程式碼
VOID HookMessageBoxA()
{
    JMPCODE jcode;
    jcode.jmp = 0xe9;//jmp
    jcode.addr = (DWORD)myMessageBox - (DWORD)RealMessageBox - 5; 

    RealMessageBox = MessageBoxA;
    ::WriteProcessMemory(GetCurrentProcess(),MessageBoxA,&jcode,sizeof(JMPCODE),NULL);
}
複製程式碼

現在測試成功。

image

完整原始碼如下:

複製程式碼
// hook_blog_writer.cpp : 定義控制檯應用程式的入口點。
//

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

typedef int (WINAPI
    *MessageBox_type) (
    __in_opt HWND hWnd,
    __in_opt LPCSTR lpText,
    __in_opt LPCSTR lpCaption,
    __in UINT uType) ;

MessageBox_type RealMessageBox = MessageBoxA;

//我們自己的MessageBox,每呼叫MessageBox都要跳到myMessageBox來處理
_declspec(naked)  void WINAPI
    myMessageBox(
    __in_opt HWND hWnd,
    __in_opt LPCSTR lpText,
    __in_opt LPCSTR lpCaption,
    __in UINT uType)
{
        __asm
    {
        PUSH ebp
        mov ebp,esp

        /*
        vs2010 debug 編譯後的程式碼由於要cmp esi esp來比較堆疊。
        所以這裡在呼叫非__asm函式前push一下esi
        */
        push esi
    }
    //下面列印MessageBox引數
    printf("hwnd:%8X lpText:%s lpCaption:%s,uType:%8X",hWnd,lpText,lpCaption,uType); 

    __asm
    {
        /*
        vs2010 debug 編譯後的程式碼由於要cmp esi esp來比較堆疊。
        所以這裡在呼叫非__asm函式前push一下esi
        */
        pop esi

        mov ebx,RealMessageBox
        add ebx,5
        jmp ebx
    }
}


#pragma pack(1)
typedef struct _JMPCODE
{
    BYTE jmp;
    DWORD addr;
}JMPCODE,*PJMPCODE;

VOID HookMessageBoxA()
{
    JMPCODE jcode;
    jcode.jmp = 0xe9;//jmp
    jcode.addr = (DWORD)myMessageBox - (DWORD)RealMessageBox - 5; 

    RealMessageBox = MessageBoxA;
    ::WriteProcessMemory(GetCurrentProcess(),MessageBoxA,&jcode,sizeof(JMPCODE),NULL);
}

int _tmain(int argc, _TCHAR* argv[])
{
    HookMessageBoxA();  //hook操作
    ::MessageBoxA(NULL,"hook test","tip",MB_OK); //執行api MessageBox
    return 0;
}
複製程式碼