一、
序言對大多數(shù)的Windows開發(fā)者來說,如何在Win32系統(tǒng)中對API函數(shù)的調(diào)用進(jìn)行攔截一直是項(xiàng)極富挑戰(zhàn)性的課題,因?yàn)檫@將是對你所掌握的計(jì)算機(jī)知識較為全面的考驗(yàn),尤其是一些在如今使用RAD進(jìn)行軟件開發(fā)時并不常用的知識,這包括了操作系統(tǒng)原理、匯編語言甚至是關(guān)于機(jī)器指令代碼的(聽上去真是有點(diǎn)恐怖,不過這是事實(shí))。
當(dāng)前廣泛使用的Windows操作系統(tǒng)中,像Win 9x和Win NT/2K,都提供了一種比較穩(wěn)健的機(jī)制來使得各個進(jìn)程的內(nèi)存地址空間之間是相互獨(dú)立,也就是說一個進(jìn)程中的某個有效的內(nèi)存地址對另一個進(jìn)程來說是無意義的,這種內(nèi)存保護(hù)措施大大增加了系統(tǒng)的穩(wěn)定性。不過,這也使得進(jìn)行系統(tǒng)級的API攔截的工作的難度也大大加大了。
當(dāng)然,我這里所指的是比較文雅的攔截方式,通過修改可執(zhí)行文件在內(nèi)存中的映像中有關(guān)代碼,實(shí)現(xiàn)對API調(diào)用的動態(tài)攔截;而不是采用比較暴力的方式,直接對可執(zhí)行文件的磁盤存儲中機(jī)器代碼進(jìn)行改寫。
二、
API鉤子系統(tǒng)一般框架通常,我們把攔截API的調(diào)用的這個過程稱為是安裝一個API鉤子(API Hook)。一個API鉤子至少有兩個模塊組成:一個是鉤子服務(wù)器(Hook Server)模塊,一般為EXE的形式;一個是鉤子驅(qū)動器(Hook Driver)模塊,一般為DLL的形式。
服務(wù)器主要負(fù)責(zé)向目標(biāo)進(jìn)程注入驅(qū)動器,使得驅(qū)動器工作在目標(biāo)進(jìn)程的地址空間中,這是關(guān)鍵的第一步。驅(qū)動器則負(fù)責(zé)實(shí)際的API攔截工作,以便在我們所關(guān)心的API函數(shù)調(diào)用的前后能做一些我們需要的工作。
一個大家比較常見的API鉤子的例子就是一些實(shí)時翻譯軟件(像金山詞霸)中必備的的功能:屏幕抓詞,它主要是對一些GDI 函數(shù)進(jìn)行了攔截,獲取它們的輸入?yún)?shù)中的字符串,然后在自己的窗口中顯示出來。針對上述的兩個部分,有以下兩點(diǎn)需要我們重點(diǎn)考慮的: 選用何種DLL注入技術(shù) 采用何種API攔截機(jī)制
三、
注入技術(shù)的選用由于在Win32系統(tǒng)中各個進(jìn)程的地址是互相獨(dú)立的,因此我們無法在一個進(jìn)程中對另一個進(jìn)程的代碼進(jìn)行有效的修改。而你要完成API鉤子的工作就必須進(jìn)行這種操作。因此,我們必須采取某種獨(dú)特的手段,使得API鉤子(準(zhǔn)確的說是鉤子驅(qū)動器)能夠成為目標(biāo)進(jìn)程中的一部分,才有較大的可能來對目標(biāo)進(jìn)程數(shù)據(jù)和代碼進(jìn)行有控制的修改。
通常有以下幾種注入方式:
1.利用注冊表如果我們準(zhǔn)備攔截的進(jìn)程連接了User32.dll,也就是使用了User32中的API(一般圖形界面的應(yīng)用程序都符合這個條件),那么就可以簡單把你的鉤子驅(qū)動器DLL的名字作為值添加在下面注冊表的鍵下: HKEY_LOCAL_MACHINE\\Software\\Microsoft\\WindowsNT\\CurrentVersion\\Windows\\AppInit_DLLs 值的形式可以為單個DLL的文件名,或者是一組DLL的文件名,相鄰的名稱之間用逗號或空格間隔。所有由該值標(biāo)識的DLL將在符合條件的應(yīng)用程序啟動的時候裝載。這是一個操作系統(tǒng)內(nèi)建的機(jī)制,相對其他方式來說危險(xiǎn)性較小,但它有一些比較明顯的缺點(diǎn): 該方法僅適用于NT/2K操作系統(tǒng)??纯存I的名稱你就應(yīng)該明白 為了激活或停止鉤子的注入,必須重新啟動Windows。這個就似乎太不方便了 不能用此方法向沒有使用User32的應(yīng)用程序注入DLL,例如控制臺應(yīng)用程序 不管需要與否,鉤子DLL將注入每一個GUI應(yīng)用程序,這將導(dǎo)致整個系統(tǒng)性能的下降
2.
建立系統(tǒng)范圍的Windows鉤子要向某個進(jìn)程注入DLL,一個十分普遍也是比較簡單的方法就是建立在標(biāo)準(zhǔn)的Windows鉤子的基礎(chǔ)上。Windows鉤子一般是在DLL中實(shí)現(xiàn)的,這是一個全局性的Windows鉤子的基本要求,這也符合我們的需要。當(dāng)我們成功地調(diào)用SetWindowsHookEx函數(shù)之后,便在系統(tǒng)中安裝了某種類型的消息鉤子,這個鉤子可以是針對某個進(jìn)程,也可以是針對系統(tǒng)中的所有進(jìn)程。一旦某個進(jìn)程中產(chǎn)生了該類型的消息,操作系統(tǒng)會自動把該鉤子所在的DLL映像到該進(jìn)程的地址空間中,從而使得消息回調(diào)函數(shù)(在SetWindowsHookEx的參數(shù)中指定)能夠?qū)Υ讼⑦M(jìn)行適當(dāng)?shù)奶幚?,在這里,我們所感興趣的當(dāng)然不是對消息進(jìn)行什么處理,因此在消息回調(diào)函數(shù)中只需把消息鉤子向后傳遞就可以了,但是我們所需的DLL已經(jīng)成功地注入了目標(biāo)進(jìn)程的地址空間,從而可以完成后續(xù)工作。
我們知道,不同進(jìn)程中使用的DLL之間是不能直接共享數(shù)據(jù)的,因?yàn)樗鼈兓顒釉诓煌牡刂房臻g中。但在Windows鉤子DLL中,有一些數(shù)據(jù),例如Windows鉤子句柄HHook,這是由SetWindowsHookEx函數(shù)返回值得到的,并且作為參數(shù)將在CallNextHookEx函數(shù)和UnhookWindoesHookEx函數(shù)中使用,顯然使用SetWindowsHookEx函數(shù)的進(jìn)程和使用CallNextHookEx函數(shù)的進(jìn)程一般不會是同一個進(jìn)程,因此我們必須能夠使句柄在所有的地址空間中都是有效的有意義的,也就是說,它的值必須必須在這些鉤子DLL所掛鉤的進(jìn)程之間是共享的。為了達(dá)到這個目的,我們就應(yīng)該把它存儲在一個共享的數(shù)據(jù)區(qū)域中。
在VC++中我們可以采用預(yù)編譯指令#pragma data_seg在DLL文件中創(chuàng)建一個新的段,并且在DEF文件中把該段的屬性設(shè)置為“shared”,這樣就建立了一個共享數(shù)據(jù)段。對于使用Delphi的人來說就沒有這么幸運(yùn)了:沒有類似的比較簡單的方法(或許是有的,但我沒有找到)。不過我們還是可以利用內(nèi)存映像技術(shù)來申請使用一塊各進(jìn)程可以共享的內(nèi)存區(qū)域,主要是利用了CreateFileMapping和MapViewOfFile這兩個函數(shù)。這倒是一個通用的方法,適合所有的開發(fā)語言,只要它能使用Windows的API。
在Borland的BCB中有一個指令#pragma codeseg與VC++中的#pragma data_seg指令有點(diǎn)類似,應(yīng)該也能起到一樣的作用,但我試了一下,沒有沒有效果,而BCB的聯(lián)機(jī)幫助中對此也提到的不多,不知怎樣才能正確的使用。一旦鉤子DLL加載進(jìn)入目標(biāo)進(jìn)程的地址空間后,在我們調(diào)用UnHookWindowsHookEx函數(shù)之前是無法使它停止工作的,除非目標(biāo)進(jìn)程關(guān)閉。
這種DLL注入方式有兩個優(yōu)點(diǎn): 這種機(jī)制在Win 9x/Me和Win NT/2K中都是得到支持的,預(yù)計(jì)在以后的版本中也將得到支持 鉤子DLL可以在不需要的時候,可由我們主動的調(diào)用UnHookWindowsHookEx來卸載,比起使用注冊表的機(jī)制來說方便了許多盡管這是一種相當(dāng)簡潔明了的方法,但它也有一些顯而易見的缺點(diǎn): 首先值得我們注意的是,Windows鉤子將會降低整個系統(tǒng)的性能,因?yàn)樗~外增加了系統(tǒng)在消息處理方面的時間 其次,只有當(dāng)目標(biāo)進(jìn)程準(zhǔn)備接受某種消息時,鉤子所在的DLL才會被系統(tǒng)映射到該進(jìn)程的地址空間中,鉤子才能真正開始發(fā)揮作用。因此如果我們要對某些進(jìn)程的整個生命周期內(nèi)的API調(diào)用情況進(jìn)行監(jiān)控,用這種方法顯然會遺漏某些API的調(diào)用
3.
使用 CreateRemoteThread函數(shù)在我看來這是一個相當(dāng)棒的方法,然而不幸的是,CreateRemoteThread這個函數(shù)只能在Win NT/2K系統(tǒng)中才得到支持,雖然在Win 9x中這個API也能被安全的調(diào)用而不出錯,但它除了返回一個空值之外什么也不做。整個DLL注入過程十分簡單。我們知道,任何一個進(jìn)程都可以使用LoadLibrary來動態(tài)地加載一個DLL。但問題是,我們?nèi)绾巫屇繕?biāo)進(jìn)程在我們的控制下來加載我們的鉤子DLL(也就是鉤子驅(qū)動器)呢?這里有一個API函數(shù)CreateRemoteThread,通過它可在一個進(jìn)程中可建立并運(yùn)行一個遠(yuǎn)程的線程。
調(diào)用該API需要指定一個線程函數(shù)指針作為參數(shù),該線程函數(shù)的原型如下: Function ThreadProc(lpParam: Pointer): DWORD;我們再來看一下LoadLibrary的函數(shù)原型: Function LoadLibrary(lpFileName: PChar): HModule;可以看出,這兩個函數(shù)原型實(shí)質(zhì)上是完全相同的(其實(shí)返回值是否相同關(guān)系不大,因?yàn)槲覀兪菬o法得到遠(yuǎn)程線程函數(shù)的返回值的),只是叫法不同而已,這種相同使得我們可以把直接把LoadLibrary當(dāng)做線程函數(shù)來使用,從而在目標(biāo)進(jìn)程中加載鉤子DLL。
類似的,當(dāng)我們需要卸載鉤子DLL時,也可以FreeLibrary作為線程函數(shù)來使用,在目標(biāo)進(jìn)程中移去鉤子DLL。一切看來是十分的簡潔方便。通過調(diào)用GetProcAddress函數(shù),我們可以得到LoadLibrary函數(shù)的地址。由于LoadLibrary是Kernel32中的函數(shù),而這個系統(tǒng)DLL的映射地址對每一個進(jìn)程來說都是相同的,因此LoadLibrary函數(shù)的地址也是如此。這點(diǎn)將確保我們能把該函數(shù)的地址作為一個有效的參數(shù)傳遞給CreateRemoteThread使用。
AddrOfLoadLibrary := GetProcAddress(GetModuleHandle(‘Kernel32.dll’), ‘LoadLibrary’);
HremoteThread := CreateRemoteThread(HTargetProcess, nil, 0, AddrOfLoadLibrary, HookDllName, 0, nil);
要使用CreateRemoteThread,我們需要目標(biāo)進(jìn)程的句柄作為參數(shù)。當(dāng)我們用OpenProcess函數(shù)來得到進(jìn)程的句柄時,通常是希望對此進(jìn)程有全權(quán)的存取操作,也就是以PROCESS_ALL_ACCESS為標(biāo)志打開進(jìn)程。但對于一些系統(tǒng)級的進(jìn)程,直接這樣顯然是不行的,只能返回一個的空句柄(值為零)。為此,我們必須把自己設(shè)置為擁有調(diào)試級的特權(quán),這樣將具有大的存取權(quán)限,從而使得我們能對這些系統(tǒng)級的進(jìn)程也可以進(jìn)行一些必要的操作。
4.
通過BHO來注入DLL 有時,我們想要注入DLL的對象僅僅是Internet Explorer。幸運(yùn)的是,Windows操作系統(tǒng)為我們提供了一個簡單的歸檔的方法(這保證了它的可靠性)―― 利用Browser Helper Objects(BHO)。一個BHO是一個在 DLL中實(shí)現(xiàn)的COM對象,它主要實(shí)現(xiàn)了一個IObjectWithSite接口,而每當(dāng)IE運(yùn)行時,它會自動加載所有實(shí)現(xiàn)了該接口的COM對象。
序言對大多數(shù)的Windows開發(fā)者來說,如何在Win32系統(tǒng)中對API函數(shù)的調(diào)用進(jìn)行攔截一直是項(xiàng)極富挑戰(zhàn)性的課題,因?yàn)檫@將是對你所掌握的計(jì)算機(jī)知識較為全面的考驗(yàn),尤其是一些在如今使用RAD進(jìn)行軟件開發(fā)時并不常用的知識,這包括了操作系統(tǒng)原理、匯編語言甚至是關(guān)于機(jī)器指令代碼的(聽上去真是有點(diǎn)恐怖,不過這是事實(shí))。
當(dāng)前廣泛使用的Windows操作系統(tǒng)中,像Win 9x和Win NT/2K,都提供了一種比較穩(wěn)健的機(jī)制來使得各個進(jìn)程的內(nèi)存地址空間之間是相互獨(dú)立,也就是說一個進(jìn)程中的某個有效的內(nèi)存地址對另一個進(jìn)程來說是無意義的,這種內(nèi)存保護(hù)措施大大增加了系統(tǒng)的穩(wěn)定性。不過,這也使得進(jìn)行系統(tǒng)級的API攔截的工作的難度也大大加大了。
當(dāng)然,我這里所指的是比較文雅的攔截方式,通過修改可執(zhí)行文件在內(nèi)存中的映像中有關(guān)代碼,實(shí)現(xiàn)對API調(diào)用的動態(tài)攔截;而不是采用比較暴力的方式,直接對可執(zhí)行文件的磁盤存儲中機(jī)器代碼進(jìn)行改寫。
二、
API鉤子系統(tǒng)一般框架通常,我們把攔截API的調(diào)用的這個過程稱為是安裝一個API鉤子(API Hook)。一個API鉤子至少有兩個模塊組成:一個是鉤子服務(wù)器(Hook Server)模塊,一般為EXE的形式;一個是鉤子驅(qū)動器(Hook Driver)模塊,一般為DLL的形式。
服務(wù)器主要負(fù)責(zé)向目標(biāo)進(jìn)程注入驅(qū)動器,使得驅(qū)動器工作在目標(biāo)進(jìn)程的地址空間中,這是關(guān)鍵的第一步。驅(qū)動器則負(fù)責(zé)實(shí)際的API攔截工作,以便在我們所關(guān)心的API函數(shù)調(diào)用的前后能做一些我們需要的工作。
一個大家比較常見的API鉤子的例子就是一些實(shí)時翻譯軟件(像金山詞霸)中必備的的功能:屏幕抓詞,它主要是對一些GDI 函數(shù)進(jìn)行了攔截,獲取它們的輸入?yún)?shù)中的字符串,然后在自己的窗口中顯示出來。針對上述的兩個部分,有以下兩點(diǎn)需要我們重點(diǎn)考慮的: 選用何種DLL注入技術(shù) 采用何種API攔截機(jī)制
三、
注入技術(shù)的選用由于在Win32系統(tǒng)中各個進(jìn)程的地址是互相獨(dú)立的,因此我們無法在一個進(jìn)程中對另一個進(jìn)程的代碼進(jìn)行有效的修改。而你要完成API鉤子的工作就必須進(jìn)行這種操作。因此,我們必須采取某種獨(dú)特的手段,使得API鉤子(準(zhǔn)確的說是鉤子驅(qū)動器)能夠成為目標(biāo)進(jìn)程中的一部分,才有較大的可能來對目標(biāo)進(jìn)程數(shù)據(jù)和代碼進(jìn)行有控制的修改。
通常有以下幾種注入方式:
1.利用注冊表如果我們準(zhǔn)備攔截的進(jìn)程連接了User32.dll,也就是使用了User32中的API(一般圖形界面的應(yīng)用程序都符合這個條件),那么就可以簡單把你的鉤子驅(qū)動器DLL的名字作為值添加在下面注冊表的鍵下: HKEY_LOCAL_MACHINE\\Software\\Microsoft\\WindowsNT\\CurrentVersion\\Windows\\AppInit_DLLs 值的形式可以為單個DLL的文件名,或者是一組DLL的文件名,相鄰的名稱之間用逗號或空格間隔。所有由該值標(biāo)識的DLL將在符合條件的應(yīng)用程序啟動的時候裝載。這是一個操作系統(tǒng)內(nèi)建的機(jī)制,相對其他方式來說危險(xiǎn)性較小,但它有一些比較明顯的缺點(diǎn): 該方法僅適用于NT/2K操作系統(tǒng)??纯存I的名稱你就應(yīng)該明白 為了激活或停止鉤子的注入,必須重新啟動Windows。這個就似乎太不方便了 不能用此方法向沒有使用User32的應(yīng)用程序注入DLL,例如控制臺應(yīng)用程序 不管需要與否,鉤子DLL將注入每一個GUI應(yīng)用程序,這將導(dǎo)致整個系統(tǒng)性能的下降
2.
建立系統(tǒng)范圍的Windows鉤子要向某個進(jìn)程注入DLL,一個十分普遍也是比較簡單的方法就是建立在標(biāo)準(zhǔn)的Windows鉤子的基礎(chǔ)上。Windows鉤子一般是在DLL中實(shí)現(xiàn)的,這是一個全局性的Windows鉤子的基本要求,這也符合我們的需要。當(dāng)我們成功地調(diào)用SetWindowsHookEx函數(shù)之后,便在系統(tǒng)中安裝了某種類型的消息鉤子,這個鉤子可以是針對某個進(jìn)程,也可以是針對系統(tǒng)中的所有進(jìn)程。一旦某個進(jìn)程中產(chǎn)生了該類型的消息,操作系統(tǒng)會自動把該鉤子所在的DLL映像到該進(jìn)程的地址空間中,從而使得消息回調(diào)函數(shù)(在SetWindowsHookEx的參數(shù)中指定)能夠?qū)Υ讼⑦M(jìn)行適當(dāng)?shù)奶幚?,在這里,我們所感興趣的當(dāng)然不是對消息進(jìn)行什么處理,因此在消息回調(diào)函數(shù)中只需把消息鉤子向后傳遞就可以了,但是我們所需的DLL已經(jīng)成功地注入了目標(biāo)進(jìn)程的地址空間,從而可以完成后續(xù)工作。
我們知道,不同進(jìn)程中使用的DLL之間是不能直接共享數(shù)據(jù)的,因?yàn)樗鼈兓顒釉诓煌牡刂房臻g中。但在Windows鉤子DLL中,有一些數(shù)據(jù),例如Windows鉤子句柄HHook,這是由SetWindowsHookEx函數(shù)返回值得到的,并且作為參數(shù)將在CallNextHookEx函數(shù)和UnhookWindoesHookEx函數(shù)中使用,顯然使用SetWindowsHookEx函數(shù)的進(jìn)程和使用CallNextHookEx函數(shù)的進(jìn)程一般不會是同一個進(jìn)程,因此我們必須能夠使句柄在所有的地址空間中都是有效的有意義的,也就是說,它的值必須必須在這些鉤子DLL所掛鉤的進(jìn)程之間是共享的。為了達(dá)到這個目的,我們就應(yīng)該把它存儲在一個共享的數(shù)據(jù)區(qū)域中。
在VC++中我們可以采用預(yù)編譯指令#pragma data_seg在DLL文件中創(chuàng)建一個新的段,并且在DEF文件中把該段的屬性設(shè)置為“shared”,這樣就建立了一個共享數(shù)據(jù)段。對于使用Delphi的人來說就沒有這么幸運(yùn)了:沒有類似的比較簡單的方法(或許是有的,但我沒有找到)。不過我們還是可以利用內(nèi)存映像技術(shù)來申請使用一塊各進(jìn)程可以共享的內(nèi)存區(qū)域,主要是利用了CreateFileMapping和MapViewOfFile這兩個函數(shù)。這倒是一個通用的方法,適合所有的開發(fā)語言,只要它能使用Windows的API。
在Borland的BCB中有一個指令#pragma codeseg與VC++中的#pragma data_seg指令有點(diǎn)類似,應(yīng)該也能起到一樣的作用,但我試了一下,沒有沒有效果,而BCB的聯(lián)機(jī)幫助中對此也提到的不多,不知怎樣才能正確的使用。一旦鉤子DLL加載進(jìn)入目標(biāo)進(jìn)程的地址空間后,在我們調(diào)用UnHookWindowsHookEx函數(shù)之前是無法使它停止工作的,除非目標(biāo)進(jìn)程關(guān)閉。
這種DLL注入方式有兩個優(yōu)點(diǎn): 這種機(jī)制在Win 9x/Me和Win NT/2K中都是得到支持的,預(yù)計(jì)在以后的版本中也將得到支持 鉤子DLL可以在不需要的時候,可由我們主動的調(diào)用UnHookWindowsHookEx來卸載,比起使用注冊表的機(jī)制來說方便了許多盡管這是一種相當(dāng)簡潔明了的方法,但它也有一些顯而易見的缺點(diǎn): 首先值得我們注意的是,Windows鉤子將會降低整個系統(tǒng)的性能,因?yàn)樗~外增加了系統(tǒng)在消息處理方面的時間 其次,只有當(dāng)目標(biāo)進(jìn)程準(zhǔn)備接受某種消息時,鉤子所在的DLL才會被系統(tǒng)映射到該進(jìn)程的地址空間中,鉤子才能真正開始發(fā)揮作用。因此如果我們要對某些進(jìn)程的整個生命周期內(nèi)的API調(diào)用情況進(jìn)行監(jiān)控,用這種方法顯然會遺漏某些API的調(diào)用
3.
使用 CreateRemoteThread函數(shù)在我看來這是一個相當(dāng)棒的方法,然而不幸的是,CreateRemoteThread這個函數(shù)只能在Win NT/2K系統(tǒng)中才得到支持,雖然在Win 9x中這個API也能被安全的調(diào)用而不出錯,但它除了返回一個空值之外什么也不做。整個DLL注入過程十分簡單。我們知道,任何一個進(jìn)程都可以使用LoadLibrary來動態(tài)地加載一個DLL。但問題是,我們?nèi)绾巫屇繕?biāo)進(jìn)程在我們的控制下來加載我們的鉤子DLL(也就是鉤子驅(qū)動器)呢?這里有一個API函數(shù)CreateRemoteThread,通過它可在一個進(jìn)程中可建立并運(yùn)行一個遠(yuǎn)程的線程。
調(diào)用該API需要指定一個線程函數(shù)指針作為參數(shù),該線程函數(shù)的原型如下: Function ThreadProc(lpParam: Pointer): DWORD;我們再來看一下LoadLibrary的函數(shù)原型: Function LoadLibrary(lpFileName: PChar): HModule;可以看出,這兩個函數(shù)原型實(shí)質(zhì)上是完全相同的(其實(shí)返回值是否相同關(guān)系不大,因?yàn)槲覀兪菬o法得到遠(yuǎn)程線程函數(shù)的返回值的),只是叫法不同而已,這種相同使得我們可以把直接把LoadLibrary當(dāng)做線程函數(shù)來使用,從而在目標(biāo)進(jìn)程中加載鉤子DLL。
類似的,當(dāng)我們需要卸載鉤子DLL時,也可以FreeLibrary作為線程函數(shù)來使用,在目標(biāo)進(jìn)程中移去鉤子DLL。一切看來是十分的簡潔方便。通過調(diào)用GetProcAddress函數(shù),我們可以得到LoadLibrary函數(shù)的地址。由于LoadLibrary是Kernel32中的函數(shù),而這個系統(tǒng)DLL的映射地址對每一個進(jìn)程來說都是相同的,因此LoadLibrary函數(shù)的地址也是如此。這點(diǎn)將確保我們能把該函數(shù)的地址作為一個有效的參數(shù)傳遞給CreateRemoteThread使用。
AddrOfLoadLibrary := GetProcAddress(GetModuleHandle(‘Kernel32.dll’), ‘LoadLibrary’);
HremoteThread := CreateRemoteThread(HTargetProcess, nil, 0, AddrOfLoadLibrary, HookDllName, 0, nil);
要使用CreateRemoteThread,我們需要目標(biāo)進(jìn)程的句柄作為參數(shù)。當(dāng)我們用OpenProcess函數(shù)來得到進(jìn)程的句柄時,通常是希望對此進(jìn)程有全權(quán)的存取操作,也就是以PROCESS_ALL_ACCESS為標(biāo)志打開進(jìn)程。但對于一些系統(tǒng)級的進(jìn)程,直接這樣顯然是不行的,只能返回一個的空句柄(值為零)。為此,我們必須把自己設(shè)置為擁有調(diào)試級的特權(quán),這樣將具有大的存取權(quán)限,從而使得我們能對這些系統(tǒng)級的進(jìn)程也可以進(jìn)行一些必要的操作。
4.
通過BHO來注入DLL 有時,我們想要注入DLL的對象僅僅是Internet Explorer。幸運(yùn)的是,Windows操作系統(tǒng)為我們提供了一個簡單的歸檔的方法(這保證了它的可靠性)―― 利用Browser Helper Objects(BHO)。一個BHO是一個在 DLL中實(shí)現(xiàn)的COM對象,它主要實(shí)現(xiàn)了一個IObjectWithSite接口,而每當(dāng)IE運(yùn)行時,它會自動加載所有實(shí)現(xiàn)了該接口的COM對象。

