PCAP文件格式分析(做抓包软件之必备)

转载源:http://blog.csdn.net/anzijin/article/details/2008333

    http://www.ebnd.cn/2009/09/07/file-format-analysis-of-wireshark-pcap/

    

 

前段时间因工作要求,需要对各种数据包进行分析和操作,内容涉及网路协议分析,socket,文件操作等。在此分享下学习和实践的经验。

首先介绍下网络抓包、协议分析的必备软件Ethereal,新版(Wireshark)以下还以 Ethereal代之,目前最新版本已经支持在无线局域网抓包了。Linux和Windows均有对应安装包,它们分别是gcc和VC++编译的。不过 Windows下是基于Winpcap而Linux下则是Libcap。Ethereal作为网路协议分析、学习、开发的敲门软件,其使用技巧及其原理机 制(BPF)网上都有比较详尽的介绍,当初我收集的相关资料随后也会上传,不再多说。下面主要介绍下Ethereal默认的*.pcap文件保存格式。

Pcap文件头24B各字段说明:

Magic:4B:0x1A 2B 3C 4D:用来标示文件的开始
Major:2B,0x02 00:当前文件主要的版本号
Minor:2B,0x04 00当前文件次要的版本号
ThisZone:4B当地的标准时间;全零
SigFigs:4B时间戳的精度;全零
SnapLen:4B最大的存储长度
LinkType:4B链路类型
常用类型:
0 BSD loopback devices, except for later OpenBSD
1            Ethernet, and Linux loopback devices
6            802.5 Token Ring
7            ARCnet
8            SLIP
9            PPP
10           FDDI
100         LLC/SNAP-encapsulated ATM
101         “raw IP”, with no link
102         BSD/OS SLIP
103         BSD/OS PPP
104         Cisco HDLC
105         802.11
108         later OpenBSD loopback devices (with the AF_value in network byte order)
113         special Linux “cooked” capture
114         LocalTalk
 
Packet 包头和Packet数据组成
字段说明:
Timestamp:时间戳高位,精确到seconds
Timestamp:时间戳低位,精确到microseconds
Caplen:当前数据区的长度,即抓取到的数据帧长度,由此可以得到下一个数据帧的位置。
Len:离线数据长度网络中实际数据帧的长度,一般不大于caplen,多数情况下和Caplen数值相等。
Packet 数据:即 Packet(通常就是链路层的数据帧)具体内容,长度就是Caplen,这个长度的后面,就是当前PCAP文件中存放的下一个Packet数据包,也就 是说:PCAP文件里面并没有规定捕获的Packet数据包之间有什么间隔字符串,下一组数据在文件中的起始位置。我们需要靠第一个Packet包确定。 最后,Packet数据部分的格式其实就是标准的网路协议格式了可以任何网络教材上找得到。
 

 

 

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

这里再把pcap文件头的结构定义添上:

typedef unsigned long  DWORD;
typedef unsigned short  WORD;
typedef unsigned char  BYTE;

typedef struct PCAP_FILE_HEADER
{
 DWORD magic;
 WORD versionMajor;
 WORD versionMinor;
 DWORD thisZone;
 DWORD sigfigs;
 DWORD snapLen;
 DWORD linkType;
}PcapFileHeader, *PPcapFileHeader;

##################################################
pcap文件格式是常用的数据报存储格式,包括wireshark在内的主流抓包软件都可以生成这种格式的数据包
下面对这种格式的文件简单分析一下: 
 
pcap文件的格式为:
  文件头    24字节
  数据报头 + 数据报  数据包头为16字节,后面紧跟数据报
  数据报头 + 数据报  ......
 
pcap.h里定义了文件头的格式
struct pcap_file_header {
        bpf_u_int32 magic;
        u_short version_major;
        u_short version_minor;
        bpf_int32 thiszone;    
        bpf_u_int32 sigfigs;   
        bpf_u_int32 snaplen;   
        bpf_u_int32 linktype;  
};
看一下各字段的含义:
 magic:   4字节 pcap文件标识 目前为“d4 c3 b2 a1”
 major:   2字节主版本号     #define PCAP_VERSION_MAJOR 2
 minor:   2字节次版本号     #define PCAP_VERSION_MINOR 4
 thiszone:4字节时区修正     并未使用,目前全为0
 sigfigs: 4字节精确时间戳   并未使用,目前全为0
 snaplen: 4字节抓包最大长度如果要抓全,设为0x0000ffff(65535),
          tcpdump -s 0就是设置这个参数,缺省为68字节
 linktype:4字节链路类型    一般都是1:ethernet
 
 
 
