国产一级一区二区_segui88久久综合9999_97久久夜色精品国产_欧美色网一区二区

掃一掃
關注微信公眾號

托管注入深入研究
2008-12-10   

這是我發表在《黑客防線》2008年12期上的一篇文章,這里是網絡上的首發。

附件是我寫的一個工具“超級間諜(SuperSpy)”,主要包括一個窗口探測功能,甚至能夠探測到VC自帶的SPY++所不能探測到的窗口;當然,還包括這里提到的“托管注入”功能。

程序需運行在.Net2.0以上,如果需要使用注入功能,則需要.Net3.5以上。

超級間諜下載地址:http://download.csdn.net/source/850509

網上關于dll注入的文章實在太多,但基本上都是針對Win32 dll的,而很少涉及到托管dll。

首先讓我們來看看Win32 dll是如何注入的,通常有兩種方法:鉤子和遠程線程。而遠程線程更靈活,所以本文主要討論遠程線程的方法,為了便于交流,先明確以下概念:

1. 主程序:用于將dll注入到其它進程的exe

2. dll:被注入其它進程的dll

3. 宿主:dll將被注入的其它程序

前兩個程序都是我們自己寫的,而第3個是原來就有的。

遠程注入的基本步驟為:主程序通過CreateRemoteThread函數迫使宿主調用LoadLibrary函數加載dll,從而執行dll的入口函數DllMain,只要我們把代碼放到DllMain里,就可以被調用了。

現在我們來看托管dll的注入:

用C#或VB.NET寫的dll沒有DllMain函數,我們自然想到了功能強大的C++。通常,我們把用C#或VB.NET寫的dll,或者用C++寫的,但編譯為/clr:pure的dll稱為托管dll

而把用C++編寫的,但編譯為/clr的dll稱為混合dll。

混合dll也可以調用托管代碼,所以也可以將其稱為托管dll,本文所說的托管dll注入,實際上是混合dll的注入。

我們首先想到的是用常規方法來注入混合dll,結果會發現:只要在DllMain函數里調用了托管代碼,程序就會崩潰。

也許你還會想到下面的方法:

定義一個類,在其構造函數里調用托管代碼,然后在全局域里定義這個類的一個變量,當我們這樣做了后會發現,注入后,什么也沒有執行。

查閱MSDN,我們找到了答案:

DllMain不能直接或間接地調用托管代碼,并且全局變量不會進行初始化。

這樣的結果讓人非常沮喪!

難道真的就沒有辦法了嗎?

網友CiCi給出了一個解決方案:

寫一個混合dll,在其中定義一個導出函數,在這個導出函數里可以調用托管代碼。

然后寫一個非托管dll(也就是Win32 dll),在其DllMain函數里調用前面那個混合dll的導出函數。

這個方案能夠解決問題,并且我在一段時間里,也一直使用這個方法。但總覺得不完美:必須使用兩個dll。

有沒有辦法用一個dll就解決問題呢?答案是肯定的。

我在一次偶然的機會中,發現了一個現象:

如里用鉤子來實現注入,則全局變量會得到初始化。這個發現讓我非常困惑:為什么遠程線程注入就不會初始化呢?

我考慮這兩種方法的區別:鉤子注入時,宿主會調用dll中的鉤子回調函數。

于是我大膽設想,只要宿主調用了dll中的任意一個函數,全局變量就會得到初始化。

于是我在dll中定義了一個空實現的函數(因為我的目地是迫使全局變量初始化,而不是去執行這個函數)

結果正如我所料,全局變量被初始化了,其構造函數中的托管代碼被調用了!

這里其實已經實現了托管代碼的注入,當然,還遠遠不夠完善。

這時候,我又覺得全局變量是多余的了:既然我能夠使宿主調用dll中的一個特定函數,為什么不直接把托管代碼放到這個函數中呢?

我馬上進行了測試,也成功了。

之前我是這樣實現讓宿主調用dll中的一個特定函數的:

在dll被注入之后,我在DllMain函數里將這個特定函數的地址寫到共享內存中(這時DllMain里沒有調用托管代碼,所以可以執行),然后主程序讀取共享內存中的值,再通過遠程線程迫使宿主調用這個特定函數。

于是我又想,既然可以在主程序中用遠程線程迫使宿主調用dll中的特定函數,為什么不直接在dll中用相同的方法去調用呢?

我還沒有來得及驗證,馬上又想到:dll跟宿主是一個進程,即使用遠程線程,傳給CreateRomoteThread函數的第一個參數也是-1(GetCurrentProcess()返回-1),為什么不直接用“近程”線程CreateThread呢?

需要著重強調地是:我當時用CreateThread的目的只是讓它調用一個特定函數,而不是要去創建一個線程,雖然最終的確創建了一個線程,不過對于此目的,貌似只是一個副作用。

我非常興奮,馬上進行了驗證,成功了!

現在我來總結一下托管代碼注入的過程:

首先定義一個線程回調函數,可以在其中調用托管代碼:

DWORD CALLBACK ThreadProc(LPVOID lp)

{

//可以在此調用托管代碼

return 0;

}

然后在dll的入口函數DllMain里調用CreateThread函數:

BOOL APIENTRY DllMain( HMODULE hModule,

DWORD ul_reason_for_call,

LPVOID lpReserved

)

