揭开木马的神秘面纱 三

来源:互联网 发布:stm32f10x数据手册 编辑:程序博客网 时间:2024/04/29 17:21

  在揭开木马的神秘面纱(二)发表后,有很多朋友来信询问新型木马的详细情况,本文会详细的分析Win2000下一种新型木马的内部构造和防御方法。(本文默认的操作系统为Win2000,开发环境为VC++6.0。)

  大家知道,一般的"古典"型木马都是通过建立TCP连接来进行命令和数据的传递的,但是这种方法有一个致命的漏洞,就是木马在等待和运行的过程中,始终有一个和外界联系的端口打开着,这是木马的阿喀琉斯之踵(参看希腊神话《特洛伊战纪》),也是高手们查找木马的杀手锏之一(Netstat大法)。所谓道高一尺,魔高一丈,木马也是在斗争中不断进步不断成长的,其中一种ICMP木马就彻底摆脱了端口的束缚,成为黑客入侵后门工具中的佼佼者。

  什么是ICMP呢?ICMP全称是Internet Control Message Protocol(互联网控制报文协议)它是IP协议的附属协议,用来传递差错报文以及其他需要注意的消息报文,这个协议常常为TCP或UDP协议服务,但是也可以单独使用,例如著名的工具Ping(向Mike
Muuss致敬),就是通过发送接收ICMP_ECHO和ICMP_ECHOREPLY报文来进行网络诊断的。

  实际上,ICMP木马的出现正是得到了Ping程序的启发,由于ICMP报文是由系统内核或进程直接处理而不是通过端口,这就给木马一个摆脱端口的绝好机会,木马将自己伪装成一个Ping的进程,系统就会将ICMP_ECHOREPLY(Ping的回包)的监听、处理权交给木马进程,一旦事先约定好的ICMP_ECHOREPLY包出现(可以判断包大小、ICMP_SEQ等特征),木马就会接受、分析并从报文中解码出命令和数据。

  ICMP_ECHOREPLY包还有对于防火墙和网关的穿透能力。对于防火墙来说,ICMP报文是被列为危险的一类:从Ping of Death到ICMP风暴到ICMP碎片攻击,构造ICMP报文一向是攻击主机的最好方法之一,因此一般的防火墙都会对ICMP报文进行过滤;但是ICMP_ECHOREPLY报文却往往不会在过滤策略中出现,这是因为一旦不允许ICMP_ECHOREPLY报文通过就意味着主机没有办法对外进行Ping的操作,这样对于用户是极其不友好的。如果设置正确,ICMP_ECHOREPLY报文也能穿过网关,进入局域网。

  为了实现发送/监听ICMP报文,必须建立SOCK_RAW(原始套接口),首先,我们需要定义一个IP首部:

typedef struct iphdr {

 unsigned int version:4; // IP版本号,4表示IPV4

 unsigned int h_len:4; // 4位首部长度

 unsigned char tos; // 8位服务类型TOS

 unsigned short total_len; // 16位总长度(字节)

 unsigned short ident; //16位标识

 unsigned short frag_and_flags; // 3位标志位

 unsigned char ttl; //8位生存时间 TTL

 unsigned char proto; // 8位协议 (TCP, UDP 或其他)

 unsigned short checksum; // 16位IP首部校验和

 unsigned int sourceIP; //32位源IP地址

 unsigned int destIP; //32位目的IP地址

}IpHeader;
  然后定义一个ICMP首部:

typedef struct _ihdr {

 BYTE i_type; //8位类型

 BYTE i_code; //8位代码

 USHORT i_cksum; //16位校验和

 USHORT i_id; //识别号(一般用进程号作为识别号)

 USHORT i_seq; //报文序列号

 ULONG timestamp; //时间戳

}IcmpHeader;


  这时可以同过WSASocket建立一个原始套接口:

SockRaw=WSASocket(

          AF_INET, //协议族

          SOCK_RAW, //协议类型,SOCK_RAW表示是原始套接口

          IPPROTO_ICMP, //协议,IPPROTO_ICMP表示ICMP数据报

          NULL, //WSAPROTOCOL_INFO置空

          0, //保留字,永远置为0

          WSA_FLAG_OVERLAPPED //标志位

          );

  注:为了使用发送接收超时设置(设置SO_RCVTIMEO, SO_SNDTIMEO),必须将标志位置为WSA_FLAG_OVERLAPPED

  随后你可以使用fill_icmp_data子程序填充ICMP报文段:

fill_icmp_data函数:

void fill_icmp_data(char * icmp_data, int datasize)