数据报头的格式
struct pcap_pkthdr {
        struct timeval ts;     
        bpf_u_int32 caplen;    
        bpf_u_int32 len;       
};
struct timeval {
        long            tv_sec;        
        suseconds_t     tv_usec;       
};
 ts:    8字节 抓包时间 4字节表示秒数,4字节表示微秒数
 caplen:4字节 保存下来的包长度(最多是snaplen,比如68字节)
 len:   4字节数据报的真实长度,如果文件中保存的不是完整数据包,可能比caplen大
 
下图仅供参考
pcap文件格式分析
 
 
 
########################################

下面主要介绍下Ethereal默认的*.pcap文件保存格式。 

Pcap文件头24B各字段说明:

Magic:4B:0x1A 2B 3C 4D:用来标示文件的开始
Major:2B,0x02 00:当前文件主要的版本号     
Minor:2B,0x04 00当前文件次要的版本号
ThisZone:4B当地的标准时间;全零
SigFigs:4B时间戳的精度;全零
SnapLen:4B最大的存储长度    
LinkType:4B链路类型
常用类型:
 0            BSD loopback devices, except for later OpenBSD
       1            Ethernet, and Linux loopback devices
       6            802.5 Token Ring
       7            ARCnet
       8            SLIP
       9            PPP
       10           FDDI
       100         LLC/SNAP-encapsulated ATM
       101         "raw IP", with no link
       102         BSD/OS SLIP
       103         BSD/OS PPP
       104         Cisco HDLC
       105         802.11
       108         later OpenBSD loopback devices (with the AF_value in network byte order)
       113         special Linux "cooked" capture
       114         LocalTalk
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Packet 包头和Packet数据组成
字段说明:
Timestamp:时间戳高位,精确到seconds     
Timestamp:时间戳低位,精确到microseconds
Caplen:当前数据区的长度,即抓取到的数据帧长度,由此可以得到下一个数据帧的位置。
Len:离线数据长度网络中实际数据帧的长度,一般不大于caplen,多数情况下和Caplen数值相等。
Packet 数据: 即 Packet(通常就是链路层的数据帧)具体内容,长度就是Caplen,这个长度的后面,就是当前PCAP文件中存放的下一个Packet数据包,也就 是说:PCAP文件里面并没有规定捕获的Packet数据包之间有什么间隔字符串,下一组数据在文件中的起始位置。我们需要靠第一个Packet包确定。 最后,Packet数据部分的格式其实就是标准的网路协议格式了可以任何网络教材上找得到。



以下是我的实现,针对自定义的UDP的抓包文件进行解析
typedef struct tagIpHead
{
    int version;//版本
    int headLength; //头长度
    int  diffsever; 
    int  totallength; //总长度
    int  identification; 
    int  flag;
    int  fragment;
    int  ttl;
    int  protocoltype; //协议类型
    int  checksum;
    unsigned long  srcip;//源ip
    unsigned long  dstip;//目的ip
}IP_HEAD;
typedef struct tagUdpHead

    unsigned short  srcport; //源端口
    unsigned short  dstport; //目的端口
    int   length; //udp包长度
}UDP_HEAD;

