这时可以同过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;//取出数据段
}
注:在WIN2000下使用SOCK_RAW需要管理员的权限。
|