• 【转载】解析Winpcap截获的数据包


    首先要清楚的是,Winpcap截获的数据包与Windows Raw socket截获的数据包不同的是,raw socket截获的数据包只局限于传输层(请参考有关OSI模型知识),也就是所只能够截获tcp,udp,icmp等高层协议,而Winpcap截获的数据包是从数据链路层开始的,它的层次更低,从而可以更深入,更详细地分析。
    由于Winpcap数据包查找网卡,打开网卡,设置过滤器等代码到处可见,
    参考http://www.coffeecat.net.cn/WinPcap/html/group__wpcap__tut6.html
    所以,前面部分的代码我在这里就不介绍了。我将专门介绍如何分析数据包。
    当一有通过过滤器的数据包被截获之后,一个回调函数将被调用,这个函数的原型是:

    /* 回调函数原型 */
    void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
    其中     header为一个结构体指针: 


    struct pcap_pkthdr {

        struct timeval ts;   /* time stamp */

        bpf_u_int32 caplen; /* length of portion present */

        bpf_u_int32 len; /* length this packet (off wire) */

    };

    caplen为截获的数据包总长度,pkt_data为截获的数据包的字节指针。
    我现在以一个我截获的数据包为例进行分析,数据包如下:

    上图为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;

    开始6个字节为目标MAC地址(00 06 5B F4 9F 68),接着的6个字节为源MAC地址(00 09 6B 8F D4 57
    ),接着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;
       好了,就写到这了,希望此文对大家会有所帮助。

  • 相关阅读:
    虚拟机LUN扩大后,重新分区
    rm: cannot remove `/home/cn0000/log/formlog.20140417': Read-only file system
    sybase 备份和恢复
    全表扫描计算成本
    标量子查询 子查询执行次数计算公式
    left join 关联条件位置
    动态SQL使用绑定变量
    如何在SQLServer中处理每天四亿三千万记录的
    移动端UI一致性解决方案
    工业互联网中MES系统的重要性
  • 原文地址:https://www.cnblogs.com/dorothychai/p/2271242.html
Copyright © 2020-2023  润新知