原始数据包的分析

 学习NDIS一段时间了,不过还是毫无头绪,理论都能明白,可是不知道怎么下手去做,网上没有没有太详细的教程。我是比较笨,而且比较懒的。:)

  所以准备暂缓NDIS网络和驱动方面的学习,等今后遇见师父了再请教之,好运~

  NDIS中的网络数据都是原始的,即没有经过主机协议栈处理的网络数据,如果学习网络协议的话,我觉得研究原始数据包还是挺合适的。

  Socket通信中,主机A与主机B之间通信,Socket接收到的内容都是通信的内容,没有附带主机A或主机B的信息,这点可以回想最开始学习Socket编程的时候,主机A发送“Hello,mydearfriend.”,那么主机B收到的确实是“Hello,mydearfriend.”。不像原始数据包中,还会带有以太网帧头、IP头、TCP/UDP头,然后才是通信的内容。所以呢,当我们通过NDIS获取到了原始数据包之后,应该按照现在的网络通信模型对原始数据包进行解析。

=============================================================================================

  常用的以太网MAC帧格式有两种,标准:DIXEthernetV2标准(即以太网V2标准)和IEEE的802.3标准。现在使用最多的是以太网V2的MAC帧格式。该标准由5个字段组成:6字节的目的地址、6字节的源地址、2字节的类型(用来标识上一层的协议类型,例如:0x0800表示上层使用是IP数据报)、46字节至1500字节长度不等的IP数据报,以及4字节的帧检验序列(FCS)。分析每一帧可以得到此数据包的源MAC地址和目的MAC地址,并且可以得到IP数据报的完整内容。

  IP数据报的第4至7位是IP首部长度,该字段可以用来准确定位上层协议的起始位置(例如:TCP的首部)。第10字节是协议字段,指出该IP数据报携带的数据使用何种协议,常用的协议字段如表1所示。第13至16字节是源IP地址,第17至20字节是目的IP地址。因此可以据此解析出该数据包的源IP地址和目的IP地址。

表1常用的IP协议和相应的字段值

协议名

ICMP

IGMP

TCP

EGP

IGP

UDP

IPv6

OSPF

协议字段值

1

3

6

8

9

17

41

89

  TCP报文段的第1至2字节是源端口号,第3至4字节是目的端口号。第13字节的前4位是数据偏移,该字段实际上指出了TCP报文段的首部长度。如果TCP承载的是HTTP协议,则可以据此定位HTTP协议的起始位置。系统处理时,如果分析是HTTP协议,则提取出HTTP的内容,以明文显示。而可以直接显示HTTP报文信息的原因是:HTTP是应用层协议,使用面向连接的TCP作为传输层协议,在向下层传递时是以明文的方式直接传递,即TCP在HTTP报文前加TCP头封装HTTP报文。因此,只要把捕获到的数据包依次剥去MAC头、IP头、TCP头,就可以显示HTTP报文的内容了。

  HTTP报文分为两种:请求报文和响应报文。

  对于判断是否为HTTP报文,目前还没有快速有效的方法。传统的方法依据:①传送HTTP报文前是否有TCP的三次握手;②判断数据包中是否含有诸如“GET”、“HTTP/1.1”等关键字。对于这两种方法,第①点需要一定的空间开销,要判断TCP承载的是否为HTTP报文,需要分配一定的空间存储前3个数据包的相关信息(实际并不需要存储3个数据包的全部内容,但是即便是若干比特的信息,也会增加NPF的负担),这给NPF(NetgroupPacketFilter)的运行速率和存储器带来挑战;对于第②点,由于HTTP是面向文本的,因此在报文中的每一个字段都是一些ASCII码串,因而各个字段的长度都是不确定的。请求报文中除了“GET”方法外,还有7种常用的方法,如果要一一模式匹配来确定是否是HTTP报文,显然会造成很大的时间开销。此外,响应报文含“HTTP/1.1”(或HTTP/1.0)的版本号,这个字段在请求报文中也有,具体的版本是1.1还是1.0则是不确定的。因此,综合考虑时间和空间的开销,本文采取模式匹配“HTTP”的方法来判断是否为HTTP报文。具体方法是:捕获到的数据包从TCP的尾部(HTTP的第一个字节)开始匹配“HTTP”,如果匹配成功,则认为是HTTP报文,否则就不是。这种方法存在的问题是:①如果TCP承载的是FTP或SMTP等其他应用层协议,恰好在某个部分也含有“HTTP”字样,则会误判为HTTP报文。但是由于基于HTTP协议的Web服务已经成为Internet的主流,非HTTP协议只占到小部分,因此误判的几率很小。系统测试期间,还未发现此类误判问题,用户界面显示的明文信息表明:确为HTTP协议的请求报文或响应报文。②该方法本身不存在“漏判”,因为所有的HTTP报文都含有“HTTP”字样,而抓到的数据包只要含有“HTTP”字样,就被过滤认为HTTP报文。但是,由于该方法需要一定的时间开销,可能来不及匹配后续的数据包而造成广义上的“漏判”。这个问题通过编写高效的模式匹配算法可以得到一定的改进,但是不能解决根本问题。根本问题在于匹配速度和内核的存储器容量的限制。

  对于过滤用户指定的IP地址和端口号,用户输入的IP地址为char[]字符数组型,而且是用标准的Internet的“.”间隔格式来表示一个Internet地址,而捕获得到的数据包拆包后的IP地址是网络字节的,因此不可直接比较。本文的方法是:首先用inet_addr()将用户输入的IP地址转换成一个无符号长整型数(实际上是in_addr类型),然后与抓到的数据包的4字节IP地址一一比较,即可过滤出用户指定的IP地址。对于端口号亦可类似处理。

