解析Winpcap截获的数据包
2009-04-28 16:38
现在Winpcap库已经被广泛应用于网络程序,特别是用于网络分析,而多数初学者对于如何分析所截获的数据包还不太清楚。现在就以我的经验来介绍一下,希望能对大家有所帮助。
首先要清楚的是,Winpcap截获的数据包与Windows Raw socket截获的数据包不同的是,raw socket截获的数据包只局限于传输层( 请参考有关OSI模型知识),也就是所只能够截获tcp,udp,icmp等高层协议,而Winpcap截获的数据包是从数据链路层开始的,它的层次更低,从而可以更深入,更详细地分析。 由于Winpcap数据包查找网卡,打开网卡,设置过滤器等代码到处可见, 参考 http://www.coffeecat.net.cn/WinPcap/html/group__wpcap__tut6.html 所以,前面部分的代码我在这里就不介绍了。我将专门介绍如何分析数据包。 当一有通过过滤器的数据包被截获之后,一个回调函数将被调用,这个函数的原型是: /* 回调函数原型 */
我现在以一个我截获的数据包为例进行分析,数据包如下: ![]() 上图为winpcap截获的数据包,现在要知道的是,数据包中到底是什么样的层次呢? 首先是以太网帧,它的格式为: ![]() 它的结构可定义为: // DLC Header typedef struct tagDLCHeader /*以太网数据帧头部结构*/ { unsigned char DesMAC[6]; /* destination HW addrress */ unsigned char SrcMAC[6]; /* source HW addresss */ unsigned short Ethertype; /* ethernet type */ } DLCHEADER, *PDLCHEADER; ),接着2个字节是以太网协议类型(08 00), 参考 以太网类型速查表。 上例中的Ethertype为0x0008,这个数值是网络顺序,要先转为主机顺序才能用,转换的函数为 u_short ntohs( u_short netshort ); 一般类型长度大于1个字节(如unsigned short、unsigned long)的数据在网络中传输都是以网络顺序传播的,都要先转为主机顺序(其实就是顺序反过来)。上述的Ethertype经转换之后成为0x0800,在 以太网类型速查表中可以查找到它对应的协议类型为IP协议。 因为以太网协议类型为IP协议,所以之后接下来的就是IP协议首部,它的结构图为: ![]() typedef struct _IPHeaer { UCHAR iphVerLen; //版本号和头长度(各占4位) UCHAR ipTOS; //服务类型 USHORT ipLength; //封包总长度,即整个IP报的长度 USHORT ipID; //封包标识,惟一标识发送的每一个数据报 USHORT ipFlags; //标志 UCHAR ipTTL; //生存时间,就是TTL UCHAR ipProtocol; //协议,可能是TCP、UDP、ICMP等 USHORT ipChecksum; //校验和 ULONG ipSource; //源IP地址 ULONG ipDestination; //目标IP地址 }IPHeader,*PIPHeader; 然后计算所需的内容,如: ip头长度=(iphVerLen*0x0f)*4; ip报文总长度(ip首部长度+协议(如tcp)首部长度+真实数据(如http数据内容))=ntohs(ipLength); 协议类型=ipProtocol(常用的有 1为ICMP,6为TCP,17为UDP)=6,所以这个数据包为tcp数据包。 源IP地址:ipSource为四个字节的ip地址,通常转为Ip地址字符串(其实也就是把每个字节的十六进制转为十进制),如这里的ipSource为“C0 A8 00 10”,转为字符串即为"192.168.0.16" 转换方法为: in_addr src,dest; src.S_un.S_addr = ipSource; dest.S_un.S_addr = ipDestination; char* ip = inet_ntoa(src);即获得源ip地址 同样可以获得目的ip地址。 因为ip首部中的协议类型是tcp,所以接下来就是tcp首部, ![]() typedef struct _TCPHeader //20个字节 { USHORT sourcePort; //16位源端口号 USHORT destinationPort;//16位目的端口号 ULONG sequenceNumber; //32位序列号 ULONG acknowledgeNumber;//32位确认号 UCHAR dataoffset; //4位首部长度/6位保留字 UCHAR flags; //6位标志位 USHORT windows; //16位窗口大小 USHORT checksum; //16位校验和 USHORT urgentPointer; //16位紧急数据偏移量 }TCPHeader,*PTCPHeader; 同样可以提取所需的数据,如: 源端口=ntohs(sourcePort); 目的端口=ntohs(destinationPort); tcp首部长度=((dataoffset*0xf0)>>4)*4; tcp首部之后,就是携带的真实数据了,它的长度等于IP报文总长度-IP首部长度-TCP首部长度,这几个结构长度都在上面有计算方法了。 好了,上面已经把一个tcp数据包介绍完了,同样,可以采用相同的方法来分析ARP,UDP,ICMP,IGMP等协议数据包。 下面给出一些常用的协议首部结构: // ARP 数据帧 typedef struct tagARPFrame { unsigned short HW_Type; /* hardware type */ unsigned short Prot_Type; /* protocol type */ unsigned char HW_Addr_Len; /* length of hardware address */ unsigned char Prot_Addr_Len; /* length of protocol address */ unsigned short Opcode; /* ARP/RARP */ unsigned char Send_HW_Addr[6]; /* sender hardware address */ unsigned long Send_Prot_Addr; /* sender protocol address */ unsigned char Targ_HW_Addr[6]; /* target hardware address */ unsigned long Targ_Prot_Addr; /* target protocol address */ unsigned char padding[18]; } ARPFRAME, *PARPFRAME; typedef struct _UDPHeader { USHORT sourcePort; //源端口号 USHORT destinationPort;//目的端口号 USHORT len; //封包长度 USHORT checksum; //校验和 }UDPHeader,*PUDPHeader; typedef struct _ICMPHeader { UCHAR icmp_type; //消息类型 UCHAR icmp_code; //代码 USHORT icmp_checksum; //校验和 //下面是回显头 USHORT icmp_id; //用来惟一标识此请求的ID号,通常设置为进程ID USHORT icmp_sequence; //序列号 ULONG icmp_timestamp; //时间戳 }ICMPHeader,*PICMPHeader; typedef struct _IGMPHeader //8字节 { UCHAR hVerType; //版本号和类型(各4位) UCHAR uReserved; //未用 USHORT uCheckSum; //校验和 ULONG dwGroupAddress;//32为组地址(D类IP地址) }IGMPHeader,*PIGMPHeader; 好了,就写到这了,希望此文对大家会有所帮助。 |
http://hi.baidu.com/cwcblog/blog/item/02310a830c5f42b76d811965.html