天天看點

“DLL技術”木馬程序内幕大揭密

http://evaa.hrbeu.edu.cn/dispbbs.asp?boardID=18&ID=348&page=2

http://evaa.hrbeu.edu.cn/dispbbs.asp?boardID=18&ID=349&page=2

 很多朋友還是不知道“DLL木馬”是什麼東東。那到底什麼是“DLL木馬”呢?它與一般的木馬又有什麼不同?帶着這些疑問,一起開始這次揭密之旅吧!

  一、追根溯源從DLL說起

要了解什麼是“DLL木馬”,就必須知道“DLL”是什麼意思!說起DLL,就不能不涉及到久遠的DOS時代。在DOS大行其道的時 代,寫程式是一件繁瑣的事情,因為每個程式的代碼都是需要獨立的,這時為了實作一個普通的功能,甚至都要為此編寫很多代碼。後來随着程式設計技術發展與進步, 程式員們開始把很多常用的代碼集合(也就是通用代碼)放進一個獨立的檔案裡,并把這個檔案稱為“庫”(Library)。在寫程式的時候,把這個庫檔案加 入編譯器,就能使用這個庫包含的所有功能而不必自己再去寫一大堆代碼,這個技術被稱為“靜态連結”(Static Link)。靜态連結技術讓勞累的程式員松了口氣,一切似乎都很美好。然而靜态連結技術的最大缺陷就是極度消耗和浪費資源,當一個程式隻想用到一個庫檔案 包含的某個圖形效果時,系統将把這個庫檔案攜帶的所有的圖形效果都加入程式,這樣就使得程式非常臃腫。雖然這并不重要,可是這些臃腫的程式卻把道路都阻塞 了——靜态連結技術讓最終的程式成了大塊頭,因為編譯器把整個庫檔案都加載進去了。

技術永遠是在發展的,靜态連結技術由于無法避免的弊 端,不能滿足程式員和程式設計的需要,人們開始尋找一種更好的方法來解決代碼重複的難題。随着Windows系統的出現, Windows系統使用一種被稱為“動态連結庫”(Dynamic Link Library)的新技術,它同樣也是使用庫檔案,DLL的名字就是這樣來的。動态連結本身和靜态連結沒什麼差別,也是把通用代碼寫進一些獨立檔案裡,但 是在編譯方面,微軟把庫檔案做成已經編譯好的程式檔案,給它們開發一個交換資料的接口。程式員編寫程式的時候,一旦要使用某個庫檔案的一個功能函數,系統 就把這個庫檔案調入記憶體,連接配接上這個程式占有的任務程序,然後執行程式要用的功能函數,并把結果傳回給程式顯示出來。完成需要的功能後,這個DLL停止運 行,整個調用過程結束。微軟讓這些庫檔案能被多個程式調用,實作了比較完美的共享,程式員無論要寫什麼程式,隻要在代碼裡加入對相關DLL的調用聲明就能 使用它的全部功能。這樣,寫出來的程式就不能再攜帶一大堆無用的垃圾了。

DLL技術的誕生,使編寫程式變成一件簡單的事 情,Windows為我們提供了幾千個函數接口,足以滿足大多數程式員的需要。而且,Windows系統自身就是由幾千個DLL檔案組成,這些DLL互相 扶持,組成了龐大的Windows系統。如果Windows依然使用靜态連結技術,那将是不可想象的。

二、什麼是API

在前面提到的“接口”又是什麼呢?因為DLL不能像靜态庫檔案那樣塞程序式裡,如何讓程式知道實作功能的代碼和檔案成了問題,微軟就為 DLL技術做了标準規範,為每個DLL檔案都明确地标注好它的功能名稱,程式隻要根據标準規範找到相關的名稱進行調用就行了,這就是 API(Application Programming Interface)應用程式接口,每個DLL帶的接口都不盡相同,最大限度地減少了程式代碼的重複。在Windows裡,最基本的3個DLL檔案是 kernel32.dll、user32.dll、gdi32.dll。它們共同構成了基本的系統架構。

三、DLL與木馬

DLL是編譯好的代碼,與一般程式沒什麼大差别,隻是它不能獨立運作,需要程式調用。那麼,DLL與木馬能扯上什麼關系呢?如果你學過 程式設計并且寫過DLL,就會發現,其實DLL的代碼和其他程式幾乎沒什麼兩樣,僅僅是接口和啟動模式不同,隻要改動一下代碼入口,DLL就變成一個獨立的程 序了。

當然,DLL檔案是沒有程式邏輯的,其實DLL并不等于EXE。不過,依然可以把DLL看做缺少了main入口的程式,DLL帶 的各個功能函數可以看作一個程式的幾個函數子產品。DLL木馬就是把一個實作了木馬功能的代碼,加上一些特殊代碼寫成DLL檔案,導出相關的API,在别人 看來,這隻是一個普通的DLL,但是這個DLL卻攜帶了完整的木馬功能,這就是DLL木馬的概念。也許有人會問,既然同樣的代碼就可以實作木馬功能,那麼 直接做程式就可以,為什麼還要多此一舉寫成DLL呢?這是為了隐藏,因為DLL運作時是直接挂在調用它的程式的程序裡的,并不會另外産生程序,是以相對于 傳統EXE木馬來說,它很難被查到。