unsigned long FileParse::Parse( const char* FileName,bool& bThreadRun)//,HWND hwnd )
{
    if (_wass_session)
    {
        delete _wass_session;
        _wass_session = NULL;
    }
    _wass_session = new WassSessions();
    //////////////////////////////////////////////////////////////////////////
    unsigned long lRes =0;
    FILE* pFile=NULL;
    int nReadSize = 0;
    char buff[FILE_READ_LEN];
    char acip[30];
    char portalip[30];
    char radiusip[30];
    unsigned long timestamp1;
    unsigned long timestamp2;
    CConfigure* config=new CConfigure();

    if (config)
    {
        //读取ip地址,添加到iplist中
        unsigned long ipTmp=0; 
        unsigned short portTmp=0;
        config->getIPConfig(acip,portalip,radiusip);
        cut_ip(acip,ipTmp,portTmp);
        acport_list.push_back(portTmp);
        acip_list.push_back(ipTmp);
        cut_ip(portalip,ipTmp,portTmp);
        portalip_list.push_back(ipTmp);
        portalport_list.push_back(portTmp);
        delete config;
        config = NULL;
    }
    //////////////////////////////////////////////////////////////////////////
    memset(buff,0,FILE_READ_LEN);
    do 
    {


        pFile =fopen(FileName,"rb"); 
        //pFile =_open( FileName, _O_RDONLY | _O_BINARY ); 
        if (!pFile)
        {
            //failed for the file opened
            fprintf(stderr, "Open the file failed:%s ", strerror(errno));

            lRes = 2;
            break;
        }
        nReadSize = fread(buff,sizeof(char),24,pFile);
        if (nReadSize == 24)
        {
            while (!feof(pFile) && bThreadRun) 
            {
                memset(buff,0,FILE_READ_LEN);
                nReadSize = fread(buff,sizeof(char),16,pFile);
                unsigned long nPacketLen=0;
                memcpy(&timestamp1,buff,4);
                memcpy(&timestamp2,buff+4,4);
                memcpy(&nPacketLen,buff+8,4);
                //nPacketLen = ntohl(nPacketLen);
                char* buf = new char[nPacketLen];
                memset(buf,0,nPacketLen);

                int nReadCount=0;
                //读取包
                while (nReadCount < nPacketLen)
                {
                    nReadSize = fread(buff,sizeof(char),nPacketLen-nReadCount,pFile);
                    memcpy(buf+nReadCount,buff,nReadSize);
                    nReadCount += nReadSize;
                }
                //在此处处理ip/udp包部分
                int nOffset=14;//数据偏移位置
                _ip->Parse(buf+nOffset);//ip解析
                if(_ip->wass_ip_head.protocoltype==17)//只处理UDP
                {
                    nOffset += 20;
                    _udp->Parse(buf+nOffset);//udp解析
                    nOffset +=8;
                    std::list<unsigned long>::iterator acit= acip_list.begin();
                    std::list<unsigned long>::iterator portalit = portalip_list.begin();
                    bool bFoundIP = false;
                    //暂时不考虑算法,遍历ip地址
                    //while (acit++ != acip_list.end())
                    for (;acit != acip_list.end();acit++)
                    {
                        unsigned long aIP = *acit;
                        char aTmp[20];

                        IPULongToString(aIP,aTmp);
                        IPULongToString(_ip->wass_ip_head.dstip,aTmp);
                        if (_ip->wass_ip_head.dstip== *acit  || _ip->wass_ip_head.srcip == *acit)
                        {
                        for (;portalit !=portalip_list.end();portalit++)
                        {
                            if (_ip->wass_ip_head.dstip== *portalit  || _ip->wass_ip_head.srcip == *portalit)
                            {
                                bFoundIP = true;
                                break;
                            }
                        }
                        break;
                        }
                    }
                    if (bFoundIP)
                    {
                        //此处是表示可以进行数据的解析
                        _portalPacket = new CPortalPacket();
                        _portalPacket->parse(buf + nOffset,nPacketLen - nOffset);
                    //设置包的源IP和目的IP,源端口,目的端口
                        _portalPacket->setIpAndPort(_ip->wass_ip_head.srcip,
                            _ip->wass_ip_head.dstip,_udp->wass_udp_head.srcport,_udp->wass_udp_head.dstport);
                        _portalPacket->setPacketTime(timestamp1,timestamp2); 
                        _wass_session->AddPacket(_portalPacket,_sessions);
                    }
                    else
                    {
                        
                    }
                }
                if (buf)
                {
                    delete [] buf;

                }

            }
        }
    } while (false);
    if (pFile)
    {
        fclose(pFile);
    }

    //////////////////////////////////////////////////////////////////////////
    //::PostMessage(_hwnd,WM_FINISHED,0,0);
    return lRes;
}
 
 
 
 
 
------------------------------------------------------------------------------------------------------------
一、基本格式:

文件头 数据包头 数据报 数据包头 数据报......

pcap文件格式 结构

 

 

文件头

格式如结构体pcap_file_header

数据包头1

格式如pcap_pkthdr

数据包

数据包

数据包头n-1

格式如pcap_pkthdr

数据包

数据包

数据包头n

格式如pcap_pkthdr

数据包

数据包


二、文件头结构体:
sturct pcap_file_header
{
DWORD              magic;
      WORD                 version_major;
      WORD                 version_minor;
DWORD              thiszone;
DWORD              sigfigs;
DWORD              snaplen;
DWORD              linktype;
}

说明:

