linux原始套接字(3)-构造IP_TCP发送与接收
一.概述
tcp报文封装在ip报文中,创建tcp的原始套接字如下:
sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
此时只能构造tcp报文,如果想进一步构造ip首部,那么就要开启sockfd的IP_HDRINCL选项:
; setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));
ip报文格式:
ip首部结构定义在netinet/ip.h
struct ip { #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned ; /* header length */ unsigned ; /* version */ #endif #if __BYTE_ORDER == __BIG_ENDIAN unsigned ; /* version */ unsigned ; /* header length */ #endif u_int8_t ip_tos; /* type of service */ u_short ip_len; /* total length */ u_short ip_id; /* identification */ u_short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_int8_t ip_ttl; /* time to live */ u_int8_t ip_p; /* protocol */ u_short ip_sum; /* checksum */ struct in_addr ip_src, ip_dst; /* source and dest address */ };
tcp报文格式:
tcp首部结构定义在netinet/tcp.h:
struct tcphdr { __extension__ union { struct { u_int16_t th_sport; /* source port */ u_int16_t th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ # if __BYTE_ORDER == __LITTLE_ENDIAN u_int8_t th_x2:; /* (unused) */ u_int8_t th_off:; /* data offset */ # endif # if __BYTE_ORDER == __BIG_ENDIAN u_int8_t th_off:; /* data offset */ u_int8_t th_x2:; /* (unused) */ # endif u_int8_t th_flags; # define TH_FIN 0x01 # define TH_SYN 0x02 # define TH_RST 0x04 # define TH_PUSH 0x08 # define TH_ACK 0x10 # define TH_URG 0x20 u_int16_t th_win; /* window */ u_int16_t th_sum; /* checksum */ u_int16_t th_urp; /* urgent pointer */ }; struct { u_int16_t source; u_int16_t dest; u_int32_t seq; u_int32_t ack_seq; # if __BYTE_ORDER == __LITTLE_ENDIAN u_int16_t res1:; u_int16_t doff:; u_int16_t fin:; u_int16_t syn:; u_int16_t rst:; u_int16_t psh:; u_int16_t ack:; u_int16_t urg:; u_int16_t res2:; # elif __BYTE_ORDER == __BIG_ENDIAN u_int16_t doff:; u_int16_t res1:; u_int16_t res2:; u_int16_t urg:; u_int16_t ack:; u_int16_t psh:; u_int16_t rst:; u_int16_t syn:; u_int16_t fin:; # else # error "Adjust your <bits/endian.h> defines" # endif u_int16_t window; u_int16_t check; u_int16_t urg_ptr; }; }; };
对照结构的定义和上面结构图很容易理解。注意:ip和tcp的四位首部长度都是指占多少个32bit。如果普通ip首部长度是20字节,4字节占32位,那么这个值就是20/4=5。
二.构造IP_TCP发送
/** * @file ip_tcp_send.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/tcp.h> /* ip首部长度 */ #define IP_HEADER_LEN sizeof(struct ip) /* tcp首部长度 */ #define TCP_HEADER_LEN sizeof(struct tcphdr) /* ip首部 + tcp首部长度 */ #define IP_TCP_HEADER_LEN IP_HEADER_LEN + TCP_HEADER_LEN void err_exit(const char *err_msg) { perror(err_msg); exit(); } /* 填充ip首部 */ struct ip *fill_ip_header(const char *src_ip, const char *dst_ip, int ip_packet_len) { struct ip *ip_header; ip_header = (struct ip *)malloc(IP_HEADER_LEN); ip_header->ip_v = IPVERSION; ip_header->ip_hl = ; /* 这里注意,ip首部长度是指占多个32位的数量,4字节=32位,所以除以4 */ ip_header->ip_tos = ; ip_header->ip_len = htons(ip_packet_len); /* 整个IP数据报长度,包括普通数据 */ ip_header->ip_id = ; /* 让内核自己填充标识位 */ ip_header->ip_off = ; ip_header->ip_ttl = MAXTTL; ip_header->ip_p = IPPROTO_TCP; /* ip包封装的协议类型 */ ip_header->ip_sum = ; /* 让内核自己计算校验和 */ ip_header->ip_src.s_addr = inet_addr(src_ip); /* 源IP地址 */ ip_header->ip_dst.s_addr = inet_addr(dst_ip); /* 目标IP地址 */ return ip_header; } /* 填充tcp首部 */ struct tcphdr *fill_tcp_header(int src_port, int dst_port) { struct tcphdr *tcp_header; tcp_header = (struct tcphdr *)malloc(TCP_HEADER_LEN); tcp_header->source = htons(src_port); tcp_header->dest = htons(dst_port); /* 同IP首部一样,这里是占32位的字节多少个 */ tcp_header->doff = ; /* 发起连接 */ tcp_header->syn = ; tcp_header->window = ; tcp_header->check = ; return tcp_header; } /* 发送ip_tcp报文 */ void ip_tcp_send(const char *src_ip, int src_port, const char *dst_ip, int dst_port, const char *data) { struct ip *ip_header; struct tcphdr *tcp_header; struct sockaddr_in dst_addr; socklen_t sock_addrlen = sizeof(struct sockaddr_in); int data_len = strlen(data); int ip_packet_len = IP_TCP_HEADER_LEN + data_len; char buf[ip_packet_len]; ; bzero(&dst_addr, sock_addrlen); dst_addr.sin_family = PF_INET; dst_addr.sin_addr.s_addr = inet_addr(dst_ip); dst_addr.sin_port = htons(dst_port); /* 创建tcp原始套接字 */ ) err_exit("socket()"); /* 开启IP_HDRINCL,自定义IP首部 */ ) err_exit("setsockopt()"); /* ip首部 */ ip_header = fill_ip_header(src_ip, dst_ip, ip_packet_len); /* tcp首部 */ tcp_header = fill_tcp_header(src_port, dst_port); bzero(buf, ip_packet_len); memcpy(buf, ip_header, IP_HEADER_LEN); memcpy(buf + IP_HEADER_LEN, tcp_header, TCP_HEADER_LEN); memcpy(buf + IP_TCP_HEADER_LEN, data, data_len); /* 发送报文 */ ret_len = sendto(sockfd, buf, ip_packet_len, , (struct sockaddr *)&dst_addr, sock_addrlen); ) printf("sendto() ok!!!\n"); else printf("sendto() failed\n"); close(sockfd); free(ip_header); free(tcp_header); } int main(int argc, const char *argv[]) { ) { printf(]); exit(); } /* 发送ip_tcp报文 */ ip_tcp_send(argv[], atoi(argv[]), argv[], atoi(argv[]), argv[]); ; }
流程:命令行接收的参数分别是:源ip地址,源端口,目标ip地址,目标端口,普通数据。然后通过源/目标ip构造ip首部,通过源/目标端口构造tcp首部。把目标ip/端口填充到sockaddr_in,最后把构造的ip首部,tcp首部,普通数据全部复制到缓冲区,一并发送到刚刚的sockaddr_in的地址!!!
三.接收IP_TCP
/** * @file ip_tcp_recv.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/tcp.h> /* ip首部长度 */ #define IP_HEADER_LEN sizeof(struct ip) /* tcp首部长度 */ #define TCP_HEADER_LEN sizeof(struct tcphdr) /* ip首部 + tcp首部长度 */ #define IP_TCP_HEADER_LEN IP_HEADER_LEN + TCP_HEADER_LEN /* 接收数据缓冲大小 */ #define BUFFER_SIZE 1024 /* ip首部 + tcp首部 + 数据缓冲区大小 */ #define IP_TCP_BUFF_SIZE IP_TCP_HEADER_LEN + BUFFER_SIZE void err_exit(const char *err_msg) { perror(err_msg); exit(); } /* 原始套接字接收 */ void raw_socket_recv() { struct ip *ip_header; struct tcphdr *tcp_header; int sock_raw_fd, ret_len; char buf[IP_TCP_BUFF_SIZE]; ) err_exit("socket()"); /* 接收数据 */ ) { bzero(buf, IP_TCP_BUFF_SIZE); ret_len = recv(sock_raw_fd, buf, IP_TCP_BUFF_SIZE, ); ) { /* 取出ip首部 */ ip_header = (struct ip *)buf; /* 取出tcp首部 */ tcp_header = (struct tcphdr *)(buf + IP_HEADER_LEN); printf("=======================================\n"); printf("from ip:%s\n", inet_ntoa(ip_header->ip_src)); printf("from port:%d\n", ntohs(tcp_header->source)); /* 取出数据 */ printf("get data:%s\n", buf + IP_TCP_HEADER_LEN); } } close(sock_raw_fd); } int main(void) { /* 原始套接字接收 */ raw_socket_recv(); ; }
流程:创建TCP类型的原始套接,原始套接字是点对点传输,不像TCP/UDP是端对端,故原始套接字不存在端口概念,可以直接接收。这里接收的是整个IP报文,里面包含了TCP报文。接收后依次取出ip首部,tcp首部,普通数据!!!
四.实验
发送端和接收端都在本机,我们打开wireshark监听lo接口。以root身份运行2个程序:
上面发送了2个tcp包,每次的源ip,源端口都是伪造的。DDOS程序就是这个原理。
wireshark结果:
linux原始套接字(3)-构造IP_TCP发送与接收的更多相关文章
- linux原始套接字(4)-构造IP_UDP
一.概述 同上一篇tcp一样,udp也是封装在ip报文里面.创建UDP的原始套接字如下: (soc ...
- Linux原始套接字实现分析---转
http://blog.chinaunix.net/uid-27074062-id-3388166.html 本文从IPV4协议栈原始套接字的分类入手,详细介绍了链路层和网络层原始套接字的特点及其内核 ...
- Linux原始套接字抓取底层报文
1.原始套接字使用场景 我们平常所用到的网络编程都是在应用层收发数据,每个程序只能收到发给自己的数据,即每个程序只能收到来自该程序绑定的端口的数据.收到的数据往往只包括应用层数据,原有的头部信息在传递 ...
- 关于linux 原始套接字编程
关于linux 网络编程最权威的书是<<unix网络编程>>,但是看这本书时有些内容你可能理解的不是很深刻,或者说只知其然而不知其所以然,那么如果你想搞懂的话那么我建议你可以看 ...
- linux原始套接字(2)-icmp请求与接收
一.概述 上一篇arp请求使用的是链路层的原始套接字.icmp封装在ip数据报里面,所以icmp请 ...
- linux原始套接字(1)-arp请求与接收
一.概述 以太网的arp数据包结构: arp结构op操作参数:1为请求,2为应答. 常用的数据结构如 ...
- Linux Socket 原始套接字编程
对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...
- UNP——原始套接字
1.原始套接字的用处 使用原始套接字可以构造或读取网际层及其以上报文. 具体来说,可以构造 ICMP, IGMP 协议报文,通过开启 IP_HDRINCL 套接字选项,进而自定义 IPv4首部. 2. ...
- Linux基础(11)原始套接字
一边接收函数返回一边判断返回值时一定要把接收的优先级加()提高再去判断 例 if((sockfd = socket()) < 0) 问题: 如何实现SYN扫描器扫描端口 , 比如AB两个设备要进 ...
随机推荐
- Hadoop学习笔记1-如何简单布署hadoop
企业机型配置: 选型标准:普通的,廉价的,标准的(容易替换的),工业化大规模生产的 CPU:支持多核CPU,如2个4核CPU 内存:16G以上,内存越大,常用数据都缓存在内存,提高速度 硬盘:不需RA ...
- SQLServer2005+分页SQL
declare @pagenum int = 1; declare @pagesize int = 3; WITH Tmp AS ( SELECT ROW_NUMBER() OVER (ORDER B ...
- Xslider演示8种滚动效果
Xslider演示8种滚动效果包括: 一.左右切换:每次移动固定距离 二.左右切换:最后一个显示在最右侧 三.自动切换 四.循环切换 五.文本的上下滚动 六.上下切换 七.上下自动循环切换 在线预览 ...
- 小谈React、React Native、React Web
React有三个东西,React JS 前端Web框架,React Native 移动终端Hybrid框架,React Web是一个源码转换工具(React Native 转 Web,并之所以特别提出 ...
- MSCRM 报表显示 rsprocessingaborted 错误
今天又有朋友遇到rsprocessingaborted这个问题,想想这个已经是很老很老的问题了,得在写一遍补充下. 一.首先会考虑是不是SrsDataConnector没有安装的原因,如果正常安装的话 ...
- curl命令学习笔记
下载文件并保存为默认文件名 curl -O http://www.xxx.com/xxx.exe 下载文件并保存为指定文件名 curl -o filename.exe http://www.xxx.c ...
- JSON解析实例——使用Json-lib
JSON解析实例——使用Json-lib Json-lib下载及使用 本文介绍用一个类库进行JSON解析. 工具下载地址:http://sourceforge.net/projects/json-li ...
- 微信小程序管理后台介绍
微信小程序的管理后台,每次进入都需要扫码,还是特别不爽,现在微信小程序还没正式发布,很多人都还没看到管理后台,这里抢先发布出来 ------------------------------------ ...
- git 上的pull request 是什么意思?
1.git 上有常见的pull request 功能 2.pull request 的含义 解释一: 有一个仓库,叫Repo A.你如果要往里贡献代码,首先要Fork这个Repo,于是在你的Gi ...
- tomcat <context path>的意义及作用
context path 是在tomcat 要支持多个应用时对每个应用的docBase做区别时的区分符. 打个比方假如你有两个请求:一个为 http:localhost:8080/test1/hell ...