HOOK(鉤子技術)
這里的hook我理解的意思就是通過攔截指令,將指令換成自己想要的指令,從而做道繞過原本的程序指令,要修改這個指令,要用匯編技術,從二進制入手。
擴展:
木馬病毒之所以會被攔截掉,是因為現在的安全軟件在內核中的HOOK了大量的關鍵位置,使得病毒木馬的進程、文件、網絡行為都將受到監控,一舉一動都難逃殺軟的眼睛,想要攔截易如反掌。
商業軟件通過逆向分析和lnline HOOK,可以篡改掉商業軟件的注冊校驗機制,讓校驗函數返回成功,繞開軟件的限制。
系統漏洞如何被補丁修復的:統一通過Inline HOOK,操作系統能夠修改原來有bug的代碼,轉而執行修復后的新版本,解決系統漏洞。
總結:
以上就是要介紹的全部HOOK技術了。當然有HOOK,就有反HOOK,很多安全軟件都會檢查關鍵的位置是否被篡改。不僅如此,因為流氓軟件隨意修改系統,Windows從Win7 x64開始加入了PatchGuard機制,針對操作系統核心數據結構都加入了定時檢測機制,一旦發現被篡改,立刻藍屏給你看,而且在隨著系統升級換代,這個檢查的粒度和強度變得越來越強。
Inline HOOK(內聯HOOK)
以下是我自己個人的理解:
這里也就是直接截取指令消息,從而實現自己的目的
Inline HOOK(內聯HOOK)的目標就是直接修改程序編譯后的指令,屬于最基礎也最常見的HOOK技術。
IAT掛鉤
以下是我自己個人的理解:
這里就要和dll劫持漏洞有點相似了,這里因為程序會調用很多dll進行操作,而為了方便調用這些dll,所以會有一個PE文件的導入表IAT存在,而我們要做的,則是修改IAT表中的參數地址,從而實現自己的操作,比如實現自己的dll文件運行。
一個程序的所有代碼一般不會全部都編譯到一個模塊中,分拆到不同的模塊既有利于合作開發,也有利于代碼管理,降低耦合。
動態鏈接庫就提供了這樣的能力,將不同的模塊編譯成一個個的動態庫文件,在使用時引入調用。
在Windows平臺上,動態鏈接庫一般以DLL文件的形式存在,主程序模塊一般是EXE文件形式存在。無論是EXE還是DLL,都是屬于PE文件。
一個模塊引用了哪些模塊的哪些函數,是被記錄在PE文件的導入表IAT中。這個表格位于PE文件的頭部,里面記錄了模塊的名字,函數的名字。
在模塊加載時,模塊加載器將解析對應函數的實際地址,填入到導入表中。
通過修改導入表IAT中函數的地址,這種HOOK叫IAT HOOK。
SEH 鉤子
以下是我自己個人的理解:
這里的意思就是在操作系統異常是,操作系統會向上報告錯誤,如果上級處理不了,就會報告給上上級,要是一直處理不了,則會彈出一個報錯對話框,而我們要修改的也正是這異常處理器中記錄的函數指針,當異常發生時就能獲得執行,從而劫持到執行流!因為這些異常處理器都位于線程的棧空間,修改起來并非難事。
SEH是Windows操作系統上結構化異常處理的縮寫,在代碼中通過try/except來捕獲異常時,操作系統將會在線程的棧空間里安置一個異常處理器(其實就是一個數據結構),里面定義了發生異常時該去執行哪里的代碼處理異常。
異常處理可以多級嵌套,那多個異常處理就構成了一個鏈表,存在于棧空間之上。
當發生異常時,操作系統系統就從最近的異常處理器進行尋求處理,如果能處理則罷了,不能處理就繼續尋求更上一級的異常處理器,直到找到能處理的異常處理器。如果都沒法處理,那對不起,只好彈出那個經典的報錯對話框,進程崩潰。
SEH HOOK針對的目標就是修改這些異常處理器中記錄的函數指針,當異常發生時就能獲得執行,從而劫持到執行流!因為這些異常處理器都位于線程的棧空間,修改起來并非難事。
C++ 可觸發的 HOOK
以下是我自己個人的理解:
這里也就是修改用c++寫的程序中的虛函數表中的地址,從而實現自己的目的
C++是一門面向對象的編程語言,支持面向對象的三大特性:封裝性、繼承性、多態性。
其中的多態性,各個C++編譯器基本上都是通過一種叫虛函數表的機制來實現。
下面通過一個實際的例子來感受一下虛函數表在C++多態性上發揮的作用。
#include <iostream> 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; } |
輸出:
通過上面的輸出,可以看到,fish和cat對象都只占據4個字節。因為這兩個類都沒有成員變量,唯一需要存儲的就是一個虛函數表指針。
以cat對象為例,看一下它的地址,是0x005cfc30:
看一下這個地址起始的4個字節,是什么:
虛表指針是0x000d9b90(這里需要注意字節順序)。
通過這個地址,找到虛函數表,里面有兩個函數地址:
查看這兩個地址,都是指向了一個jmp指令,分別跳到了Cat類的兩個虛函數。
通過上面的實例,總結一下對象、虛函數表和虛函數代碼之間的關系如下圖所示:
每個包含虛函數的類對象,在內存中都有一個指針,位于對象頭部,指向的是一個虛函數表,表中的每一項都是虛函數地址。
類繼承后,如果重寫了父類的虛函數,子類對象指向的表格中對應函數的地址將會更新為子類的函數。
這樣,使用父類指針指向子類對象,通過指針調用虛函數時,就能調用到子類重寫的虛函數,從而實現多態性。
既然是記錄函數地址的表格,那就有存在被篡改的可能,這就是C++ virtable HOOK。
通過篡改對應虛函數的地址,實現對相應函數調用的攔截。
實施這種HOOK,需要逆向分析目標C++對象的結構,掌握虛函數表中各個函數的位置,才能精準打擊。
上面幾種HOOK,修改的都是應用層的函數指針,而操作系統內核中還有一些非常重要的表格,它們的表項中記錄了一些更加關鍵的函數,HOOK這些表格中的函數是非常高危的操作,操作不當將導致操作系統崩潰。當然,高風險高回報,HOOK這些函數,能實現一些非常強大的功能,是病毒、木馬、安全軟件非常愛干的事情。
SSDT 掛鉤
以下是我自己個人的理解:
因為操作系統會提供給程序API接口,來進行消息交互,而在操作系統中,會將所有的系統服務函數地址存放在一張表中,而這張表在windows中叫做SSDT,我們要修改的也正是這張表中的函數地址,從而實現自己的操作。
系統調用是操作系統提供給應用程序的編程接口API,應用程序通過這些API得以操作計算機的資源(如進程、網絡、文件等)。
執行系統調用的時候,CPU將從用戶模式切換到內核模式,進入內核后,將會根據系統調用的API編號,去找到對應的系統服務函數,實現對應API的功能。
操作系統將所有的系統服務函數地址,存放在了一個表格中,這個表格就是系統服務描述符表。在Linux上,這個表格的名字叫sys_call_table,在Windows上,它叫KeServiceDescriptorTable,簡稱SSDT。
Windows上的SSDT向來是兵家必爭之地,安全軟件為了監控應用程序的行為,通常都會替換SSDT表格中的系統服務函數地址為它們的函數。當系統調用觸發時,安全軟件將會及時知曉,并通過應用程序的參數來判定是否“放行”這次調用。
IDT 掛鉤
以下是我自己個人的理解:
這里是在CPU異常的情況下,會根據知道好的函數跳轉道哪里去的操作,而該轉向哪里去處理這些情況的函數地址,而這些地址會存放道一張表中,讓CPU進行查閱,而這里有所不同的是,CPU異常后可能跳轉的次數有點多,所有會有多種表存放了跳轉的函數地址,所以我們要進行修改的話必須知道每張表中的函數地址,這樣我們才可以進行函數地址的修改,從而實現自己的操作。
內核中除了記錄系統服務的SSDT,還有一個非常重要的表格:中斷描述符表IDT。
IDT用于記錄CPU執行過程中遇到中斷、異常等情況時,該轉向哪里去處理這些情況的函數地址。
HOOK IDT有一個注意事項,不同于SSDT是全局唯一的,IDT是與CPU核心緊密相關的,對于多核處理器,會對應多個IDT表。如果想通過HOOK IDT中的函數來搞事情的話,可能需要同時處理多個表。
除了直接修改函數指令和修改函數指針之外,還有一類特殊的HOOK,它們通過系統提供的機制攔截某些消息、通知,從而有機會介入監聽、攔截。
IRP HOOK
以下是我自己個人的理解:
這個hook技術就是去截獲驅動程序發送出去的消息并進行篡改,從而實現自己想要的操作
在Windows系統上,用戶程序和內核驅動之間的交互是通過一種稱為IRP的數據結構實現的,你可以簡單將其理解為應用程序發送了一個消息下去,這個消息就是一個IRP。
而接收消息的目標,是驅動程序創建的設備Device。注意,這個設備不一定是物理設備,也可能完全不存在的虛擬設備,驅動程序可以任意創建一個不存在的設備。
Windows內核中提供了驅動設備的掛載操作,允許別的驅動程序對指定設備進行掛載,從而可以截獲發送給該設備的“消息”,這種HOOK方式被稱為IRP HOOK。
國內一些安全軟件為了互相攻擊,經常用這種方式攔截對方驅動程序的消息,從而可以“保護”自己不被對方干掉。
TDI HOOK && NDIS HOOK
以下是我自己個人的理解:
這里就是在windows內核中的網絡結構層次中進行截獲消息,之所以能夠截獲消息,是因為TDI和NDIS這兩個標準的接口,因為這兩個接口可以用來被理解成事用來分別向各層傳遞消息用的,而windows為了擴展支持,允許類似防火墻之類的軟件通過這些接口接入,從而能夠截獲到網絡通信流量,進行安全審計,而我們也正是裝了這個空子,從而截獲指令消息
這兩種HOOK方式與Windows內核中的網絡子系統密切相關。
Windows內核中的網絡結構是分層式設計。從最上層的API socket層、到TCP/IP協議棧層、再到底層的網卡驅動程序,分了很多個層次。
而層與層之間的交互,是通過一系列標準接口來實現的,其中最重要的兩個接口標準就是TDI和NDIS。TDI封裝了不同協議棧的差異(Windows不止支持TCP/IP協議棧)提供給上層統一的調用接口。NDIS則封裝了底層不同網卡的驅動程序接口差異,提供給上層統一的收發數據包接口。
Windows為了擴展性支持,允許類似防火墻之類的軟件通過這些接口接入,從而能夠截獲到網絡通信流量,進行安全審計。
既然開了這些接口,一些流氓軟件和木馬病毒也就盯上了它們,通過這些接口就能輕松監聽、篡改網絡數據,達到邪惡的目的。
Windows Message HOOK
以下是我自己個人的理解:
這個就是應用與程序中的,我們日常應用程序時,程序和系統之間會有交互的,而windows通過開啟API接口和這些程序進行交互消息,而也正是因為API接口的開啟,使得我們可以截獲指令消息從而篡改
Windows操作系統的UI交互是以消息來驅動的,用戶的鍵盤輸入、鼠標操作都會被操作系統以消息的形式發送到各個應用程序處理。
Windows提供了API接口,可以被程序用于捕獲這些消息,從而實現一些特定的功能。
這種機制叫做Windows消息鉤子,最常見的就要數鍵盤鉤子了,在十多年前流氓軟件和木馬病毒大行其道的時候,這些惡意軟件經常喜歡通過這種方式來監聽用戶的鍵盤輸入,從而來盜取QQ密碼(當然,現在肯定是不行的了)。