1、标识位:32位的,这个标识位的值是16进制的 0xa1b2c3d4。
a 32-bit        magic number ,The magic number has the value hex a1b2c3d4.
2、主版本号:16位, 默认值为0x2。
a 16-bit          major version number,The major version number should have the value 2.
3、副版本号:16位,默认值为0x04。
a 16-bit          minor version number,The minor version number should have the value 4.
4、区域时间:32位,实际上该值并未使用,因此可以将该位设置为0。
a 32-bit          time zone offset field that actually not used, so you can (and probably should) just make it 0;
5、精确时间戳:32位,实际上该值并未使用,因此可以将该值设置为0。
a 32-bit          time stamp accuracy field tha not actually used,so you can (and probably should) just make it 0;
6、数据包最大长度:32位,该值设置所抓获的数据包的最大长度,如果所有数据包都要抓获,将该值设置为65535; 例如:想获取数据包的前64字节,可将该值设置为64。
a 32-bit          snapshot length&quot; field;The snapshot length field should be the maximum number of bytes perpacket that will be captured. If the entire packet is captured, make it 65535; if you only capture, for example, the first 64 bytes of the packet, make it 64.
7、链路层类型:32位, 数据包的链路层包头决定了链路层的类型。
a 32-bit link layer type field.The link-layer type depends on the type of link-layer header that the
packets in the capture file have:

以下是数据值与链路层类型的对应表
0            BSD       loopback devices, except for later OpenBSD
1            Ethernet, and Linux loopback devices   以太网类型,大多数的数据包为这种类型。
6            802.5 Token Ring
7            ARCnet
8            SLIP
9            PPP
10          FDDI
100        LLC/SNAP-encapsulated ATM
101        raw IP, with no link
102        BSD/OS SLIP
103        BSD/OS PPP
104        Cisco HDLC
105        802.11
108        later OpenBSD loopback devices (with the AF_value in network byte order)
113               special Linux cooked capture
114               LocalTalk




三、数据包头结构体:
struct pcap_pkthdr
{
struct timeval         ts;
      DWORD              caplen;
      DWORD              len;
}

struct timeval
{
DWORD       GMTtime;
DWORD       microTime

说明:

1、时间戳,包括:
秒计时:32位,一个UNIX格式的精确到秒时间值,用来记录数据包抓获的时间,记录方式是记录从格林尼治时间的1970年1月1日 00:00:00 到抓包时经过的秒数;
毫秒计时:32位, 抓取数据包时的毫秒值。
a time stamp, consisting of:
a UNIX-format time-in-seconds when the packet was captured, i.e. the number of seconds since January 1,1970, 00:00:00 GMT (that GMT, *NOT* local time!);   
the number of microseconds since that second when the packet was captured;

2、数据包长度:32位 ,标识所抓获的数据包保存在pcap文件中的实际长度,以字节为单位。
a 32-bit value giving the number of bytes of packet data that were captured;

3、数据包实际长度: 所抓获的数据包的真实长度,如果文件中保存不是完整的数据包,那么这个值可能要比前面的数据包长度的值大。
a 32-bit value giving the actual length of the packet, in bytes (which may be greater than the previous number, if you are not saving the entire packet).

 

 

Wireshark的Pcap文件格式分析

前段时间因工作要求,需要对各种数据包进行分析和操作,内容涉及网路协议分析,socket,文件操作等。在此分享下学习和实践的经验。  

