一、從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使用靜態鏈接技術,它的體積會有多大?我不敢想。
二、應用程序接口API
上面我們對DLL技術做了個大概分析,在里面我提到了“接口”,這又是什么呢?因為DLL不能像靜態庫文件那樣塞進程序里,所以,如何讓程序知道實現功能的代碼和文件成了問題,微軟就為DLL技術做了標準規范,讓一個DLL文件像奶酪一樣開了許多小洞,每個洞口都注明里面存放的功能的名字,程序只要根據標準規范找到相關洞口就可以取得它要的美味了,這個洞口就是“應用程序接口”(Application Programming Interface),每個DLL帶的接口都不相同,盡最大可能的減少了代碼的重復。用Steven的一句話:API就是一個工具箱,你根據需要取出螺絲刀、扳手,用完后再把它們放回原處。在Windows里,最基本的3個DLL文件是kernel32.dll、user32.dll、gdi32.dll。它們共同構成了基本的系統框架。