資源描述:
《托管注入深入研究》由會(huì)員上傳分享,免費(fèi)在線閱讀,更多相關(guān)內(nèi)容在工程資料-天天文庫。
1、托管注入深入研究~教育資源庫 這是我發(fā)表在《黑客防線》2008年12期上的一篇文章,這里是網(wǎng)絡(luò)上的首發(fā)?! 「郊俏覍懙囊粋€(gè)工具超級(jí)間諜(SuperSpy),主要包括一個(gè)窗口探測(cè)功能,甚至能夠探測(cè)到VC自帶的SPY++所不能探測(cè)到的窗口;當(dāng)然,還包括這里提到的托管注入功能?! 〕绦蛐柽\(yùn)行在.Net2.0以上,如果需要使用注入功能,則需要.Net3.5以上。 超級(jí)間諜下載source/850509 網(wǎng)上關(guān)于dll注入的文章實(shí)在太多,但基本上都是針對(duì)ain,只要我們把代碼放到DllMain里,就可以被調(diào)用了?! ‖F(xiàn)在我們來看托管dll的注入: 用C#或VB.寫的dll
2、沒有DllMain函數(shù),我們自然想到了功能強(qiáng)大的C++。通常,我們把用C#或VB.寫的dll,或者用C++寫的,但編譯為/clr:pure的dll稱為托管dll 而把用C++編寫的,但編譯為/clr的dll稱為混合dll?! 』旌蟙ll也可以調(diào)用托管代碼,所以也可以將其稱為托管dll,本文所說的托管dll注入,實(shí)際上是混合dll的注入?! ∥覀兪紫认氲降氖怯贸R?guī)方法來注入混合dll,結(jié)果會(huì)發(fā)現(xiàn):只要在DllMain函數(shù)里調(diào)用了托管代碼,程序就會(huì)崩潰?! ∫苍S你還會(huì)想到下面的方法: 定義一個(gè)類,在其構(gòu)造函數(shù)里調(diào)用托管代碼,然后在全局域里定義這個(gè)類的一個(gè)變量,當(dāng)我們這樣做
3、了后會(huì)發(fā)現(xiàn),注入后,什么也沒有執(zhí)行?! 〔殚哅SDN,我們找到了答案: DllMain不能直接或間接地調(diào)用托管代碼,并且全局變量不會(huì)進(jìn)行初始化?! ∵@樣的結(jié)果讓人非常沮喪! 難道真的就沒有辦法了嗎? 網(wǎng)友CiCi給出了一個(gè)解決方案: 寫一個(gè)混合dll,在其中定義一個(gè)導(dǎo)出函數(shù),在這個(gè)導(dǎo)出函數(shù)里可以調(diào)用托管代碼?! ∪缓髮懸粋€(gè)非托管dll(也就是ain函數(shù)里調(diào)用前面那個(gè)混合dll的導(dǎo)出函數(shù)。 這個(gè)方案能夠解決問題,并且我在一段時(shí)間里,也一直使用這個(gè)方法。但總覺得不完美:必須使用兩個(gè)dll?! ∮袥]有辦法用一個(gè)dll就解決問題呢?答案是肯定的?! ∥以谝淮闻既坏臋C(jī)會(huì)中
4、,發(fā)現(xiàn)了一個(gè)現(xiàn)象: 如里用鉤子來實(shí)現(xiàn)注入,則全局變量會(huì)得到初始化。這個(gè)發(fā)現(xiàn)讓我非常困惑:為什么遠(yuǎn)程線程注入就不會(huì)初始化呢? 我考慮這兩種方法的區(qū)別:鉤子注入時(shí),宿主會(huì)調(diào)用dll中的鉤子回調(diào)函數(shù)?! ∮谑俏掖竽懺O(shè)想,只要宿主調(diào)用了dll中的任意一個(gè)函數(shù),全局變量就會(huì)得到初始化。 于是我在dll中定義了一個(gè)空實(shí)現(xiàn)的函數(shù)(因?yàn)槲业哪康厥瞧仁谷肿兞砍跏蓟?,而不是去?zhí)行這個(gè)函數(shù)) 結(jié)果正如我所料,全局變量被初始化了,其構(gòu)造函數(shù)中的托管代碼被調(diào)用了! 這里其實(shí)已經(jīng)實(shí)現(xiàn)了托管代碼的注入,當(dāng)然,還遠(yuǎn)遠(yuǎn)不夠完善。 這時(shí)候,我又覺得全局變量是多余的了:既然我能夠使宿主調(diào)用dll
5、中的一個(gè)特定函數(shù),為什么不直接把托管代碼放到這個(gè)函數(shù)中呢? 我馬上進(jìn)行了測(cè)試,也成功了?! ≈拔沂沁@樣實(shí)現(xiàn)讓宿主調(diào)用dll中的一個(gè)特定函數(shù)的: 在dll被注入之后,我在DllMain函數(shù)里將這個(gè)特定函數(shù)的地址寫到共享內(nèi)存中(這時(shí)DllMain里沒有調(diào)用托管代碼,所以可以執(zhí)行),然后主程序讀取共享內(nèi)存中的值,再通過遠(yuǎn)程線程迫使宿主調(diào)用這個(gè)特定函數(shù)?! ∮谑俏矣窒?,既然可以在主程序中用遠(yuǎn)程線程迫使宿主調(diào)用dll中的特定函數(shù),為什么不直接在dll中用相同的方法去調(diào)用呢? 我還沒有來得及驗(yàn)證,馬上又想到:dll跟宿主是一個(gè)進(jìn)程,即使用遠(yuǎn)程線程,傳給CreateRomote
6、Thread函數(shù)的第一個(gè)參數(shù)也是-1(GetCurrentProcess()返回-1),為什么不直接用近程線程CreateThread呢? 需要著重強(qiáng)調(diào)地是:我當(dāng)時(shí)用CreateThread的目的只是讓它調(diào)用一個(gè)特定函數(shù),而不是要去創(chuàng)建一個(gè)線程,雖然最終的確創(chuàng)建了一個(gè)線程,不過對(duì)于此目的,貌似只是一個(gè)副作用?! ∥曳浅Ed奮,馬上進(jìn)行了驗(yàn)證,成功了! 現(xiàn)在我來總結(jié)一下托管代碼注入的過程: 首先定義一個(gè)線程回調(diào)函數(shù),可以在其中調(diào)用托管代碼:Dain里調(diào)用CreateThread函數(shù):BOOLAPIENTRYDllMain(HMODULEhModule,DWORDul_r
7、eason_for_call,LPVOIDlpReserved){switch(ul_reason_for_call){caseDLL_PROCESS_ATTACH:CreateThread(0,0,ThreadProc,0,0,0);break;caseDLL_THREAD_ATTACH:break;caseDLL_THREAD_DETACH:break;caseDLL_PROCESS_DETACH:break;}return12下一頁友情提醒:,特別!TRUE;} 這樣就實(shí)現(xiàn)了托管代碼的注入?! ∩钊朐掝}: 通常我們會(huì)