  首先介绍下网络抓包、协议分析的必备软件Ethereal,新版(Wireshark)以下还以 Ethereal代之,目前最新版本已经支持在无线局域网抓包了。Linux和Windows均有对应安装包,它们分别是gcc和VC++编译的。不过 Windows下是基于Winpcap而Linux下则是Libcap。Ethereal作为网路协议分析、学习、开发的敲门软件,其使用技巧及其原理机制(BPF)网上都有比较详尽的介绍,当初我收集的相关资料随后也会上传,不再多说。下面主要介绍下Ethereal默认的*.pcap文件保存格式。 

Pcap文件头24B各字段说明:

Magic:4B:0x1A 2B 3C 4D:用来标示文件的开始Major:2B,0x02 00:当前文件主要的版本号      Minor:2B,0x04 00当前文件次要的版本号ThisZone:4B当地的标准时间;全零SigFigs:4B时间戳的精度;全零SnapLen:4B最大的存储长度    LinkType:4B链路类型常用类型: 0            BSD loopback devices, except for later OpenBSD
       1            Ethernet, and Linux loopback devices
       6            802.5 Token Ring
       7            ARCnet
       8            SLIP
       9            PPP
       10           FDDI
       100         LLC/SNAP-encapsulated ATM
       101         "raw IP", with no link
       102         BSD/OS SLIP
       103         BSD/OS PPP
       104         Cisco HDLC
       105         802.11
       108         later OpenBSD loopback devices (with the AF_value in network byte order)
       113         special Linux "cooked" capture
       114         LocalTalk  Packet 包头和Packet数据组成字段说明:Timestamp:时间戳高位,精确到seconds      Timestamp:时间戳低位,精确到microsecondsCaplen:当前数据区的长度,即抓取到的数据帧长度,由此可以得到下一个数据帧的位置。Len:离线数据长度:网络中实际数据帧的长度,一般不大于caplen,多数情况下和Caplen数值相等。Packet 数据:即 Packet(通常就是链路层的数据帧)具体内容,长度就是Caplen,这个长度的后面,就是当前PCAP文件中存放的下一个Packet数据包,也就是说:PCAP文件里面并没有规定捕获的Packet数据包之间有什么间隔字符串,下一组数据在文件中的起始位置。我们需要靠第一个Packet包确定。最后,Packet数据部分的格式其实就是标准的网路协议格式了可以任何网络教材上找得到。
 
-------------------------------------------------------------------

Python读取pcap文件

 

From:http://blog.sina.com.cn/s/blog_4b5039210100gnlu.html

 

    想试一试读取pcap文件的内容,并且分析出pcap文件头,每一包数据的pcap头,每一包的数据内容(暂时不包括数据包的协议解析),关于pcap文件的格式,可以参看:http://blog.sina.com.cn/s/blog_4b5039210100fzrt.html

    搞了一下午,写了一个py文件,rdpcap.py,想把里面的二进制文件全部弄成十六进制的,然后作为字符串写入到一个txt文件,转化成字符串是为了显示看起来方便。

    程序如下:

 

#!/usr/bin/env python
#coding=utf-8
#读取pcap文件,解析相应的信息,为了在记事本中显示的方便,把二进制的信息

import struct

fpcap = open('test.pcap','rb')
ftxt = open('result.txt','w')

string_data = fpcap.read()

#pcap文件包头解析
pcap_header = {}
pcap_header['magic_number'] = string_data[0:4]
pcap_header['version_major'] = string_data[4:6]
pcap_header['version_minor'] = string_data[6:8]
pcap_header['thiszone'] = string_data[8:12]
pcap_header['sigfigs'] = string_data[12:16]
pcap_header['snaplen'] = string_data[16:20]
pcap_header['linktype'] = string_data[20:24]
#把pacp文件头信息写入result.txt
ftxt.write("Pcap文件的包头内容如下: \n")
for key in ['magic_number','version_major','version_minor','thiszone',
            'sigfigs','snaplen','linktype']:
    ftxt.write(key+ " : " + repr(pcap_header[key])+'\n')
          

#pcap文件的数据包解析
step = 0
packet_num = 0
packet_data = []

pcap_packet_header = {}
i =24

while(i<len(string_data)):
     
      #数据包头各个字段
      pcap_packet_header['GMTtime'] = string_data[i:i+4]
      pcap_packet_header['MicroTime'] = string_data[i+4:i+8]
      pcap_packet_header['caplen'] = string_data[i+8:i+12]
      pcap_packet_header['len'] = string_data[i+12:i+16]
      #求出此包的包长len
      packet_len = struct.unpack('I',pcap_packet_header['len'])[0]
      #写入此包数据
      packet_data.append(string_data[i+16:i+16+packet_len])
      i = i+ packet_len+16
      packet_num+=1
   
   
   
   
#把pacp文件里的数据包信息写入result.txt
for i in range(packet_num):
    #先写每一包的包头
    ftxt.write("这是第"+str(i)+"包数据的包头和数据:"+'\n')
    for key in ['GMTtime','MicroTime','caplen','len']:
        ftxt.write(key+' : '+repr(pcap_packet_header[key])+'\n')
    #再写数据部分
    ftxt.write('此包的数据内容'+repr(packet_data[i])+'\n')
ftxt.write('一共有'+str(packet_num)+"包数据"+'\n')
      
ftxt.close()
fpcap.close()

 

最终得到的result文件如下所示:字段显示的都是十六进制,其中的数据部分和wireshark打开,显示的十六进制窗口一样。

(其实如果程序想到得到某一个或某几个字节的十进制数,用struct还是很容易转换的)

posted @ 2017-10-11 12:03  致橡树的你  阅读(10510)  评论(0编辑  收藏  举报