{

switch (ul_reason_for_call)

{

case DLL_PROCESS_ATTACH:

CreateThread(0,0,ThreadProc,0,0,0);

break;

case DLL_THREAD_ATTACH:

break;

case DLL_THREAD_DETACH:

break;

case DLL_PROCESS_DETACH:

break;

}

return TRUE;

}

這樣就實現了托管代碼的注入。

深入話題:

通常我們會有這樣的要求:在主程序中單擊某個按扭,使宿主執行dll中的一段代碼,多次單擊就多次執行。

上面的代碼無法實現這個目地,因為只有dll剛被注入到宿主時,DllMain中的DLL_PROCESS_ATTACH才會收到通知,我們自然想到:每次執行后,卸載掉dll,下次單擊后,DLL_PROCESS_ATTACH又會收到通知;遺憾的是,目前我還沒有辦法卸載掉這個dll。

對于非托管dll,我們可以通過FreeLibrary來卸載(如果是在dll內部卸載,還必須借助FreeLibraryAndExitThread函數,關于dll的自卸載,由于沒有在這個程序中使用,這里就不做介紹了)

而對于托管dll,查閱MSDN,我們得到的結論是:.Net不支持dll的卸掉,dll只能隨著應用程序域的卸載而卸載。

對于混合dll,應該如何卸載呢?

我嘗試用Win32 dll的方式卸載,結果也“真的”卸載了,這是因為:

1. DllMain中的DLL_PROCESS_DETACH收到了通知

2. 用模塊查看工具去看,宿主中確實不存在注入的dll了

但事實上是失敗了,至少有兩個問題:

1. 關閉宿主時,會提示出錯

2. 再次注入時,雖然DLL_PROCESS_ATTACH會收到通知,但用CreateThread創建的新線程并不被執行

既然MSDN上都說無法卸載托管dll,那這個問題就擱淺了吧。

雖然不能卸載這個dll,但并不是說就不能實現前面提到的問題。

實際上,每次主程序通過CreateRemoteThread函數在宿主創建線程時,DllMain中的DLL_THREAD_ATTACH和DLL_THREAD_DETACH也會收到通知,當然最好不要在這里直接調用CreateThread,這是因為:

1. 如果不做處理,直接在DLL_THREAD_ATTACH中調中CreateThread,顯然會造成死循環:每產生一個線程,DLL_THREAD_ATTACH就會收到通知,然后又去創建線程,它又會得到通知……

2. 雖然多次單擊按扭注入時,DLL_THREAD_ATTACH都會得到通知;但反過來,得到了通知,不意味著就是遠程注入:一個最明顯的情況是,對CreateThread的調用就會導致DLL_THREAD_ATTACH收到通知,但這并非遠程注入

我的解決辦法是:每次主程序調用CreateRemoteThread迫使宿主調用LoadLibrary加載dll的時候,都會使模塊計數器加1,根據模塊計數器的值的變化,就可以確定是否是遠程注入了。

示例程序說明:

示例程序包括3個文件:

1. SuperSpy.exe 用C#寫的主程序

2. Invoke.dll 用C++寫的dll,也只能用C++來寫

3. PropertyControl.dll 用VB.NET寫的插件,它不是必需的

除了Invoke.dll必須用C++外,其余兩個可以用任意語言寫。

你也許會感到奇怪,為什么有兩個dll?

其中PropertyControl.dll是一個插件,你完全可以把所有的代碼都放到Invoke.dll中,之所以用插件的形式,只是為了方便大家編寫自已的插件,插件的規范是,在任意一個類中實現以下成員(你可以用你習慣的語言來編寫這個插件):

public static void Inject();

public static string Description

{

get;

}

兩個成員都必須是公共、靜態的,其中Inject方法是注入后要調用的方法;Description屬性是用于描述插件作用的。

我自已實現的插件的功能是:查詢和編輯一個托管程序中所有窗口(包括控件)的屬性,這是一個很強大的功能,我舉兩個例子:

1. 星號密碼查看。由于已經注入,本來顯示為星號的密碼,可以直接通過Text屬性獲得

2. 灰色按扭突破。只需將窗口的Enabled屬性設置成true即可

以往要實現這兩個功能,都需要分別編寫相關的程序,而現在僅僅是這個插件的一個簡單應用。而且,你可以編寫自己的插件,來實現自己的要求。


熱詞搜索:

上一篇:對某高級技工學校網站的安全檢測和加固(1)
下一篇:利用基于Web的黑客技術造攻擊uTorrent客戶端(1)

分享到: 收藏
主站蜘蛛池模板: 鸡泽县| 庆元县| 临安市| 石台县| 兴化市| 云南省| 法库县| 哈巴河县| 德清县| 绥德县| 阳东县| 芮城县| 项城市| 若尔盖县| 银川市| 大足县| 忻城县| 鹤庆县| 云梦县| 舟曲县| 德昌县| 涟水县| 锡林浩特市| 化州市| 上饶市| 富顺县| 左贡县| 德昌县| 招远市| 泸溪县| 黔东| 乐业县| 页游| 丽水市| 通辽市| 古交市| 肇庆市| 望谟县| 桦南县| 皋兰县| 铁岭市|