原始套接字简介(原始套接字系列一)

大多数程序员所接触到的套接字(Socket)为两类:

  (1)流式套接字(SOCK_STREAM):一种面向连接的Socket,针对于面向连接的TCP服务应用

  (2)数据报式套接字(SOCK_DGRAM):一种无连接的Socket,对应于无连接的UDP服务应用。

  从用户的角度来看,SOCK_STREAM、SOCK_DGRAM这两类套接字似乎的确涵盖了TCP/IP应用的全部,因为基于TCP/IP的应用,从协议栈的层次上讲,在传输层的确只可能建立于TCP或UDP协议之上(图1),而SOCK_STREAM、SOCK_DGRAM又分别对应于TCP和UDP,所以几乎所有的应用都可以用这两类套接字实现。



图1 TCP/IP协议栈

  但是,当我们面对如下问题时,SOCK_STREAM、SOCK_DGRAM将显得这样无助:

  (1) 怎样发送一个自定义的IP包?

  (2) 怎样发送一个ICMP协议包?

  (3) 怎样使本机进入杂糅模式,从而能够进行网络sniffer?

  (4) 怎样分析所有经过网络的包,而不管这样包是否是发给自己的?

  (5) 怎样伪装本地的IP地址?

  这使得我们必须面对另外一个深刻的主题――原始套接字(Raw Socket)。Raw Socket广泛应用于高级网络编程,也是一种广泛的黑客手段。著名的网络sniffer、拒绝服务攻击(DOS)、IP欺骗等都可以以Raw Socket实现。

  Raw Socket与标准套接字(SOCK_STREAM、SOCK_DGRAM)的区别在于前者直接置"根"于操作系统网络核心(Network Core),而SOCK_STREAM、SOCK_DGRAM则"悬浮"于TCP和UDP协议的外围,如图2所示:



图2 Raw Socket与标准Socket

  当我们使用Raw Socket的时候,可以完全自定义IP包,一切形式的包都可以"制造"出来。因此,本文事先必须对TCP/IP所涉及IP包结构进行必要的交待。

  目前,IPv4的报头结构为:

版本号(4) 包头长(4) 服务类型(8)
数据包长度(16)

标识(16)

偏移量(16)

生存时间(8)

传输协议(8)

校验和(16)

源地址(32)
 

目的地址(32)
 

选项(8)

.........

填充

  对其进行数据结构封装:

typedef struct _iphdr //定义IP报头 

 unsigned char h_lenver; //4位首部长度+4位IP版本号 
 unsigned char tos; //8位服务类型TOS 
 unsigned short total_len; //16位总长度(字节) 
 unsigned short ident; //16位标识 
 unsigned short frag_and_flags; //3位标志位 
 unsigned char ttl; //8位生存时间 TTL 
 unsigned char proto; //8位协议 (TCP, UDP 或其他) 
 unsigned short checksum; //16位IP首部校验和 
 unsigned int sourceIP; //32位源IP地址 
 unsigned int destIP; //32位目的IP地址 
} IP_HEADER;

  或者将上述定义中的第一字节按位拆分:

typedef struct _iphdr //定义IP报头 

 unsigned char h_len : 4; //4位首部长度
 unsigned char ver : 4; //4位IP版本号 
 unsigned char tos; 
 unsigned short total_len; 
 unsigned short ident; 
 unsigned short frag_and_flags; 
 unsigned char ttl; 
 unsigned char proto; 
 unsigned short checksum; 
 unsigned int sourceIP; 
 unsigned int destIP; 
} IP_HEADER;

  更加严格地讲,上述定义中h_len、ver字段的内存存放顺序还与具体CPU的Endian有关,因此,更加严格的IP_HEADER可定义为:

typedef struct _iphdr //定义IP报头 

 #if defined(__LITTLE_ENDIAN_BITFIELD)
  unsigned char h_len : 4; //4位首部长度
  unsigned char ver : 4; //4位IP版本号 
 #elif defined (__BIG_ENDIAN_BITFIELD)
  unsigned char ver : 4; //4位IP版本号 
  unsigned char h_len : 4; //4位首部长度
 #endif
 unsigned char tos; 
 unsigned short total_len; 
 unsigned short ident; 
 unsigned short frag_and_flags; 
 unsigned char ttl; 
 unsigned char proto; 
 unsigned short checksum; 
 unsigned int sourceIP; 
 unsigned int destIP; 
} IP_HEADER;

  TCP报头结构为:


源端口(16)

目的端口(16)

序列号(32)

确认号(32)

TCP偏移量(4)

保留(6)

标志(6)

窗口(16)

校验和(16)

紧急(16)

