本節書摘來自異步社群出版社《c++ 黑客程式設計揭秘與防範》一書中的第1章,第1.3節,作者:冀雲,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。
c++ 黑客程式設計揭秘與防範
下面介紹一些在黑客程式設計中會用到的api函數,盡量排一點簡單易用的函數,用簡單的幾行代碼來完成一定的功能,希望大家能在這裡體會到程式設計樂趣,不至于被大段的代碼影響了自己前進的心情。
一般的病毒木馬都有這種類似的功能,完成這個功能其實并不複雜,我們來拆解思考一下實作這段代碼的步驟。
複制是一個拷貝的過程。既然是拷貝,就要知道拷貝的原位置和目的位置。也就是整個過程其實分3步,首先要得到自身程式所在的路徑,然後獲得windows目錄和系統目錄,最後分别拷貝自身程式到這兩個目錄中。這3個步驟要如何完成,下面我們來看看完成這些功能的api函數。
獲得自身程式所在路徑的api函數的定義:
該函數有3個參數,分别如下。
(1)hmodule:該參數在獲得自身程式時使用為null。
(2)lpfilename:該參數指定一個字元型的緩沖區,用于儲存程式自身所在的路徑。
(3)nsize:該參數指定緩沖區的大小。
獲得windows目錄的api函數的定義:
該函數有兩個參數,分别如下。
(1)lpbuffer:該參數指定一個字元型的緩沖區,用于儲存windows目錄的路徑。
(2)usize:該參數指定緩沖區的大小。
獲得系統目錄的api函數的定義:
(1)lpbuffer:該參數指定一個字元型的緩沖區,用于儲存系統目錄的路徑。
拷貝檔案的api函數的定義:
(1)lpexistingfilename:該參數指向一個已存在檔案的路徑,即原檔案路徑。
(2)lpnewfilename:該參數指向一個新的檔案的位置,即欲拷貝到的檔案的目的路徑。
(3)bfailifexists:該參數是一個布爾型參數,如果參數為true,若目的檔案已存在則傳回,複制失敗;如果參數為false,若目的檔案已存在則強行覆寫原有的檔案。
需要使用的api函數已經介紹完了,下面就來真正完成這個複制自身程式到windows目錄和系統目錄下的程式,代碼如下:
該函數需要包含windows.h這個頭檔案,也就是在該段程式的最開始處加一句:
了解一個系統相關資訊也是一項比較重要的内容,強大的掃描軟體nmap在對目标主機進行掃描時,也能對目标主機的系統等資訊進行識别,真的是很強大。這裡簡單地擷取一些與系統相關的資訊,主要擷取的内容有作業系統的版本、作業系統的名字及目前登入的使用者名稱。接下來逐個介紹這些api函數。
(1)擷取作業系統版本
代碼如下:
該函數就一個參數,這個參數是指向一個osversioninfo結構的指針。看一下osversioninfo這個結構體。
dwplatformid的取值有3個,而現在主要使用一個,即ver_platform_win32_nt。
(2)擷取計算機名稱
該函數有兩個參數,介紹如下。
① lpbuffer:儲存計算機名稱緩沖區。
② lpnsize:儲存緩沖區的長度,該參數是一個輸入/輸出參數。
(3)擷取目前使用者名稱
① lpbuffer:儲存目前使用者名稱的緩沖區。
② nsize:儲存緩沖區的長度,該參數是一個輸入/輸出參數。
我們封裝一個簡單的函數來擷取系統的這3個資訊,代碼如下:
将代碼進行編譯連接配接并運作,其執行結果如圖1-13所示。
這個程式完成了我們想要的功能,對于程式設計的部分就介紹到這裡。下面介紹debug和release方面的内容。
關于擷取系統資訊的程式,我們編寫完成了,也編譯連接配接并運作過了。找到剛才編譯的程式,檢視一下它的檔案大小,如圖1-14所示。
從圖1-14中可以看出,該程式竟然有153kb大小。是不是很驚人?我們一共寫了不過十幾行代碼,但是卻生成了如此大體積的程式,這是為什麼呢?因為代碼預設編譯連接配接是debug版本的,如圖1-15所示。
從圖1-15中可以看出,我們的代碼是由debug方式編譯的。debug被稱為調試版本,在這種方式的編譯下,可執行程式中會附帶很多和調試相關的資料或代碼,而且不做任何的優化,以此為開發人員提供大量的調試資訊,進而友善了程式的調試工作。除了debug方式編譯以外,還有一種方式是release方式編譯,單擊“win32 debug”右邊的下拉箭頭可以選擇“win32 release”,如圖1-16所示。
release方式被稱作釋出版本,是為最終使用者使用的,這種方式對代碼做了大量的優化工作,不再包含與調試相關的資訊,進而使程式的運作效率更高,體積更小,如圖1-17所示。
從圖1-17可以看出,兩個程式的檔案大小發生了截然不同的變化。是以,當我們自己寫程式調試時,應該使用調試版,以友善我們對程式進行調試。當我們的程式已經調試完畢,那麼可以使用釋出版來與大家進行交流。
很多時候,我們都需要檢視函數的定義,而函數的定義都在sdk的頭檔案中。雖然從msdn中也能找到函數的定義,但是還是有略微的不同,而且對于查找自定義函數的函數定義也是很友善的。
回到我們的代碼當中,随便選中一個api函數,比如getcomputername()這個函數。加入要檢視該函數的定義應該如何檢視呢?我們在getcomputername()這個函數上單擊滑鼠右鍵,在彈出的快捷菜單上選擇“go to definition of getcomputername”(到getcomputername函數的定義處)指令,如圖1-18所示。
當選擇“go to definition of getcomputername”指令以後,會來到“winbase.h”頭檔案中的getcomputername()函數的定義處,如圖1-19和圖1-20所示。
1-19
圖1-19 “winbase.h”頭檔案
從圖1-20中可以看出,getcomputername是一個宏,其對應的函數為getcomputernamea()。關于getcomputername()和getcomputernamea(),包括可以看到的getcomputernamew(),我們都不進行介紹。通過圖1-20的函數定義和前面介紹這個函數的定義來比較一下,可以看到,頭檔案中的定義比msdn中的定義對于函數的描述更加詳細,比如winapi表示函數的調用方式。
除了“go to definition of getcomputername”以外,還有一個“go to reference to getcomputername”,這個是檢視何處引用了函數。大家可以自行進行練習。
本文僅用于學習和交流目的,不代表異步社群觀點。非商業轉載請注明作譯者、出處,并保留本文的原始連結。