{

 IcmpHeader *icmp_hdr;

 char *datapart;

 icmp_hdr = (IcmpHeader*)icmp_data;

 icmp_hdr->i_type = ICMP_ECHOREPLY; //类型为ICMP_ECHOREPLY

 icmp_hdr->i_code = 0;

 icmp_hdr->i_id = (USHORT)GetCurrentProcessId(); //识别号为进程号

 icmp_hdr->i_cksum = 0; //校验和初始化

 icmp_hdr->i_seq = 0; //序列号初始化

 datapart = icmp_data + sizeof(IcmpHeader); //数据端的地址为icmp报文

                        地址加上ICMP的首部长度

 memset(datapart,"A", datasize - sizeof(IcmpHeader)); //这里我填充的数据

                全部为"A",你可以填充任何代码和数据,实际上

                木马和控制端之间就是通过数据段传递数据的。

}

  再使用CheckSum子程序计算ICMP校验和:

 调用方法:

((IcmpHeader*)icmp_data)->i_cksum
= checksum((USHORT*)icmp_data, datasize);

CheckSum函数:

USHORT CheckSum (USHORT *buffer, int size)

{

 unsigned long cksum=0;

 while(size >1)

  {

    cksum+=*buffer++;

    size -=sizeof(USHORT);

  }

  if(size ) cksum += *(UCHAR*)buffer;

  cksum = (cksum >> 16) + (cksum & 0xffff);

  cksum += (cksum >>16);

  return (USHORT)(~cksum);

}// CheckSum函数是标准的校验和函数,你也可以用优化过的任何校验和函数来代替它

  随后,就可以通过sendto函数发送ICMP_ECHOREPLY报文:

  sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest));
  作为服务端的监听程序,基本的操作相同,只是需要使用recvfrm函数接收ICMP_ECHOREPLY报文并用decoder函数将接收来的报文解码为数据和命令:



recv_icmp=recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct
sockaddr*)&from,&fromlen);

decode_resp(recvbuf,recv_icmp,&from);

decoder函数:

void decoder(char *buf, int bytes,struct sockaddr_in *from)

{

 IpHeader *iphdr;

 IcmpHeader *icmphdr;

 unsigned short iphdrlen;

 iphdr = (IpHeader *)buf; //IP首部的地址就等于buf的地址

 iphdrlen = iphdr->h_len * 4 ; // 因为h_len是32位word,要转换成bytes必须*4

 icmphdr = (IcmpHeader*)(buf + iphdrlen); //ICMP首部的地址等于IP首部长度加buf

 printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr));
//取出源地址

 printf(" icmp_id=%d. ",icmphdr->i_id); //取出进程号

 printf(" icmp_seq=%d. ",icmphdr->i_seq); //取出序列号

 printf(" icmp_type=%d",icmphdr->i_type); //取出类型

 printf(" icmp_code=%d",icmphdr->i_code); //取出代码

 for(i=0;i < ICMP_DATA_SIZE;I++) printf(?%c?,*(buf+iphdrlen+i+12));//取出数据段

}

  注:在WIN2000下使用SOCK_RAW需要管理员的权限。

  对于ICMP木马,除非你使用嗅探器或者监视windows的SockAPI调用,否则从网络上是很难发现木马的行踪的(关于进程的隐藏及破解会在下一篇文章中进行讨论),那么,有什么可以补救的方法呢?有的,就是过滤ICMP报文,对于win2000可以使用系统自带的路由功能对ICMP协议进行过滤,win2000的Routing
& Remote Access功能十分强大,其中之一就是建立一个TCP/IP协议过滤器:打开Routing & Remote Access,选中机器名,在IP路由->General->网卡属性中有两个过滤器-输入过滤和输出过滤,只要在这里将你想过滤的协议制定为策略,ICMP木马就英雄无用武之地了;不过值得注意的是,一旦在输入过滤器中禁止了ICMP_ECHOREPLY报文,你就别想再用Ping这个工具了;如果过滤了所有的ICMP报文,你就收不到任何错误报文,当你使用IE访问一个并不存在的网站时,往往要花数倍的时间才能知道结果(嘿嘿,网络不可达、主机不可达、端口不可达报文你一个都收不到),而且基于ICMP协议的tracert工具也会失效,这也是方便和安全之间的矛盾统一了吧。

  本文的撰写是为了深入地研究Win2000的入侵和防御技术,探讨TCP/IP协议和Windows编程技巧,请不要将文中的内容用于任何违法的目的,文中所附为试验性的ICMP通讯程序,仅仅提供通过ICMP_ECHOREPLY进行通讯交换数据的功能以供研究;如果你对本文中的内容或代码有疑问,请Mail
to:Shotgun@xici.net,但是出于网络安全的考虑,本人不会提供任何木马软件及代码。

  随后你可以使用fill_icmp_data子程序填充ICMP报文段:

fill_icmp_data函数:

void fill_icmp_data(char * icmp_data, int datasize)

