NameLess的大名都應該聽說過吧,估計還有相當多的人用過呢,個人認為這個后門非常經典,我們再來簡單看一下有關它的介紹:僅有一個DLL文件,平時不開端口,可以進行反向連接的后門程序。
這個后門早已經開源了,網上流傳最廣的是V1.14(穩定版),(我已經把這個版本的完整源代碼打包了)哈哈,這樣的好事可千萬不能錯過哦,立馬從網上Down回來研究了下,越讀越覺得越有味道,就把一些東西分享出來吧,希望對各位能有所幫助。
對于一個較完整的后門來說,最需要關心的地方莫過于幾點:啟動方式、連接方式、控制功能、自身保護。而NameLess就具備了一個完整后門的所有功能,我們就通過品讀它的代碼來啟發自己能做出一個屬于自己的后門吧。
首先將源代碼文件解壓,鼠標雙擊NameLess.dsw文件打開,我的測試環境是VC6.0,更高的版本我沒測試過(沒安裝),為了方便分析,我同時使用EditPlus將其打開了,便于快速查找各函數的定義跟蹤流程。
一、啟動方式
NameLess后門的安裝方法:打開CMD窗口,轉到后門放置的目錄,輸入Rundll32 NameLess.dll,Install ServiceName ActiveString Password。
可見它是通過系統提供的Rundll32程序來進行安裝的(畢竟它只有一個DLL文件),安裝函數代碼在輸出的Install函數中,我們在源工程中找到這個函數并跟蹤到InstallService(param)中,一目了然。
作者首先用自寫的DesStringArgument函數把命令行參數給分解出來,再用自寫的ReadRegEx函數檢查注冊表鍵HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\NameLess(我們下面用“注冊表路徑1來代替這個路徑”)是否存在,然后進入HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\"ServiceName"(注冊表路徑2)把start的值改為2,接著進入子項“Parameters”中把原服務的ServiceName讀取出來后保存到注冊表路徑1中,隨后把自身的一些信息比如密碼、替換的服務名也保存在這里以備卸載的時候恢復。后面緊接著就是卸載函數RemoveService,大概流程就是先判斷密碼是否正確,然后到“注冊表路徑1”中找到原服務的文件路徑進行恢復,然后刪除掉“注冊表路徑1”。(代碼我就不貼了,見附件源工程中的NameLess.cpp文件)
這種啟動方法只需替換掉系統中原有不太重要的服務,在當時來說效果應該是比較好的,不過隨著現在主動防御的大行其道,這種直接修改注冊表的方法已失去了效果,畢竟是兩年前的作品了。但主動防御也不是無懈可擊,它畢竟還是要被用戶控制的(技術是要為用戶服務的),所以我們可以綜合利用各種方法將自己完美地偽裝好后欺騙用戶的允許,順利地Pass,所以說“人”才是網絡安全中最薄弱的一環。
二、連接方式
現在我們的后門可以啟動了,但它是如何工作的呢?我們知道如果程序以服務方式啟動的話,在DLL中必須導出一個ServiceMain函數,所以我們就在NameLess.cpp文件中找到該函數開始我們的分析過程。
這里先注冊了一個服務控制函數ServiceHandler以便控制服務的啟動、暫停等行為,具體的實現在TellSCM函數中,這個函數是通過調用API函數SetServiceStatus實現的,沒什么新意。我們回到ServiceMain函數中繼續看,就剩下一個調用了:RealService,看樣子是從這里開始了真正的工作。
在RealService函數中經過一系列的讀取注冊表初始化后程序創建了一個保護線程,(該線程函數ShieldThread的實現代碼在源工程的./Command/Shield.h文件中,這個放到后面的“自我保護”功能中講解)然后初始化套接字InitSocket,緊接著StartSniffer,然后就調用了WSACleanup開始做清理工作了,所以我們就來專注分析StartSniffer函數(函數的實現代碼在源工程./Sniffer/Sniffer.h文件中)。
這里首先建立了一個IPPROTO_IP類型的原始套接字,緊接著調用函數GetInetIP獲取本機的IP地址,它這個GetInetIP函數中對各種情況都進行了比較完善的考慮,大家在做自己的程序時可以參考一下。我們繼續關注它的sniffer工作,在bind了套接字之后調用了WSAIoctl將第二個參數dwIoControlCode設置為SIO_RCVALL來捕獲流經本機的所有數據;每捕獲到一個數據包后就調用自寫函數DecodeIPPack(具體功能后面有分析)將其解析出來后創建一個StartBackDoor線程,因為給它傳遞的參數為NULL,所以此線程函數將執行BindShell函數(實現代碼在./Socket/Socket.h文件中)建立一個新的監聽套接字,將其屬性設置為可重用,每監聽到一個新的連接后就為其建立一個會話套接字并比較源IP,代碼如下:
if(stricmp(SourceIP,inet_ntoa(AccpetAddr.sin_addr))) { closesocket(AcceptSocket); continue; } |
這一段的作用比較容易讓人感到迷惑,AccpetAddr是接收到的連接另一方屬性,我們使用EditPlus的“在文件中查找”在整個目錄里面搜索SourceIP查看它到底是做什么的。最后把注意力放在了DecodeIPPack函數上(實現代碼在./Sniffer/Sniffer.h文件中):
BOOL DecodeIPPack(const char * IPBuffer) { IPHeader * pIpheader; int IPHeaderLen; struct sockaddr_in SourceAddr; pIpheader = (IPHeader*)IPBuffer; if ((pIpheader->proto != IPPROTO_TCP)) return FALSE; SourceAddr.sin_addr.s_addr = pIpheader->sourceIP; memcpy(SourceIP, inet_ntoa(SourceAddr.sin_addr), sizeof(SourceIP)); IPHeaderLen = sizeof(unsigned long) * (pIpheader->h_lenver & 0xf); return DecodeTCPPack(IPBuffer+IPHeaderLen); } |
在前面我們提到了這個函數,但并不知道它的具體作用,現在就來詳細分析一下,每當捕獲到一個數據包就傳遞給這個函數,并將其強制轉換成IPHeader類型(這個結構類型會經常用到,網上有很多,附帶的源代碼中是定義在Sniffer.h文件中)。函數首先檢查數據報的協議類型是否為IPPROTO_TCP,然后將sourceIP字段值賦給SourceAddr.sin_addr.s_addr,再通過memcpy函數拷貝到SourceIP變量中,到這里我們可以知道每一個協議為IPPROTO_TCP的數據包的源IP都會被賦給SourceIP,隨后將其傳遞給了DecodeTCPPack和CheckTcpData函數,在這兩個函數中先進行初始化處理后就調用CheckTcpData檢查數據,這個函數有一點點長,所以我就簡單介紹一下它的工作流程算了:首先在數據報中找到"\n",接著判斷它前面是否為"\r",如果是就把它前面的內容全部拷貝到一個字符串StringData中,再使用PortPoint = strstr(StringData,":");和HostPoint = strstr(StringData,"|");這兩句在里面尋找主機地址和端口,緊接著還會分析端口合法性和主機地址的有效性,這里就不多說了。從這里可以知道它是使用嗅探的原理來取得控制端的IP實現反向連接的,大概原理就是捕獲流經本機的所有數據包,然后根據自定義的協議來分析是否是控制端發送過來的數據,如果是就從中取得相關信息后連接。
好了,中間分析了這么多后我們回到stricmp(SourceIP,inet_ntoa(AccpetAddr.sin_addr))這里繼續看,通過上面的分析我們清楚了SourceIP是用來區分是否是控制端的IP的。如果符合規則的話就為其建立一個控制線程,在此線程函數ClientThread中使用自定義函數ReveiceMessage來接收命令,首先判斷輸入的密碼是否正確,通過后即發送預定義的歡迎信息,然后進入一個循環中不停地接受控制端的命令并執行。
到這里我們就基本上把NameLess的連接流程搞清楚了,這種使用嗅探的方法有它的好處,就是容易過防火墻,但也有它的缺點,就是當網絡繁忙的時候很容易丟失封包。一些其他的反向連接方式更加流行,就是通過一個固定的域名來作為中轉站,控制端每次啟動的時候都自動將自己的IP更新到一個指定的網頁文件中,而服務端就通過讀取這個文件來得到控制端的IP后主動進行連接。網上的資料很多,大家可以多找來一些代碼參考。
共2頁: 1 [2] 下一頁 | ||||
|