选项(0或32)

数据(可变)

  对应数据结构:

typedef struct psd_hdr //定义TCP伪报头 

 unsigned long saddr; //源地址 
 unsigned long daddr; //目的地址 
 char mbz; 
 char ptcl; //协议类型 
 unsigned short tcpl; //TCP长度 
}PSD_HEADER; 
typedef struct _tcphdr //定义TCP报头 

 unsigned short th_sport; //16位源端口 
 unsigned short th_dport; //16位目的端口 
 unsigned int th_seq; //32位序列号 
 unsigned int th_ack; //32位确认号 
 unsigned char th_lenres; //4位首部长度/4位保留字 
 unsigned char th_flag; //6位标志位 
 unsigned short th_win; //16位窗口大小 
 unsigned short th_sum; //16位校验和 
 unsigned short th_urp; //16位紧急数据偏移量 
} TCP_HEADER;

  同样地,TCP头的定义也可以将位域拆分:

typedef struct _tcphdr
{
 unsigned short th_sport; 
 unsigned short th_dport; 
 unsigned int th_seq; 
 unsigned int th_ack; 
 /*little-endian*/
 unsigned short tcp_res1: 4, tcp_hlen: 4, tcp_fin: 1, tcp_syn: 1, tcp_rst: 1, tcp_psh: 1, tcp_ack: 1, tcp_urg: 1, tcp_res2: 2;
 unsigned short th_win; 
 unsigned short th_sum; 
 unsigned short th_urp; 
} TCP_HEADER;

  UDP报头为:


源端口(16)

目的端口(16)

报文长(16)

校验和(16)

  对应的数据结构为:

typedef struct _udphdr //定义UDP报头 
{
 unsigned short uh_sport;//16位源端口
 unsigned short uh_dport;//16位目的端口
 unsigned short uh_len;//16位长度
 unsigned short uh_sum;//16位校验和
} UDP_HEADER;

  ICMP协议是网络层中一个非常重要的协议,其全称为Internet Control Message Protocol(因特网控制报文协议),ICMP协议弥补了IP的缺限,它使用IP协议进行信息传递,向数据包中的源端节点提供发生在网络层的错误信息反馈。ICMP报头为:


类型(8)

代码(8)

校验和(16)
消息内容

  常用的回送与或回送响应ICMP消息对应数据结构为:

typedef struct _icmphdr //定义ICMP报头(回送与或回送响应)

 unsigned char i_type;//8位类型
 unsigned char i_code; //8位代码 
 unsigned short i_cksum; //16位校验和 
 unsigned short i_id; //识别号(一般用进程号作为识别号) 
 unsigned short i_seq; //报文序列号 
 unsigned int timestamp;//时间戳 
} ICMP_HEADER;

  常用的ICMP报文包括ECHO-REQUEST(响应请求消息)、ECHO-REPLY(响应应答消息)、Destination Unreachable(目标不可到达消息)、Time Exceeded(超时消息)、Parameter Problems(参数错误消息)、Source Quenchs(源抑制消息)、Redirects(重定向消息)、Timestamps(时间戳消息)、Timestamp Replies(时间戳响应消息)、Address Masks(地址掩码请求消息)、Address Mask Replies(地址掩码响应消息)等,是Internet上十分重要的消息。后面章节中所涉及到的ping命令、ICMP拒绝服务攻击、路由欺骗都与ICMP协议息息相关。

  另外,本系列文章中的部分源代码参考了一些优秀程序员的开源项目,由于篇幅的关系我们不能一一列举,在此一并表示感谢。

  So, let‘s go.

原始套接字简介(原始套接字系列一),布布扣,bubuko.com

时间: 2024-10-18 09:59:13

原始套接字简介(原始套接字系列一)的相关文章

nginx源码分析--监听套接字的创建 套接字的监听 HTTP请求创建连接

作为一个web服务器,那么肯定是有监听套接字的,这个监听套接字是用于接收HTTP请求的,这个监听套接字的创建是根据配置文件的内容来创建的,在nginx.conf文件中有多少个地址就需要创建多少个监听套接字.这里不说各个结构体的构造 只说大体情况! 1).首先在main函数中调用了ngx_init_cycle()函数,在这个函数的最后调用了ngx_open_listening_sockets函数,这个函数负责将创建的监听套接字进行套接字选项的设置(比如非阻塞.接受发送的缓冲区.绑定.监听处理) 2

什么是套接字(Socket)?套接字(Socket)是什么意思?(转)