四、DLL的運作

雖然DLL不能自己運作,可是Windows在加載DLL的時候,需要一個入口函數,就如同EXE的main一樣,否則系統無法引用 DLL。是以根據編寫規範,Windows必須查找并執行DLL裡的一個函數DllMain作為加載DLL的依據,這個函數不作為API導出,而是内部函 數。DllMain函數使DLL得以保留在記憶體裡,有的DLL裡面沒有DllMain函數,可是依然能使用,這是因為Windows在找不到 DllMain的時候,會從其它運作庫中找一個不做任何操作的預設DllMain函數啟動這個DLL使它能被載入,并不是說DLL可以放棄DllMain 函數。

五、DLL木馬技術分析

編寫DLL木馬并不是一些人想象的那麼容易寫的。要寫一個能用的DLL木馬,需要了解更多關于作業系統底層的知識。

1.木馬的主體

千 萬别把木馬子產品寫得真的像個API庫一樣,這不是開發WINAPI。DLL木馬可以導出幾個輔助函數,但是必須有一個過程負責主要執行代碼,否則這個 DLL隻能是一堆零碎API函數,别提工作了。如果涉及一些通用代碼,可以在DLL裡寫一些内部函數,供自己的代碼使用,而不是把所有代碼都開放成接口, 這樣它自己本身都難調用了,更不可能發揮作用。

DLL木馬的标準執行入口為DllMain,是以必須在DllMain裡寫好DLL木馬運作的代碼,或者指向DLL木馬的執行子產品。

2.動态嵌入技術

Windows 中,每個程序都有自己的私有記憶體空間,别的程序是不允許對這個私人領地進行操作的,但是,實際上我們仍然可以利用種種方法進入并操作程序的私有記憶體,這就 是動态嵌入,它是将自己的代碼嵌入正在運作的程序中的技術。動态嵌入有很多種,最常見的是鈎子、API以及遠端線程技術,現在的大多數DLL木馬都采用遠 程線程技術把自己挂在一個正常系統程序中。其實動态嵌入并不少見,羅技的MouseWare驅動就挂着每一個系統程序。遠端線程技術就是通過在另一個程序 中建立遠端線程(RemoteThread)的方法進入那個程序的記憶體位址空間。在DLL木馬的範疇裡,這個技術也叫做“注入”,當載體在那個被注入的進 程裡建立了遠端線程并指令它加載DLL時,木馬就挂上去執行了,沒有新程序産生,要想讓木馬停止惟有讓挂接這個木馬DLL的程序退出運作。但是,很多時候 我們隻能束手無策——它和Explorer.exe挂在一起了。

3.木馬的啟動

DLL不能獨立運作,是以無法在啟動 項目裡直接啟動它。要想讓“馬兒”順利地跑起來,就需要一個EXE使用動态嵌入技術讓DLL挂上其他正常程序,讓被嵌入的程序調用這個DLL的 DllMain函數,激活木馬運作,最後啟動木馬的EXE結束運作,木馬啟動完畢。啟動DLL木馬的EXE非常重要,它被稱為加載(Loader)。所 以,一個相對比較成熟的DLL木馬會想辦法保護它的Loader不會那麼容易被發現和毀滅。

Loader可以是多種多樣 的,Windows的rundll32.exe也被一些DLL木馬用來做了Loader,這種木馬一般不帶動态嵌入技術,它直接挂着rundll32程序 運作,用rundll32的方法像調用API一樣去引用這個DLL的啟動函數激發木馬子產品開始執行,即使你殺了rundll32,木馬本體還是在的,一個 最常見的例子就是3721中文實名,雖然它不是木馬。

系統資料庫的AppInit_DLLs鍵也被一些木馬用來啟動自己,如求職 信 病毒。利用系統資料庫啟動,就是讓系統執行DllMain來達到啟動木馬的目的。因為它是kernel調入的,對這個DLL的穩定性有很大要求,稍有錯誤就會 導緻系統崩潰,是以很少看到這種木馬。有一些更複雜點的DLL木馬通過svchost.exe啟動,這種DLL木馬必須寫成NT-Service,入口函 數是ServiceMain,一般很少見,但是這種木馬的隐蔽 性也不錯,而且Loader有保障。

4.寥寥無幾

由 于DLL木馬挂着系統程序運作,如果它本身寫得不好,例如沒有防止運作錯誤的代碼或者沒有嚴格規範使用者的輸入,DLL就會很容易出錯并崩潰。但是DLL崩 潰會導緻它挂着的程式跟着遭殃,别忘記它挂接的可是系統程序啊,結局就是……慘不忍睹。是以寫一個能公布的DLL木馬,在排錯檢查方面做的工作要比一般的 EXE木馬多,甚至寫得多了連編寫者自己都會煩躁不已!

六、DLL木馬的發現和清除

經常看看啟動項有沒有多出莫名其妙的項目,這是Loader的所在。而DLL木馬本體比較難發現,,在Loader裡查找DLL名稱, 或者從程序裡看有沒有挂接什麼陌生的DLL!但是,對于一些計算機的初級使用者來說,這樣的發現過程是非常困難的!是以,最簡單的方法:防毒軟體和防火牆!

繼續閱讀