木馬揭秘
相信經常玩
木馬的朋友們都會知道一些木馬的特性,也會有自己最喜愛的木馬,不過,很多朋友依然不知道近年興起的“DLL木馬”為何物。什麼是“DLL
木馬”呢?它與一般的木馬有什麼不同?
技術說起
要了解DLL
木馬,就必須知道這個“DLL”是什麼意思,所以,讓我們追溯到幾年前,DOS系統大行其道的日子裡。在那時候,寫程式是一件繁瑣的事情,因為每個程式的
代碼都是獨立的,有時候為了實現一個功能,就要為此寫很多代碼,後來隨著
編程技術發展,
程式設計師們把很多常用的代碼集合(通用代碼)放進一個
獨立的
檔案里,並把這個檔案稱為“
庫”(Library),在
寫程式的時候,把這個
庫檔案加入
編譯器,就能使用這個
庫包含的所有功能,而不必自己再去寫一大堆
代碼,這個技術被稱為“
靜態連結”(Static Link)。
靜態連結技術讓勞累的
程式設計師鬆了口氣,一切似乎都很美好。可是事實證明,美好的事物不會存在太久,因為
靜態連結就像一個粗魯的推銷員,不管你想不想要宣傳單,他都全部塞到你的手上來。寫一個
程式只想用到一個庫檔案包含的某個圖形效果,就因為這個,你不得不把這個庫檔案攜帶的所有的圖形效果都加入程式,留著它們當花瓶擺設,這倒沒什麼重要,可是這些花瓶卻把道路都阻塞了——
靜態連結技術讓最終的程式成了大塊頭,因為
編譯器把整個庫檔案也算進去了。
靜態連結技術
時代在發展,
靜態連結技術由於天生的弊端,不能滿足
程式設計師的願望,人們開始尋找一種更好的方法來解決
代碼重複的難題。後來,Windows系統出現了,時代的分水嶺終於出現。Windows系統使用一種新的連結技術,這種被稱為“
動態連結”(Dynamic Link)的新技術同樣也是使用庫檔案,微軟稱它們為“
動態程式庫”——Dynamic Link Library,DLL的名字就是這樣來的。
動態連結本身和
靜態連結沒什麼區別,也是把通用
代碼寫進一些獨立檔案里,但是在編譯方面,微軟繞了個圈子,並沒有採取把庫檔案加進程式的方法,而是把庫檔案做成已經編譯好的
程式檔案,給它們開個交換
數據的接口,
程式設計師寫程式的時候,一旦要使用某個庫檔案的一個功能
函式,系統就把這個庫檔案調入記憶體,連線上這個程式占有的任務進程,然後執行程式要用的功能函式,並把結果返回給程式顯示出來,在我們看來,就像是程式自己帶有的功能一樣。完成需要的功能後,這個DLL停止運行,整個調用過程結束。微軟讓這些庫檔案能被多個程式調用,實現了比較完美的共享,
程式設計師無論要寫什麼程式,只要在
代碼里加入對相關DLL的調用聲明就能使用它的全部功能。最重要的是,DLL絕對不會讓你多拿一個花瓶,你要什麼它就給你什麼,你不要的東西它才不會給你。這樣,寫出來的程式就不能再攜帶一大堆垃圾了——絕對不會讓你把吃剩的東西帶回家,否則罰款,這是自助餐。
DLL技術的誕生,使編寫程式變成一件簡單的事情,
Windows為我們提供了幾千個
函式接口,足以滿足大多數
程式設計師的需要。而且,Windows系統自身就是由幾千個DLL檔案組成,這些DLL相互扶持,組成了強大的
Windows系統。如果Windows使用
靜態連結技術,它的體積會有多大?我不敢想。
應用程式接口
上面我們對DLL技術做了個大概分析,在裡面我提到了“
接口”,這又是什麼呢?因為DLL不能像
靜態庫檔案那樣塞進程式里,所以,如何讓程式知道實現功能的
代碼和檔案成了問題,
微軟就為DLL技術做了標準規範,讓一個DLL檔案像乳酪一樣開了許多小洞,每個洞口都註明裡面存放的功能的名字,程式只要根據標準規範找到相關洞口就可以取得它要的美味了,這個洞口就是“
應用程式接口”(Application Programming Interface),每個DLL帶的接口都不相同,盡最大可能的減少了代碼的重複。用Steven的一句話:API就是一個工具箱,你根據需要取出螺絲刀、扳手,用完後再把它們放回原處。在Windows里,最基本的3個DLL檔案是
kernel32.dll、
user32.dll、
gdi32.dll。它們共同
構成了
基本的系統框架。
運行特點
DLL是編譯好的
代碼,與一般程式沒什麼大差別,只是它不能獨立運行,需要程式調用。那么,DLL與
木馬能扯上什麼關係呢?如果你學過編程並且寫過DLL,就會發現,其實DLL的
代碼和其他程式幾乎沒什麼兩樣,僅僅是接口和啟動模式不同,只要改動一下代碼入口,DLL就變成一個獨立的程式了。當然,DLL檔案是沒有程式邏輯的,這裡並不是說DLL=EXE,不過,依然可以把DLL看做缺少了main入口的EXE,DLL帶的各個功能
函式可以看作一個程式的幾個函式模組。DLL
木馬就是把一個實現了木馬功能的
代碼,加上一些特殊代碼寫成DLL檔案,導出相關的API,在別人看來,這只是一個普通的DLL,但是這個DLL卻攜帶了完整的
木馬功能,這就是DLL木馬的概念。也許有人會問,既然同樣的
代碼就可以實現
木馬功能,那么直接做程式就可以,為什麼還要多此一舉寫成DLL呢?這是為了隱藏,因為DLL運行時是直接掛在調用它的程式的進程里的,並不會另外產生進程,所以相對於傳統EXE木馬來說,它很難被查到。
雖然DLL不能自己運行,可是Windows在載入DLL的時候,需要一個入口
函式,就如同EXE的main一樣,否則系統無法引用DLL。所以根據編寫規範,Windows必須查找並執行DLL里的一個
函式DllMain作為載入DLL的依據,這個函式不作為API導出,而是內部函式。DllMain
函式使DLL得以保留在記憶體里,有的DLL裡面沒有DllMain函式,可是依然能使用,這是因為Windows在找不到DllMain的時候,會從其它
運行庫中找一個不做任何操作的
預設DllMain函式啟動這個DLL使它能被載入,並不是說DLL可以放棄DllMain
函式。
技術分析
到了這裡,您也許會想,既然DLL
木馬有那么多好處,以後寫木馬都採用DLL方式不就好了嗎?話雖然是這么說沒錯,但是DLL木馬並不是一些人想像的那么容易寫的。要寫一個能用的DLL
木馬,你需要了解更多知識。
木馬的主體
千萬別把
木馬模組寫得真的像個API庫一樣,這不是開發WINAPI。DLL
木馬可以導出幾個輔助
函式,但是必須有一個過程負責主要執行
代碼,否則這個DLL只能是一堆零碎API函式,別提工作了。
如果涉及一些通用
代碼,可以在DLL里寫一些內部
函式,供自己的代碼使用,而不是把所有代碼都開放成接口,這樣它自己本身都難調用了,更不可能發揮作用。
DLL
木馬的標準執行入口為DllMain,所以必須在DllMain里寫好DLL木馬運行的
代碼,或者指向DLL木馬的執行模組。
動態嵌入技術
Windows中,每個進程都有自己的私有記憶體空間,別的進程是不允許對這個私人領地進行操作的,但是,實際上我們仍然可以利用種種方法進入並操作進程的私有記憶體,這就是動態嵌入,它是將自己的
代碼嵌入正在運行的進程中的技術。動態嵌入有很多種,最常見的是鉤子、API以及
遠程執行緒技術,現在的大多數DLL
木馬都採用遠程執行緒技術把自己掛在一個正常
系統進程中。其實動態嵌入並不少見,羅技的MouseWare驅動就掛著每一個系統進程。
遠程執行緒技術就是通過在另一個進程中創建遠程執行緒(RemoteThread)的方法進入那個進程的記憶體地址空間。在DLL
木馬的範疇里,這個技術也叫做“注入”,當載體在那個被注入的進程里創建了遠程執行緒並命令它載入DLL時,木馬就掛上去執行了,沒有新進程產生,要想讓木馬停止惟有讓掛接這個木馬DLL的進程退出運行。但是,很多時候我們只能束手無策——它和Explorer.exe掛在一起了,你確定要關閉Windows嗎?
木馬的啟動
有人也許會迫不及待的說,直接把這個DLL加入系統啟動項目不就可以了。答案是NO,前面說過,DLL不能獨立運行,所以無法在啟動項目里直接啟動它。要想讓
木馬跑起來,就需要一個EXE使用
動態嵌入技術讓DLL搭上其他正常進程的車,讓被嵌入的進程調用這個DLL的DllMain
函式,激發木馬運行,最後啟動木馬的EXE結束運行,木馬啟動完畢。
啟動DLL
木馬的EXE是個重要角色,它被稱為Loader,如果沒有Loader,DLL木馬就是破爛一堆,因此,一個算得上成熟的DLL木馬會想辦法保護它的Loader不會那么容易被毀滅。記得狼狽為奸的故事嗎?DLL
木馬就是爬在狼Loader上的狽。
Loader可以是多種多樣的,Windows的rundll32.exe也被一些DLL
木馬用來做了Loader,這種木馬一般不帶
動態嵌入技術,它直接掛著rundll32進程運行,用rundll32的方法(rundll32.exe [DLL名],[
函式] [
參數])像調用API一樣去引用這個DLL的啟動函式激發木馬模組開始執行,即使你殺了rundll32,木馬本體還是在的,一個最常見的例子就是3721中文實名,雖然它不是木馬。
註冊表的AppInit_DLLs鍵也被一些
木馬用來啟動自己,如
求職信病毒。利用
註冊表啟動,就是讓系統執行DllMain來達到啟動
木馬的目的。因為它是kernel調入的,對這個DLL的穩定性有很大要求,稍有錯誤就會導致
系統崩潰,所以很少看到這種
木馬。
有一些更複雜點的DLL
木馬通過svchost.exe啟動,這種DLL木馬必須寫成NT-Service,入口
函式是ServiceMain,一般很少見,但是這種木馬的隱蔽性也不錯,而且Loader有保障。
其它
到這裡大家也應該對DLL
木馬有個了解了,是不是很想寫一個?別急,不知道大家想過沒有,既然DLL木馬這么好,為什麼到現在能找到的DLL木馬寥寥無幾?現在讓我來潑冷水,最重要的原因只有一個:由於DLL木馬掛著
系統進程運行,如果它本身寫得不好,例如沒有防止運行錯誤的
代碼或者沒有嚴格規範用戶的輸入,DLL就會出錯崩潰。別緊張,一般的EXE也是這樣完蛋的,但是DLL崩潰會導致它掛著的程式跟著遭殃,別忘記它掛接的是
系統進程喔,結局就是……慘不忍睹。所以寫一個能公布的DLL
木馬,在排錯檢查方面做的工作要比一般的EXE木馬多,寫得多了自己都煩躁……
發現和查殺
經常看看啟動項有沒有多出莫名其妙的項目,這是Loader的所在,只要殺了狼,狽就不能再狂了。而DLL
木馬本體比較難發現,需要你有一定編程知識和分析能力,在Loader里查找DLL名稱,或者從進程里看多掛接了什麼陌生的DLL,可是對新手來說……總之就是比較難啊比較難,所以,最簡單的方法:
防毒軟體和
防火牆(不是萬能藥,切忌長期服用)。