駭客愛用的HOOK技術大揭秘

什麼是HOOK技術?

病毒木馬為何慘遭殺軟攔截?

商業軟體為何頻遭免費破解?

系統漏洞為何能被補丁修復?

這一切的背後到底是人性的扭曲,還是道德的淪喪,盡請收看今天的專題文章:

《什麼是HOOK技術?》

上面是開個玩笑,言歸正傳,今天來聊的話題就是安全領域一個非常重要的技術:HOOK技術。

HOOK,英文意思是“鉤子”

駭客愛用的HOOK技術大揭秘

在計算機程式設計中,HOOK是一種「劫持」程式原有執行流程,新增額外處理邏輯的一種技術。

按照這個定義,其實我們Python中的裝飾器和Java中的註解,這種面向切面程式設計的手法在某種程度上來說,也算是HOOK。

不同的是,本文要探討的HOOK並非屬於程式原有的邏輯,而是在程式已經編譯成可執行檔案甚至已經在執行中的時候,如何劫持和修改程式的流程。

按照劫持的目標不同,常見的HOOK有以下這些型別:

Inline HOOKIAT HOOKC++ virtable HOOKSEH HOOKIDT HOOKSSDT HOOKIRP HOOKTDI HOOK && NDIS HOOKWindows Message HOOK

接下來,咱們挨個來看一下。

Inline HOOK

程式和程式碼是給程式設計師們看的,計算機要執行,最終是要編譯成CPU的機器指令才能執行。

Inline HOOK

的目標就是直接修改程式編譯後的指令,屬於最基礎也最常見的HOOK技術。

下面我們以一個例項來感受一下Inline HOOK的效果:

void functionA() { cout << “this is function A” << endl;}void hookFunction() { cout << “this is hookFunction” << endl;}int main() { cout << “before hook” << endl; functionA(); // prepare hook unsigned char code[5] = { 0xe9, 0x00, 0x00, 0x00, 0x00 }; unsigned int offset = (unsigned int)hookFunction - ((unsigned int)functionA + 5); *(unsigned int*)&code[1] = offset; // install hook unsigned long old = 0; VirtualProtect(functionA, 0x1000, PAGE_EXECUTE_READWRITE, &old); memcpy(functionA, code, 5); cout << “after hook” << endl; functionA(); return 0;}

輸出:

駭客愛用的HOOK技術大揭秘

程式碼中定義了目標函式functionA和hook函式hookFunction。

第一次呼叫,輸出顯示呼叫了原函式。

然後安裝一個HOOK,準備了一條jmp指令,覆蓋函式入口處的指令。此時觀察覆蓋前後的函式指令變化對比:

駭客愛用的HOOK技術大揭秘

再次呼叫該函式,則一進入就發生跳轉,我們安裝的HOOK函式得到了執行。

所以第二次輸出顯示HOOK函式得到了呼叫。

駭客愛用的HOOK技術大揭秘

大部分情況下,我們習慣於在函式入口處執行HOOK,但這並不是絕對的,還需要具體問題具體分析。比如如果我們需要等待函式執行完畢時拿到返回值才能介入處理,這個時候就需要在函式return的地方進行HOOK。甚至有可能需要在函式中途某個地方介入,這個時候就需要更進一步的對函式的反編譯指令進行分析,確定HOOK的點位和處理邏輯。

執行Inline HOOK非常關鍵的幾點:

指令所在的記憶體頁是否允許寫入操作,若只讀,須先新增寫入許可權

需要動態解析目標位置處的指令,不能像上面那樣暴力覆蓋,否則會影響原來函式的執行邏輯

如果在HOOK處理函式中需要呼叫原函式,注意別陷入死迴圈

如果有引數,需要處理好堆疊平衡

Inline HOOK由於是直接在CPU機器指令層面上的操作,所以首先是無法做到跨平臺。同時,還需要對CPU的指令集有一定程式的瞭解,具備一定的組合語言功底。

好在,IT行業從來不缺造輪子的人,已經有不少優秀的開源專案將Inline HOOK封裝成庫,比如:

Detours

除了直接修改函式的機器指令,還有一類HOOK,它們修改的是某些重要的函式指標,從而達到劫持執行的目的。

形形色色的函式指標,就衍生出各式各樣的HOOK技術。

IAT HOOK

一個程式的所有程式碼一般不會全部都編譯到一個模組中,分拆到不同的模組既有利於合作開發,也有利於程式碼管理,降低耦合。

動態連結庫就提供了這樣的能力,將不同的模組編譯成一個個的動態庫檔案,在使用時引入呼叫。

在Windows平臺上,動態連結庫一般以DLL檔案的形式存在,主程式模組一般是EXE檔案形式存在。無論是EXE還是DLL,都是屬於PE檔案。

一個模組引用了哪些模組的哪些函式,是被記錄在PE檔案的匯入表IAT中。這個表格位於PE檔案的頭部,裡面記錄了模組的名字,函式的名字。

在模組載入時,模組載入器將解析對應函式的實際地址,填入到匯入表中。

透過修改匯入表IAT中函式的地址,這種HOOK叫

IAT HOOK

SEH HOOK

SEH是Windows作業系統上

結構化異常處理

的縮寫,在程式碼中透過try/except來捕獲異常時,作業系統將會線上程的棧空間裡安置一個異常處理器(其實就是一個數據結構),裡面定義了發生異常時該去執行哪裡的程式碼處理異常。

異常處理可以多級巢狀,那多個異常處理就構成了一個連結串列,存在於棧空間之上。

駭客愛用的HOOK技術大揭秘

當發生異常時,作業系統系統就從最近的異常處理器進行尋求處理,如果能處理則罷了,不能處理就繼續尋求更上一級的異常處理器,直到找到能處理的異常處理器。如果都沒法處理,那對不起,只好彈出那個經典的報錯對話方塊,程序崩潰。

駭客愛用的HOOK技術大揭秘

SEH HOOK

針對的目標就是修改這些異常處理器中記錄的函式指標,當異常發生時就能獲得執行,從而劫持到執行流!因為這些異常處理器都位於執行緒的棧空間,修改起來並非難事。

C++ virtable HOOK

C++是一門面向物件的程式語言,支援面向物件的三大特性:封裝性、繼承性、多型性。

其中的多型性,各個C++編譯器基本上都是透過一種叫虛擬函式表的機制來實現。

下面透過一個實際的例子來感受一下虛擬函式表在C++多型性上發揮的作用。

#include using namespace std;class Animal {public: virtual void breathe() { cout << “Animal breathe” << endl; } virtual void eat() { cout << “Animal eat” << endl; }};class Fish : public Animal {public: virtual void breathe() { cout << “Fish breathe” << endl; } virtual void eat() { cout << “Fish eat” << endl; }};class Cat : public Animal {public: virtual void breathe() { cout << “Cat breathe” << endl; } virtual void eat() { cout << “Cat eat” << endl; }};int main() { Animal* animal = nullptr; Fish* fish = new Fish(); Cat* cat = new Cat(); animal = fish; animal->breathe(); animal->eat(); cout << “————————” << endl; cout << “sizeof(fish) = ” << sizeof(fish) << endl; cout << “sizeof(cat) = ” << sizeof(cat) << endl; cout << “————————” << endl; animal = cat; animal->breathe(); animal->eat(); delete fish; delete cat; return 0;}

輸出:

駭客愛用的HOOK技術大揭秘

透過上面的輸出,可以看到,fish和cat物件都只佔據4個位元組。因為這兩個類都沒有成員變數,唯一需要儲存的就是一個虛擬函式表指標。

以cat物件為例,看一下它的地址,是0x005cfc30:

駭客愛用的HOOK技術大揭秘

看一下這個地址起始的4個位元組,是什麼:

駭客愛用的HOOK技術大揭秘

虛表指標是0x000d9b90(這裡需要注意位元組順序)。

透過這個地址,找到虛擬函式表,裡面有兩個函式地址:

駭客愛用的HOOK技術大揭秘

檢視這兩個地址,都是指向了一個jmp指令,分別跳到了Cat類的兩個虛擬函式。

駭客愛用的HOOK技術大揭秘

駭客愛用的HOOK技術大揭秘

透過上面的例項,總結一下物件、虛擬函式表和虛擬函式程式碼之間的關係如下圖所示:

駭客愛用的HOOK技術大揭秘

每個包含虛擬函式的類物件,在記憶體中都有一個指標,位於物件頭部,指向的是一個虛擬函式表,表中的每一項都是虛擬函式地址。

類繼承後,如果重寫了父類的虛擬函式,子類物件指向的表格中對應函式的地址將會更新為子類的函式。

這樣,使用父類指標指向子類物件,透過指標呼叫虛擬函式時,就能呼叫到子類重寫的虛擬函式,從而實現多型性。

既然是記錄函式地址的表格,那就有存在被篡改的可能,這就是C++ virtable HOOK。

透過篡改對應虛擬函式的地址,實現對相應函式呼叫的攔截。

實施這種

HOOK

,需要逆向分析目標C++物件的結構,掌握虛擬函式表中各個函式的位置,才能精準打擊。

上面幾種HOOK,修改的都是應用層的函式指標,而作業系統核心中還有一些非常重要的表格,它們的表項中記錄了一些更加關鍵的函式,HOOK這些表格中的函式是非常高危的操作,操作不當將導致作業系統崩潰。當然,高風險高回報,HOOK這些函式,能實現一些非常強大的功能,是病毒、木馬、安全軟體非常愛乾的事情。

SSDT HOOK

系統呼叫

是作業系統提供給應用程式的程式設計介面API,應用程式透過這些API得以操作計算機的資源(如程序、網路、檔案等)。

執行系統呼叫的時候,CPU將從使用者模式切換到核心模式,進入核心後,將會根據系統呼叫的API編號,去找到對應的系統服務函式,實現對應API的功能。

作業系統將所有的系統服務函式地址,存放在了一個表格中,這個表格就是系統

服務描述符表

。在Linux上,這個表格的名字叫s

ys_call_table

,在Windows上,它叫

KeServiceDescriptorTable

,簡稱

SSDT

駭客愛用的HOOK技術大揭秘

Windows上的SSDT向來是兵家必爭之地,安全軟體為了監控應用程式的行為,通常都會替換SSDT表格中的系統服務函式地址為它們的函式。當系統呼叫觸發時,安全軟體將會及時知曉,並透過應用程式的引數來判定是否“放行”這次呼叫。

IDT HOOK

核心中除了記錄系統服務的SSDT,還有一個非常重要的表格:

中斷描述符表IDT。

IDT用於記錄CPU執行過程中遇到中斷、異常等情況時,該轉向哪裡去處理這些情況的函式地址。

HOOK IDT有一個注意事項,不同於SSDT是全域性唯一的,IDT是與CPU核心緊密相關的,對於多核處理器,會對應多個IDT表。如果想透過HOOK IDT中的函式來搞事情的話,可能需要同時處理多個表。

駭客愛用的HOOK技術大揭秘

除了直接修改函式指令和修改函式指標之外,還有一類特殊的HOOK,它們透過系統提供的機制攔截某些訊息、通知,從而有機會介入監聽、攔截。

IRP HOOK

在Windows系統上,使用者程式和核心驅動之間的互動是透過一種稱為IRP的資料結構實現的,你可以簡單將其理解為應用程式傳送了一個訊息下去,這個訊息就是一個IRP。

而接收訊息的目標,是驅動程式建立的

裝置Device

。注意,這個裝置不一定是物理裝置,也可能完全不存在的虛擬裝置,驅動程式可以任意建立一個不存在的裝置。

Windows核心中提供了驅動裝置的掛載操作,允許別的驅動程式對指定裝置進行掛載,從而可以截獲傳送給該裝置的“訊息”,這種HOOK方式被稱為

IRP HOOK

駭客愛用的HOOK技術大揭秘

國內一些安全軟體為了互相攻擊,經常用這種方式攔截對方驅動程式的訊息,從而可以“保護”自己不被對方幹掉。

TDI HOOK && NDIS HOOK

這兩種HOOK方式與Windows核心中的網路子系統密切相關。

Windows核心中的網路結構是分層式設計。從最上層的API socket層、到TCP/IP協議棧層、再到底層的網絡卡驅動程式,分了很多個層次。

而層與層之間的互動,是透過一系列標準介面來實現的,其中最重要的兩個介面標準就是

TDI

NDIS

。TDI封裝了不同協議棧的差異(Windows不止支援TCP/IP協議棧)提供給上層統一的呼叫介面。NDIS則封裝了底層不同網絡卡的驅動程式介面差異,提供給上層統一的收發資料包介面。

Windows為了擴充套件性支援,允許類似防火牆之類的軟體透過這些介面接入,從而能夠截獲到網路通訊流量,進行安全審計。

既然開了這些介面,一些流氓軟體和木馬病毒也就盯上了它們,透過這些介面就能輕鬆監聽、篡改網路資料,達到邪惡的目的。

Windows Message HOOK

Windows作業系統的UI互動是以訊息來驅動的,使用者的鍵盤輸入、滑鼠操作都會被作業系統以訊息的形式傳送到各個應用程式處理。

Windows提供了API介面,可以被程式用於捕獲這些訊息,從而實現一些特定的功能。

HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId);