应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题.多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据.为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口,区分不同应用程序进程间的网络通信和连接. 网络化的应用程序在开始任何通讯之前都必需要创建套接字.就像电话的插口一样,没有它就完全没办法通信. 生成套接字,主要有3个参数:通信的目的IP地址.使用的传输层协议(

流式套接字(SOCK_STREAM),数据报套接字 (SOCK_DGRAM) 的比较

1.流式套接字 使用这种套接字时,数据在客户端是顺序发送的,并且到达的顺序是一致的.比如你在客户端先发送1,再发送2,那么在服务器端的接收顺序是先接收到1,再接收到2,流式套接字是可靠的,是面向连接的: 2.数据报套接字 这种套接字是无连接的,数据是打包成数据包发送的,到达的顺序不一定与发送的顺序是一致的,并且数据不一定是可达的,并且接收到的数据还可能出错. 既然这样那为什么还要使用这种套接字呢?因为现每个使用udp的程序都有自己的对数据进行确认的协议.如TFTP协议规定了每收到一个消息比如,

Unix域套接字简介

在Linux系统中,有很多进程间通信方式,套接字(Socket)就是其中的一种.但传统的套接字的用法都是基于TCP/IP协议栈的,需要指定IP地址.如果不同主机上的两个进程进行通信,当然这样做没什么问题.但是,如果只需要在一台机器上的两个不同进程间通信,还要用到IP地址就有点大材小用了. 其实很多人并不一定知道,对于套接字来说,还存在一种叫做Unix域套接字的类别,专门用来解决这个问题.其API的掉用方法基本上和普通TCP/IP的套接字一样,只是有些许差别. 因此,再正式介绍之前,先来复习一下套

BZOJ 3196 二逼平衡树 树套树(线段树套Treap)

题目大意: 写一种数据结构,他可以: 1.查询k在区间内的排名. 2.查询区间内排名为k的值 3.修改某一个值. 4.求k在区间内的前驱. 5.求k在区间内的后继. 思路:本来以为有什么只有神犇才知道的神一般的数据结构来维护它,问了别人之后,发现只是树套树.据说怎么套都行.我见识鄙陋,就只能线段树套Treap了. 这也是第一次写树套树,还1A了,有点开心. 写树套树,一定要确定自己对这两个树及其熟练,加上少量精细的思考,就可以完成树套树.(我只是弱渣,求神犇别D) 具体实现:第一层是线段树,第二

一个土豪玩家养十个免费玩家陪自己玩 免费游戏的关键在一个“诱”字,核心在一个“贱”字,本质是一个“骗”字

“老子玩游戏从来是不花钱的!”多年来这一直是中国玩家最引以为傲的地方,从过去的“盗版下载”到如今的“免费游戏”,始终如此,优越感秒杀美欧日韩! 可以说得益于中国独特的游戏环境,在这片土地上孕育了一批可能是世界上最抠门的玩家,“如何让中国玩家为游戏掏钱”简直是一个世界性难题!当然天才的中国运营商最终找到了为这些铁公鸡拔毛的办法. “免费游戏”模式最早出现于国内的一些私服,之后<热血传奇><传奇世界>等大型网游也开始试水,直到<征途>震撼上线,首次明确了“一个土豪玩家养十个

python使用原始套接字 解析原始ip头数据

使用底层套接字解码底层流量,是这次做的重点工作. 首先来捕获第一个包 # coding:utf-8import socket # 监听的主机IP host = "192.168.1.100" socket_protocol = socket.IPPROTO_ICMP sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol) sniffer.bind((host, 0)) sniffer.setso

nginx源代码分析--监听套接字的创建 套接字的监听 HTTP请求创建连接

作为一个webserver,那么肯定是有监听套接字的,这个监听套接字是用于接收HTTP请求的,这个监听套接字的创建是依据配置文件的内容来创建的,在nginx.conf文件里有多少个地址就须要创建多少个监听套接字.这里不说各个结构体的构造 仅仅说大体情况! 1).首先在main函数中调用了ngx_init_cycle()函数,在这个函数的最后调用了ngx_open_listening_sockets函数,这个函数负责将创建的监听套接字进行套接字选项的设置(比方非堵塞.接受发送的缓冲区.绑定.监听处

套接口编程简介

1.套接口地址结构 POSIX规范只需要结构中的三个成员:sin_family.sin_addr.sin_port.其中sin_addr又是一个结构 2.通用套接口地址结构 套接口函数被定义为采用指向通用套接口地址结构的指针.由于套接口函数的定义并没有使用void*指针类型, 所以调用套接口函数时必须进行强制类型转换. 使用通用套接口地址结构的原因:内核必须依据通用套接口地址结,检查sin_family的值来确定结构的类型 3.字节排序函数 多字节数据在内存中存储有两种方法.小端字节序把低字节存