学习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;