用 SEH 技術實現 API Hook

來源:互聯網
上載者:User

閱讀本文之前,我先假設讀者已經知道了 SEH 和 API Hook 的基本概念,因為我不打算在此進行掃盲工作。什嗎?你不懂什麼叫 SEH 和 API Hook ?那……先去找點資料看看吧,到處都有哦,推薦讀物:Jeffrey Richter 大牛的《Windows核心編程》。(沒話可說,研究系統底層編程的葵花寶典,必備!)

另外值得補充的是,API Hook 跟一般的 Hook 是一點關係都沒有的,雖然它們都是“Hook”,但是在技術上卻有著天壤之別。啊……不明白?先去看看葵花寶典吧……

呵呵,廢話不多說了,讓我們開始吧。

經常研究 Crack 的朋友一定會知道 INT 3 這個指令。(你不知道?我倒……) 這個指令在軟體調試中非常有用,因為我們可以利用它來設定特定的斷點(BreakPoint),當程式遇到 INT 3 指令的時候,將會產生一個斷點異常,這個異常在 Windows.inc 裡面定義為 EXCEPTION_BREAKPOINT ,對應值是 080000003h 。Hoho,說了那麼多,你想到什麼了嗎?

是的,聰明的你應該已經想到了!既然是異常,就肯定可以通過 SEH 來進行處理。於是我們可以這樣做:在調用 API 之前,先設定一個斷點,然後當 API 正式啟動並執行時候,就會因為碰到 INT 3 指令而進入我們的異常處理模組,接著我們就可以在處理模組裡面為所欲為了——是改變什麼東西還是讓它順利通過,我沒話說,看你喜歡吧……

簡單地說,過程就是類似這樣的:

程式遇到 INT 3 指令後,產生一個中斷異常,這時 Windows 就拿著一份處理異常的活挨個問 SEH 鏈表上的回呼函數:“你幹不幹?”,“不幹”,“你呢?”,“我也不幹”……當 Windows 終於問到我們定義好的斷點異常處理函數後,他說:“讓我來幹好了!”,於是 Windows 就不會再問餘下的人了,他把全權托給了我們的處理函數,至於我們的函數在之後做了什麼手腳……呵呵,只有天知道!

明白了嗎?其實在這裡我們是利用了軟體調試上的一個小技巧,實現了“偽 API Hook”。嚴格來說,這種方法不能算是真正的 API Hook ,但是由於我們可以在 SEH 回呼函數中為所欲為,而系統不會發覺,所以也可以勉強算個數吧。

弄清楚原理後,剩下的就不難了。我們首先要儲存目標 API 的入口地址,接著要設定一個 INT 3 指令,然後就在 SEH 的回呼函數中進行地址修正等工作,最後萬事倶備,只欠東風了。程式一運行,就進入了我們的 SEH 回呼函數,呵呵,你愛怎麼樣就怎麼樣吧……

怎麼樣?一點都不難吧。羅裡羅嗦地說了一大堆,可能有人會開始不耐煩了……呵,別著急,下面我就給出原始碼。補充一句:本方法只是提供了一種新的思路,如果你在深入研究中發現了我的錯誤,或者有更好的解決方案,請給我來信啊,我的郵箱:lcother@163.net。

(注意,本技術只能在 NT/2000/XP 平台下使用)

