一.概述                                                   

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发送与接收的更多相关文章

  1. linux原始套接字(4)-构造IP_UDP

    一.概述                                                    同上一篇tcp一样,udp也是封装在ip报文里面.创建UDP的原始套接字如下: (soc ...

  2. Linux原始套接字实现分析---转

    http://blog.chinaunix.net/uid-27074062-id-3388166.html 本文从IPV4协议栈原始套接字的分类入手,详细介绍了链路层和网络层原始套接字的特点及其内核 ...

  3. Linux原始套接字抓取底层报文

    1.原始套接字使用场景 我们平常所用到的网络编程都是在应用层收发数据,每个程序只能收到发给自己的数据,即每个程序只能收到来自该程序绑定的端口的数据.收到的数据往往只包括应用层数据,原有的头部信息在传递 ...

  4. 关于linux 原始套接字编程

    关于linux 网络编程最权威的书是<<unix网络编程>>,但是看这本书时有些内容你可能理解的不是很深刻,或者说只知其然而不知其所以然,那么如果你想搞懂的话那么我建议你可以看 ...

  5. linux原始套接字(2)-icmp请求与接收

    一.概述                                                    上一篇arp请求使用的是链路层的原始套接字.icmp封装在ip数据报里面,所以icmp请 ...

  6. linux原始套接字(1)-arp请求与接收

    一.概述                                                   以太网的arp数据包结构: arp结构op操作参数:1为请求,2为应答. 常用的数据结构如 ...

  7. Linux Socket 原始套接字编程

    对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...

  8. UNP——原始套接字

    1.原始套接字的用处 使用原始套接字可以构造或读取网际层及其以上报文. 具体来说,可以构造 ICMP, IGMP 协议报文,通过开启 IP_HDRINCL 套接字选项,进而自定义 IPv4首部. 2. ...

  9. Linux基础(11)原始套接字

    一边接收函数返回一边判断返回值时一定要把接收的优先级加()提高再去判断 例 if((sockfd = socket()) < 0) 问题: 如何实现SYN扫描器扫描端口 , 比如AB两个设备要进 ...

随机推荐

  1. JS代码实用代码实例(输入框监听,点击显示点击其他地方消失,文件本地预览上传)

    前段时间写前端,遇到一些模块非常有用,总结以备后用 一.input框字数监听 <!DOCTYPE html> <html lang="en"> <he ...

  2. phonegap安卓手机开发入门

    先安装安卓开发安环境 http://www.cnblogs.com/zhangsanshi/p/3582368.html 安装phonegap 在安装ant http://www.cnblogs.co ...

  3. [deviceone开发]-动画示例源码

    一.简介 do_FrameAnimtionView组件是用加载GIF动态图片和加载一系列图片形成动画效果的展示组件,这个示例直观的展示组件基本的使用方式.适合初学者. 二.效果图 三.相关讨论 htt ...

  4. 通过gulp为requireJs引入的模块添加版本号

    由于项目用到requireJs,并且通过gulp来对项目进行统一的管理,为了防止浏览器对文件进行缓存,所以通过gulp为项目中的文件添加版本号. 1.分别安装gulp-rev.gulp-rev-col ...

  5. SAP中发送邮件

    WITH HEADER LINE, docdata LIKE sodocchgi1, objtxt WITH HEADER LINE, objpack WITH HEADER LINE, reclis ...

  6. SharePoint 2013 和卷影复制服务(VSS)概述

    对备份供应商而言,卷影复制服务 (VSS) 使用集中式 API 简化了 Microsoft 服务器解决方案的备份.Microsoft SharePoint Foundation 包括一个参考 VSS ...

  7. 应用matplotlib绘制地图

    #!/usr/bin/env python # -*- coding: utf-8 -*- from math import sqrt import shapefile from matplotlib ...

  8. 刀锋上前行!绕过Ramint蠕虫病毒直接脱壳

    系统 : Windows xp 程序 : 某游戏客户端 程序下载地址 :不提供 要求 : 脱去压缩壳 使用工具 : OD & PEID & LordPE & Import RE ...

  9. DevExtreme官方视频教程分享

    收集在此,希望对使用这个工具的人有帮助 DevExtreme 1 2 3 4 5 6 DevExpress DevExtreme入门视频一:Getting Started DevExpress Dev ...

  10. Sql Server 2008 数据库附加失败提示9004错误解决办法

    附加数据库 对于 服务器“WSS_Content”失败.  (Microsoft.SqlServer.Smo)执行 Transact-SQL 语句或批处理时发生了异常. (Microsoft.SqlS ...