這種機制叫做Windows訊息鉤子,最常見的就要數鍵盤鉤子了,在十多年前流氓軟體和木馬病毒大行其道的時候,這些惡意軟體經常喜歡透過這種方式來監聽使用者的鍵盤輸入,從而來盜取QQ密碼(當然,現在肯定是不行的了)。

總結

以上就是要介紹的全部HOOK技術了。當然有HOOK,就有反HOOK,很多安全軟體都會檢查關鍵的位置是否被篡改。不僅如此,因為流氓軟體隨意修改系統,Windows從Win7 x64開始加入了PatchGuard機制,針對作業系統核心資料結構都加入了定時檢測機制,一旦發現被篡改,立刻藍色畫面給你看,而且在隨著系統升級換代,這個檢查的粒度和強度變得越來越強。

最後來回到文章開頭的幾個問題:

病毒木馬為何慘遭殺軟攔截?

因為安全軟體在核心中HOOK了大量的關鍵位置,病毒木馬的程序、檔案、網路行為都將受到監控,一舉一動都難逃殺軟的眼睛,想要攔截易如反掌。

商業軟體為何頻遭免費破解?

透過逆向分析加上Inline HOOK,破解者可以篡改掉商業軟體的註冊校驗機制,讓校驗函式返回成功,繞開軟體的限制。

系統漏洞為何能被補丁修復?

統一透過Inline HOOK,作業系統能夠修改原來有bug的程式碼,轉而執行修復後的新版本,解決系統漏洞。

需要

HOOK技術文件

的同學可以

點贊+關注

私信【技術文件】

即可免費領取

【HOOK技術文件】

** 你看,技術就是一柄雙刃劍,善或惡,一念之間。

駭客愛用的HOOK技術大揭秘