;*********************************************************
;程式名稱:用 SEH 技術實現 API Hook
;適用系統:Win NT/2000/XP
;作者:羅聰
;日期:2002-11-22
;出處:http://www.LuoCong.com(老羅的繽紛天地)
;注意事項:如欲轉載,請保持本程式的完整,並註明:
;轉載自“老羅的繽紛天地”(http://www.LuoCong.com)
;*********************************************************

.386
.model flat, stdcall
option casemap:none

include /masm32/include/windows.inc
include /masm32/include/kernel32.inc
include /masm32/include/user32.inc
includelib /masm32/lib/kernel32.lib
includelib /masm32/lib/user32.lib

WndProc         proto :DWORD, :DWORD, :DWORD, :DWORD
Error_Handler   proto :DWORD, :DWORD, :DWORD, :DWORD
SetHook         proto

.const
IDI_LC                  equ 1
IDC_CHECKBUTTON_HOOK    equ 3000
IDC_BUTTON_ABOUT        equ 3001
IDC_BUTTON_EXIT         equ 3002

.data
szDlgName               db  "lc_dialog", 0
szMsgAbout              db  "-= SEH for API Hook =-", 13, 10, 13, 10,/
                            "作者:羅聰(lcother@163.net)", 13, 10, 13, 10,/
                            "老羅的繽紛天地", 13, 10,/
                            "http://www.LuoCong.com", 13, 10, 0
szMyText                db  13, 10, 13, 10, "(哈哈,看到有什麼不同了嗎?)", 0
szMsgHooked             db  "MessageBoxIndirectA() has been hooked!",/
                            13, 10, 13, 10,/
                            "即將改變原來的 MessageBoxIndirectA() 的參數,", 13, 10,/
                            "請注意後面的對話方塊跟沒有 Hook 之前有什麼不同……", 0
szCaption               db  "SEH for API Hook by LC", 0
szLibUser               db  "user32", 0
szProcMsgBoxInd         db  "MessageBoxIndirectA", 0
dwAddress               dd  0
dwOldProtect            dd  0
bOldByte                db  0
dwRetAddr               dd  0

.data?
hInstance               HINSTANCE       ?
mbp                     MSGBOXPARAMS    <>
szText                  db  1024 dup(?)

.code
main:
    ; 設定 SEH 鏈:
    assume  fs:nothing
    push    offset Error_Handler
    push    fs:[0]
    mov     fs:[0], esp

    invoke  GetModuleHandle, NULL
    mov     hInstance, eax
    invoke  DialogBoxParam, hInstance, offset szDlgName, 0, WndProc, 0

    ; 恢複原來的 SEH 鏈:
    pop     fs:[0]
    pop     eax
    invoke  ExitProcess, 0

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg == WM_CLOSE
        invoke EndDialog, hWnd, 0

    .elseif uMsg == WM_INITDIALOG
        mov eax, hWnd
        mov [mbp.hwndOwner], eax
        invoke LoadIcon, hInstance, IDI_LC
        invoke SendMessage, hWnd, WM_SETICON, ICON_SMALL, eax
        ; 儲存 API 的原入口地址:
        invoke GetModuleHandle, addr szLibUser
        invoke GetProcAddress, eax, addr szProcMsgBoxInd
        mov [dwAddress], eax
        ; 儲存原對話方塊的輸出文字:
        invoke lstrcpy, addr szText, addr szMsgAbout

    .elseif uMsg == WM_COMMAND
        mov eax, wParam
        mov edx, eax
        shr edx, 16
        movzx eax, ax
        .if edx == BN_CLICKED
            .if eax == IDC_BUTTON_EXIT || eax == IDCANCEL
                invoke EndDialog, hWnd, NULL

            .elseif eax == IDC_BUTTON_ABOUT || eax == IDOK
                mov [mbp.cbSize], sizeof mbp
                mov eax, hInstance
                mov [mbp.hInstance], eax
                mov [mbp.lpszText], offset szMsgAbout
                mov [mbp.lpszCaption], offset szCaption
                mov [mbp.dwStyle], MB_OK or MB_APPLMODAL or MB_USERICON
                mov [mbp.lpszIcon], IDI_LC
                invoke MessageBoxIndirect, addr mbp

            .elseif eax == IDC_CHECKBUTTON_HOOK
                ; 把記憶體保護設定成 可讀/可寫/可執行:
                invoke VirtualProtect, [dwAddress], 1, PAGE_EXECUTE_READWRITE, addr dwOldProtect
                invoke IsDlgButtonChecked, hWnd, IDC_CHECKBUTTON_HOOK
                mov edx, [dwAddress]
                test eax, eax
                .if zero?                                           ; uninstall hook
                    mov cl, [bOldByte]                              ; bOldByte = API 原入口地址
                    mov byte ptr [edx], cl                          ; 恢複 API 的原入口地址
                    invoke lstrcpy, addr szMsgAbout, addr szText    ; 恢複原對話方塊的輸出文字:
                .else                                               ; re-install hook
                    mov cl, byte ptr [edx]                          ; byte ptr [edx] = API 原入口地址
                    mov byte ptr [edx], 0CCh                        ; 斷點異常(INT 3 指令)
                    mov [bOldByte], cl                              ; 儲存 API 的原入口地址
                    invoke lstrcat, addr szMsgAbout, addr szMyText  ; 改變原對話方塊的輸出文字:
                .endif

            .endif
        .endif
    .else
        mov eax, FALSE
        ret
    .endif
    mov eax, TRUE
    ret
WndProc endp

;****************************************
; 函數功能:處理異常錯誤
;****************************************
Error_Handler proc uses ecx lpExceptRecord:DWORD, lpFrame:DWORD, lpContext:DWORD, lpDispatch:DWORD
    ; 輸出 "API hooked":
    invoke  MessageBox, [mbp.hwndOwner], addr szMsgHooked, addr szCaption,/
            MB_OK or MB_ICONINFORMATION

    ; 儲存並改變 SetHook 函數的傳回值:(經過修正)
    ; (想不明白?呵呵,用調試器跟蹤一下吧,我也說不清楚,只能意會不能言傳……)
    mov eax, [lpContext]
    mov eax, [eax][CONTEXT.regEsp]
    mov ecx, [eax]
    mov [eax], offset SetHook
    mov [dwRetAddr], ecx

    ; 把 API 原入口地址寫回去,以便繼續運行原 API:
    ; (跟蹤一下吧,我實在是不知道怎麼才能說得清楚……)
    mov eax, [dwAddress]
    mov cl, [bOldByte]
    mov byte ptr [eax], cl

    ; 繼續下一個 Execution:
    mov eax, ExceptionContinueExecution
    ret
Error_Handler endp

;****************************************
; 函數功能:設定 API Hook
;****************************************
SetHook proc uses ecx
    mov eax, [dwAddress]
    mov cl, [eax]
    mov byte ptr [eax], 0CCh    ; 斷點異常(INT 3 指令)
    mov [bOldByte], cl
    jmp [dwRetAddr]             ; 跳回經過 Hook 之後的 API 的返回地址(很重要!)
SetHook endp

end main
;********************   over    ********************
;by LC

它的資源檔:

#include "resource.h"

#define IDI_LC              1
#define IDC_CHECKBOX_HOOK   3000
#define IDC_BUTTON_ABOUT    3001
#define IDC_BUTTON_EXIT     3002
#define IDC_STATIC          -1

IDI_LC  ICON    "lc.ico"

LC_DIALOG DIALOGEX 10, 10, 200, 50
STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "SEH for API Hook by LC, 2002-11-22"
FONT 8, "MS Sans Serif"
BEGIN
    AUTOCHECKBOX    "&Hook MessageBoxIndirectA", IDC_CHECKBOX_HOOK, 5, 5, 190, 12
    PUSHBUTTON      "關於(&A)", IDC_BUTTON_ABOUT, 5, 30, 90, 14, BS_FLAT | BS_CENTER
    PUSHBUTTON      "退出(&X)", IDC_BUTTON_EXIT, 105, 30, 90, 14, BS_FLAT | BS_CENTER
END

沒啥特別的,仔細一想就明白了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.