追根溯源DLL木馬進(jìn)程內(nèi)幕大揭密

字號(hào):

如果是位經(jīng)常玩木馬的朋友,那么一般情況下都會(huì)或多或少掌握一些木馬的特性,然而,很多朋友還是不知道“DLL木馬”是什么東東。那到底什么是“DLL木馬”呢?它與一般的木馬又有什么不同?帶著這些疑問(wèn),一起開(kāi)始這次揭密之旅吧!
    一、追根溯源從DLL說(shuō)起
    要了解什么是“DLL木馬”,就必須知道“DLL”是什么意思!說(shuō)起DLL,就不能不涉及到久遠(yuǎn)的DOS時(shí)代。在DOS大行其道的時(shí)代,寫(xiě)程序是一件繁瑣的事情,因?yàn)槊總€(gè)程序的代碼都是需要獨(dú)立的,這時(shí)為了實(shí)現(xiàn)一個(gè)普通的功能,甚至都要為此編寫(xiě)很多代碼。后來(lái)隨著編程技術(shù)發(fā)展與進(jìn)步,程序員們開(kāi)始把很多常用的代碼集合(也就是通用代碼)放進(jìn)一個(gè)獨(dú)立的文件里,并把這個(gè)文件稱(chēng)為“庫(kù)”(Library)。在寫(xiě)程序的時(shí)候,把這個(gè)庫(kù)文件加入編譯器,就能使用這個(gè)庫(kù)包含的所有功能而不必自己再去寫(xiě)一大堆代碼,這個(gè)技術(shù)被稱(chēng)為“靜態(tài)鏈接”(Static Link)。靜態(tài)鏈接技術(shù)讓勞累的程序員松了口氣,一切似乎都很美好。然而靜態(tài)鏈接技術(shù)的缺陷就是極度消耗和浪費(fèi)資源,當(dāng)一個(gè)程序只想用到一個(gè)庫(kù)文件包含的某個(gè)圖形效果時(shí),系統(tǒng)將把這個(gè)庫(kù)文件攜帶的所有的圖形效果都加入程序,這樣就使得程序非常臃腫。雖然這并不重要,可是這些臃腫的程序卻把道路都阻塞了——靜態(tài)鏈接技術(shù)讓最終的程序成了大塊頭,因?yàn)榫幾g器把整個(gè)庫(kù)文件都加載進(jìn)去了。
    技術(shù)永遠(yuǎn)是在發(fā)展的,靜態(tài)鏈接技術(shù)由于無(wú)法避免的弊端,不能滿(mǎn)足程序員和編程的需要,人們開(kāi)始尋找一種更好的方法來(lái)解決代碼重復(fù)的難題。隨著Windows系統(tǒng)的出現(xiàn), Windows系統(tǒng)使用一種被稱(chēng)為“動(dòng)態(tài)鏈接庫(kù)”(Dynamic Link Library)的新技術(shù),它同樣也是使用庫(kù)文件,DLL的名字就是這樣來(lái)的。動(dòng)態(tài)鏈接本身和靜態(tài)鏈接沒(méi)什么區(qū)別,也是把通用代碼寫(xiě)進(jìn)一些獨(dú)立文件里,但是在編譯方面,微軟把庫(kù)文件做成已經(jīng)編譯好的程序文件,給它們開(kāi)發(fā)一個(gè)交換數(shù)據(jù)的接口。程序員編寫(xiě)程序的時(shí)候,一旦要使用某個(gè)庫(kù)文件的一個(gè)功能函數(shù),系統(tǒng)就把這個(gè)庫(kù)文件調(diào)入內(nèi)存,連接上這個(gè)程序占有的任務(wù)進(jìn)程,然后執(zhí)行程序要用的功能函數(shù),并把結(jié)果返回給程序顯示出來(lái)。完成需要的功能后,這個(gè)DLL停止運(yùn)行,整個(gè)調(diào)用過(guò)程結(jié)束。微軟讓這些庫(kù)文件能被多個(gè)程序調(diào)用,實(shí)現(xiàn)了比較完美的共享,程序員無(wú)論要寫(xiě)什么程序,只要在代碼里加入對(duì)相關(guān)DLL的調(diào)用聲明就能使用它的全部功能。這樣,寫(xiě)出來(lái)的程序就不能再攜帶一大堆無(wú)用的垃圾了。
    DLL技術(shù)的誕生,使編寫(xiě)程序變成一件簡(jiǎn)單的事情,Windows為我們提供了幾千個(gè)函數(shù)接口,足以滿(mǎn)足大多數(shù)程序員的需要。而且,Windows系統(tǒng)自身就是由幾千個(gè)DLL文件組成,這些DLL相互扶持,組成了龐大的Windows系統(tǒng)。如果Windows依然使用靜態(tài)鏈接技術(shù),那將是不可想象的。
    二、什么是API
    在前面提到的“接口”又是什么呢?因?yàn)镈LL不能像靜態(tài)庫(kù)文件那樣塞進(jìn)程序里,如何讓程序知道實(shí)現(xiàn)功能的代碼和文件成了問(wèn)題,微軟就為DLL技術(shù)做了標(biāo)準(zhǔn)規(guī)范,為每個(gè)DLL文件都明確地標(biāo)注好它的功能名稱(chēng),程序只要根據(jù)標(biāo)準(zhǔn)規(guī)范找到相關(guān)的名稱(chēng)進(jìn)行調(diào)用就行了,這就是API(Application Programming Interface)應(yīng)用程序接口,每個(gè)DLL帶的接口都不盡相同,限度地減少了程序代碼的重復(fù)。在Windows里,最基本的3個(gè)DLL文件是kernel32.dll、user32.dll、gdi32.dll。它們共同構(gòu)成了基本的系統(tǒng)框架。
    三、DLL與木馬
    DLL是編譯好的代碼,與一般程序沒(méi)什么大差別,只是它不能獨(dú)立運(yùn)行,需要程序調(diào)用。那么,DLL與木馬能扯上什么關(guān)系呢?如果你學(xué)過(guò)編程并且寫(xiě)過(guò)DLL,就會(huì)發(fā)現(xiàn),其實(shí)DLL的代碼和其他程序幾乎沒(méi)什么兩樣,僅僅是接口和啟動(dòng)模式不同,只要改動(dòng)一下代碼入口,DLL就變成一個(gè)獨(dú)立的程序了。
    當(dāng)然,DLL文件是沒(méi)有程序邏輯的,其實(shí)DLL并不等于EXE。不過(guò),依然可以把DLL看做缺少了main入口的程序,DLL帶的各個(gè)功能函數(shù)可以看作一個(gè)程序的幾個(gè)函數(shù)模塊。DLL木馬就是把一個(gè)實(shí)現(xiàn)了木馬功能的代碼,加上一些特殊代碼寫(xiě)成DLL文件,導(dǎo)出相關(guān)的API,在別人看來(lái),這只是一個(gè)普通的DLL,但是這個(gè)DLL卻攜帶了完整的木馬功能,這就是DLL木馬的概念。也許有人會(huì)問(wèn),既然同樣的代碼就可以實(shí)現(xiàn)木馬功能,那么直接做程序就可以,為什么還要多此一舉寫(xiě)成DLL呢?這是為了隱藏,因?yàn)镈LL運(yùn)行時(shí)是直接掛在調(diào)用它的程序的進(jìn)程里的,并不會(huì)另外產(chǎn)生進(jìn)程,所以相對(duì)于傳統(tǒng)EXE木馬來(lái)說(shuō),它很難被查到。
    四、DLL的運(yùn)行
    雖然DLL不能自己運(yùn)行,可是Windows在加載DLL的時(shí)候,需要一個(gè)入口函數(shù),就如同EXE的main一樣,否則系統(tǒng)無(wú)法引用DLL。所以根據(jù)編寫(xiě)規(guī)范,Windows必須查找并執(zhí)行DLL里的一個(gè)函數(shù)DllMain作為加載DLL的依據(jù),這個(gè)函數(shù)不作為API導(dǎo)出,而是內(nèi)部函數(shù)。DllMain函數(shù)使DLL得以保留在內(nèi)存里,有的DLL里面沒(méi)有DllMain函數(shù),可是依然能使用,這是因?yàn)閃indows在找不到DllMain的時(shí)候,會(huì)從其它運(yùn)行庫(kù)中找一個(gè)不做任何操作的缺省DllMain函數(shù)啟動(dòng)這個(gè)DLL使它能被載入,并不是說(shuō)DLL可以放棄DllMain
    五、DLL木馬技術(shù)分析
    到了這里,大家也許會(huì)想,既然DLL木馬有那么多好處,以后寫(xiě)木馬都采用DLL方式不就好了嗎?話雖然是這么說(shuō)沒(méi)錯(cuò),但是編寫(xiě)DLL木馬并不是一些人想象的那么容易寫(xiě)的。要寫(xiě)一個(gè)能用的DLL木馬,需要了解更多關(guān)于操作系統(tǒng)底層的知識(shí)。
    1.木馬的主體
    千萬(wàn)別把木馬模塊寫(xiě)得真的像個(gè)API庫(kù)一樣,這不是開(kāi)發(fā)WINAPI。DLL木馬可以導(dǎo)出幾個(gè)輔助函數(shù),但是必須有一個(gè)過(guò)程負(fù)責(zé)主要執(zhí)行代碼,否則這個(gè)DLL只能是一堆零碎API函數(shù),別提工作了。如果涉及一些通用代碼,可以在DLL里寫(xiě)一些內(nèi)部函數(shù),供自己的代碼使用,而不是把所有代碼都開(kāi)放成接口,這樣它自己本身都難調(diào)用了,更不可能發(fā)揮作用。
    DLL木馬的標(biāo)準(zhǔn)執(zhí)行入口為DllMain,所以必須在DllMain里寫(xiě)好DLL木馬運(yùn)行的代碼,或者指向DLL木馬的執(zhí)行模塊。
    2.動(dòng)態(tài)嵌入技術(shù)
    Windows中,每個(gè)進(jìn)程都有自己的私有內(nèi)存空間,別的進(jìn)程是不允許對(duì)這個(gè)私人領(lǐng)地進(jìn)行操作的,但是,實(shí)際上我們?nèi)匀豢梢岳梅N種方法進(jìn)入并操作進(jìn)程的私有內(nèi)存,這就是動(dòng)態(tài)嵌入,它是將自己的代碼嵌入正在運(yùn)行的進(jìn)程中的技術(shù)。動(dòng)態(tài)嵌入有很多種,最常見(jiàn)的是鉤子、API以及遠(yuǎn)程線程技術(shù),現(xiàn)在的大多數(shù)DLL木馬都采用遠(yuǎn)程線程技術(shù)把自己掛在一個(gè)正常系統(tǒng)進(jìn)程中。其實(shí)動(dòng)態(tài)嵌入并不少見(jiàn),羅技的MouseWare驅(qū)動(dòng)就掛著每一個(gè)系統(tǒng)進(jìn)程。遠(yuǎn)程線程技術(shù)就是通過(guò)在另一個(gè)進(jìn)程中創(chuàng)建遠(yuǎn)程線程(RemoteThread)的方法進(jìn)入那個(gè)進(jìn)程的內(nèi)存地址空間。在DLL木馬的范疇里,這個(gè)技術(shù)也叫做“注入”,當(dāng)載體在那個(gè)被注入的進(jìn)程里創(chuàng)建了遠(yuǎn)程線程并命令它加載DLL時(shí),木馬就掛上去執(zhí)行了,沒(méi)有新進(jìn)程產(chǎn)生,要想讓木馬停止惟有讓掛接這個(gè)木馬DLL的進(jìn)程退出運(yùn)行。但是,很多時(shí)候我們只能束手無(wú)策——它和Explorer.exe掛在一起了。
    3.木馬的啟動(dòng)
    也許您會(huì)有這樣的想法,直接把這個(gè)DLL加入系統(tǒng)啟動(dòng)項(xiàng)目不就可以了?“NO”!前面已經(jīng)介紹過(guò),DLL不能獨(dú)立運(yùn)行,所以無(wú)法在啟動(dòng)項(xiàng)目里直接啟動(dòng)它。要想讓“馬兒”順利地跑起來(lái),就需要一個(gè)EXE使用動(dòng)態(tài)嵌入技術(shù)讓DLL掛上其他正常進(jìn)程,讓被嵌入的進(jìn)程調(diào)用這個(gè)DLL的DllMain函數(shù),激活木馬運(yùn)行,最后啟動(dòng)木馬的EXE結(jié)束運(yùn)行,木馬啟動(dòng)完畢。啟動(dòng)DLL木馬的EXE非常重要,它被稱(chēng)為加載(Loader)。所以,一個(gè)相對(duì)比較成熟的DLL木馬會(huì)想辦法保護(hù)它的Loader不會(huì)那么容易被發(fā)現(xiàn)和毀滅。
    Loader可以是多種多樣的,Windows的rundll32.exe也被一些DLL木馬用來(lái)做了Loader,這種木馬一般不帶動(dòng)態(tài)嵌入技術(shù),它直接掛著rundll32進(jìn)程運(yùn)行,用rundll32的方法像調(diào)用API一樣去引用這個(gè)DLL的啟動(dòng)函數(shù)激發(fā)木馬模塊開(kāi)始執(zhí)行,即使你殺了rundll32,木馬本體還是在的,一個(gè)最常見(jiàn)的例子就是3721中文實(shí)名,雖然它不是木馬。
    注冊(cè)表的AppInit_DLLs鍵也被一些木馬用來(lái)啟動(dòng)自己,如求職信病毒。利用注冊(cè)表啟動(dòng),就是讓系統(tǒng)執(zhí)行DllMain來(lái)達(dá)到啟動(dòng)木馬的目的。因?yàn)樗莐ernel調(diào)入的,對(duì)這個(gè)DLL的穩(wěn)定性有很大要求,稍有錯(cuò)誤就會(huì)導(dǎo)致系統(tǒng)崩潰,所以很少看到這種木馬。有一些更復(fù)雜點(diǎn)的DLL木馬通過(guò)svchost.exe啟動(dòng),這種DLL木馬必須寫(xiě)成NT-Service,入口函數(shù)是ServiceMain,一般很少見(jiàn),但是這種木馬的隱蔽性也不錯(cuò),而且Loader有保障。
    4.寥寥無(wú)幾
    到這里大家也應(yīng)該對(duì)DLL木馬有個(gè)了解了,是不是很想寫(xiě)一個(gè)?別急,不知道大家想過(guò)沒(méi)有,既然DLL木馬這么好,為什么到現(xiàn)在能找到的DLL木馬寥寥無(wú)幾?現(xiàn)在讓我來(lái)潑冷水,最重要的原因只有一個(gè):由于DLL木馬掛著系統(tǒng)進(jìn)程運(yùn)行,如果它本身寫(xiě)得不好,例如沒(méi)有防止運(yùn)行錯(cuò)誤的代碼或者沒(méi)有嚴(yán)格規(guī)范用戶(hù)的輸入,DLL就會(huì)很容易出錯(cuò)并崩潰。但是DLL崩潰會(huì)導(dǎo)致它掛著的程序跟著遭殃,別忘記它掛接的可是系統(tǒng)進(jìn)程啊,結(jié)局就是……慘不忍睹。所以寫(xiě)一個(gè)能公布的DLL木馬,在排錯(cuò)檢查方面做的工作要比一般的EXE木馬多,甚至寫(xiě)得多了連編寫(xiě)者自己都會(huì)煩躁不已!
    六、DLL木馬的發(fā)現(xiàn)和查殺
    經(jīng)??纯磫?dòng)項(xiàng)有沒(méi)有多出莫名其妙的項(xiàng)目,這是Loader的所在。而DLL木馬本體比較難發(fā)現(xiàn),需要用戶(hù)有一定編程知識(shí)和分析能力,在Loader里查找DLL名稱(chēng),或者從進(jìn)程里看有沒(méi)有掛接什么陌生的DLL!但是,對(duì)于一些計(jì)算機(jī)的初級(jí)用戶(hù)來(lái)說(shuō),這樣的發(fā)現(xiàn)過(guò)程是非常困難的!因此,最簡(jiǎn)單的方法:殺毒軟件和防火墻,這雖然不是萬(wàn)能的解決之道,但是對(duì)于不了解系統(tǒng)原理,沒(méi)有編程經(jīng)驗(yàn)的新手來(lái)說(shuō),總算是一種權(quán)宜之計(jì)吧!