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两个设备要进 ...
随机推荐
- JS代码实用代码实例(输入框监听,点击显示点击其他地方消失,文件本地预览上传)
前段时间写前端,遇到一些模块非常有用,总结以备后用 一.input框字数监听 <!DOCTYPE html> <html lang="en"> <he ...
- phonegap安卓手机开发入门
先安装安卓开发安环境 http://www.cnblogs.com/zhangsanshi/p/3582368.html 安装phonegap 在安装ant http://www.cnblogs.co ...
- [deviceone开发]-动画示例源码
一.简介 do_FrameAnimtionView组件是用加载GIF动态图片和加载一系列图片形成动画效果的展示组件,这个示例直观的展示组件基本的使用方式.适合初学者. 二.效果图 三.相关讨论 htt ...
- 通过gulp为requireJs引入的模块添加版本号
由于项目用到requireJs,并且通过gulp来对项目进行统一的管理,为了防止浏览器对文件进行缓存,所以通过gulp为项目中的文件添加版本号. 1.分别安装gulp-rev.gulp-rev-col ...
- SAP中发送邮件
WITH HEADER LINE, docdata LIKE sodocchgi1, objtxt WITH HEADER LINE, objpack WITH HEADER LINE, reclis ...
- SharePoint 2013 和卷影复制服务(VSS)概述
对备份供应商而言,卷影复制服务 (VSS) 使用集中式 API 简化了 Microsoft 服务器解决方案的备份.Microsoft SharePoint Foundation 包括一个参考 VSS ...
- 应用matplotlib绘制地图
#!/usr/bin/env python # -*- coding: utf-8 -*- from math import sqrt import shapefile from matplotlib ...
- 刀锋上前行!绕过Ramint蠕虫病毒直接脱壳
系统 : Windows xp 程序 : 某游戏客户端 程序下载地址 :不提供 要求 : 脱去压缩壳 使用工具 : OD & PEID & LordPE & Import RE ...
- DevExtreme官方视频教程分享
收集在此,希望对使用这个工具的人有帮助 DevExtreme 1 2 3 4 5 6 DevExpress DevExtreme入门视频一:Getting Started DevExpress Dev ...
- Sql Server 2008 数据库附加失败提示9004错误解决办法
附加数据库 对于 服务器“WSS_Content”失败. (Microsoft.SqlServer.Smo)执行 Transact-SQL 语句或批处理时发生了异常. (Microsoft.SqlS ...