=============================================================================================

  这里仅对原始数据包中HTTP的解析,如果是原始数据包中其他应用层的协议解析,也可参考此思路。

  说明:这里例举原始数据包中HTTP的解析的原因是,我想做一个防火墙,不仅可以屏蔽某个网站(这里有两种方法:①屏蔽该网站的IP地址②主机发送到该网站的HTTP请求时,自己通过程序伪造一个HTTP应答在网站的主机返回HTTP应答之前返回给浏览器,毕竟和“127.0.0.1”比其他IP地址通信更快。可能第二种方法有问题。),还可以屏蔽某网站的某个页面(方法可参考屏蔽某个网站的方法②)。

【参考资料感谢作者】
基于WinPcap的数据包捕获和分析系统的设计与实现:http://www.paper.edu.cn

#define ETHERTYPE_IP    0x0800
#define ETHERTYPE_ARP   0x0806

typedef struct _ETHeader         // 14 bytes
{
    UCHAR    dhost[6];            // 目的MAC地址destination mac address
    UCHAR    shost[6];            // 源MAC地址source mac address
    USHORT    type;                // 下层协议类型,如IP(ETHERTYPE_IP)、ARP(ETHERTYPE_ARP)等
} ETHeader, *PETHeader;

#define ARPHRD_ETHER     1

// ARP协议opcodes
#define    ARPOP_REQUEST    1        // ARP 请求    
#define    ARPOP_REPLY        2        // ARP 响应

typedef struct _ARPHeader        // 28字节的ARP头
{
    USHORT    hrd;                //    硬件地址空间,以太网中为ARPHRD_ETHER
    USHORT    eth_type;            //  以太网类型,ETHERTYPE_IP ??
    UCHAR    maclen;                //    MAC地址的长度,为6
    UCHAR    iplen;                //    IP地址的长度,为4
    USHORT    opcode;                //    操作代码,ARPOP_REQUEST为请求,ARPOP_REPLY为响应
    UCHAR    smac[6];            //    源MAC地址
    UCHAR    saddr[4];            //    源IP地址
    UCHAR    dmac[6];            //    目的MAC地址
    UCHAR    daddr[4];            //    目的IP地址
} ARPHeader, *PARPHeader;


typedef union _IPADDRESS{
    ULONG ip;
    UCHAR a[4];
}IPADDRESS, *PIPADDRESS;

//Protocol
typedef struct _IPHeader        // 20
{
    UCHAR     iphVerLen;      // 版本号和头长度(各占4位)
    UCHAR     ipTOS;          // 服务类型 
    USHORT    ipLength;       // 封包总长度,即整个IP报的长度
    USHORT    ipID;              // 封包标识,惟一标识发送的每一个数据报
    USHORT    ipFlags;          // 标志
    UCHAR     ipTTL;          // 生存时间,就是TTL
    UCHAR     ipProtocol;     // 协议,可能是TCP、UDP、ICMP等
    
#define PROTO_ICMP    1
#define PROTO_IGMP    2
#define PROTO_TCP     6
#define PROTO_UDP     17
    USHORT    ipChecksum;     // 校验和
    IPADDRESS srcip;
    IPADDRESS destip;
} IPHeader, *PIPHeader; 


//  define the tcp flags....
#define   TCP_FIN   0x01
#define   TCP_SYN   0x02
#define   TCP_RST   0x04
#define   TCP_PSH   0x08
#define   TCP_ACK   0x10
#define   TCP_URG   0x20
#define   TCP_ACE   0x40
#define   TCP_CWR   0x80

typedef struct _TCPHeader                //20 bytes
{
    USHORT            sourcePort;            // 16位源端口号
    USHORT            destinationPort;    // 16位目的端口号
    ULONG            sequenceNumber;        // 32位序列号
    ULONG            acknowledgeNumber;    // 32位确认号
    UCHAR            dataoffset;            // 高4位表示数据偏移
    UCHAR            flags;                // 6位标志位
//FIN - 0x01
//SYN - 0x02
//RST - 0x04 
//PUSH- 0x08
//ACK- 0x10
//URG- 0x20
//ACE- 0x40
//CWR- 0x80
    USHORT            windows;            // 16位窗口大小
    USHORT            checksum;            // 16位校验和
    USHORT            urgentPointer;        // 16位紧急数据偏移量 
} TCPHeader, *PTCPHeader;

typedef struct _UDPHeader
{
    USHORT            sourcePort;        // 源端口号        
    USHORT            destinationPort;// 目的端口号        
    USHORT            len;            // 封包长度
    USHORT            checksum;        // 校验和
} UDPHeader, *PUDPHeader;

/*tcp_udp校验和尾首部定义*/
typedef struct Psd_head{
    IPADDRESS sadr;            //源IP地址
    IPADDRESS dadr;            //目的IP地址
    u_char        mbz;            //置空(0)
    u_char      proto;            //协议内型
    u_short    tlen;//TCP/UDP数据包的长度(即从TCP/UDP报头算起到数据包结束的长度 单位:字节)
}Psd_header;

typedef struct _ICMPHeader
{
    UCHAR   type;
    UCHAR   code;
    USHORT  checksum;
    USHORT  id;
    USHORT  sequence;
    ULONG   timestamp;
} ICMPHeader, *PICMPHeader;
原文地址:https://www.cnblogs.com/RodYang/p/3237981.html