{

 IcmpHeader *icmp_hdr;

 char *datapart;

 icmp_hdr = (IcmpHeader*)icmp_data;

 icmp_hdr->i_type = ICMP_ECHOREPLY; //类型为ICMP_ECHOREPLY

 icmp_hdr->i_code = 0;

 icmp_hdr->i_id = (USHORT)GetCurrentProcessId(); //识别号为进程号

 icmp_hdr->i_cksum = 0; //校验和初始化

 icmp_hdr->i_seq = 0; //序列号初始化

 datapart = icmp_data + sizeof(IcmpHeader); //数据端的地址为icmp报文

                        地址加上ICMP的首部长度

 memset(datapart,"A", datasize - sizeof(IcmpHeader)); //这里我填充的数据

                全部为"A",你可以填充任何代码和数据,实际上

                木马和控制端之间就是通过数据段传递数据的。

}

  再使用CheckSum子程序计算ICMP校验和:

 调用方法:

((IcmpHeader*)icmp_data)->i_cksum
= checksum((USHORT*)icmp_data, datasize);

CheckSum函数:

USHORT CheckSum (USHORT *buffer, int size)

{

 unsigned long cksum=0;

 while(size >1)

  {

    cksum+=*buffer++;

    size -=sizeof(USHORT);

  }

  if(size ) cksum += *(UCHAR*)buffer;

  cksum = (cksum >> 16) + (cksum & 0xffff);

  cksum += (cksum >>16);

  return (USHORT)(~cksum);

}// CheckSum函数是标准的校验和函数,你也可以用优化过的任何校验和函数来代替它

  随后,就可以通过sendto函数发送ICMP_ECHOREPLY报文:

  sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest));
  作为服务端的监听程序,基本的操作相同,只是需要使用recvfrm函数接收ICMP_ECHOREPLY报文并用decoder函数将接收来的报文解码为数据和命令:



recv_icmp=recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct
sockaddr*)&from,&fromlen);

decode_resp(recvbuf,recv_icmp,&from);

decoder函数:

void decoder(char *buf, int bytes,struct sockaddr_in *from)

{

 IpHeader *iphdr;

 IcmpHeader *icmphdr;

 unsigned short iphdrlen;

 iphdr = (IpHeader *)buf; //IP首部的地址就等于buf的地址

 iphdrlen = iphdr->h_len * 4 ; // 因为h_len是32位word,要转换成bytes必须*4

 icmphdr = (IcmpHeader*)(buf + iphdrlen); //ICMP首部的地址等于IP首部长度加buf

 printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr));
//取出源地址

 printf(" icmp_id=%d. ",icmphdr->i_id); //取出进程号

 printf(" icmp_seq=%d. ",icmphdr->i_seq); //取出序列号

 printf(" icmp_type=%d",icmphdr->i_type); //取出类型

 printf(" icmp_code=%d",icmphdr->i_code); //取出代码

 for(i=0;i < ICMP_DATA_SIZE;I++) printf(?%c?,*(buf+iphdrlen+i+12));//取出数据段

}

  注:在WIN2000下使用SOCK_RAW需要管理员的权限。

  对于ICMP木马,除非你使用嗅探器或者监视windows的SockAPI调用,否则从网络上是很难发现木马的行踪的(关于进程的隐藏及破解会在下一篇文章中进行讨论),那么,有什么可以补救的方法呢?有的,就是过滤ICMP报文,对于win2000可以使用系统自带的路由功能对ICMP协议进行过滤,win2000的Routing
& Remote Access功能十分强大,其中之一就是建立一个TCP/IP协议过滤器:打开Routing & Remote Access,选中机器名,在IP路由->General->网卡属性中有两个过滤器-输入过滤和输出过滤,只要在这里将你想过滤的协议制定为策略,ICMP木马就英雄无用武之地了;不过值得注意的是,一旦在输入过滤器中禁止了ICMP_ECHOREPLY报文,你就别想再用Ping这个工具了;如果过滤了所有的ICMP报文,你就收不到任何错误报文,当你使用IE访问一个并不存在的网站时,往往要花数倍的时间才能知道结果(嘿嘿,网络不可达、主机不可达、端口不可达报文你一个都收不到),而且基于ICMP协议的tracert工具也会失效,这也是方便和安全之间的矛盾统一了吧。

  本文的撰写是为了深入地研究Win2000的入侵和防御技术,探讨TCP/IP协议和Windows编程技巧,请不要将文中的内容用于任何违法的目的,文中所附为试验性的ICMP通讯程序,仅仅提供通过ICMP_ECHOREPLY进行通讯交换数据的功能以供研究;如果你对本文中的内容或代码有疑问,请Mail
to:Shotgun@xici.net,但是出于网络安全的考虑,本人不会提供任何木马软件及代码。

原创粉丝点击