有关pcap文件相关内容的学习

最近的工作内容都跟网络有关, 因此少不了抓包的工作. 很多时候还需要稍微解析和使用一下抓包爪下来的pcap文件, 于是乎决定稍微了解下libpcap的相关内容.

pcap文件

cap文件是libpcap定义的一种用于存放抓包信息的文件. 文件主要是由一个全局的文件头以及若干数据块组成, 数据块是由数据块头部以及包信息组成. 具体的可以参照这里. 在使用tcpdump抓包时可以加上-w参数来生成cap文件. 有了这个文件, 之后就可以随时使用tcpdump或者wireshark来查看对应的内容.

解析

既然已经知道了文件的格式, 我们自然可以手动来对包来进行解析. 这里主要有两种方式, 一种是使用libpcap提供的接口, 一种是纯粹根据定义来解析. 我自己在做实验的时候选择了后面一种方式, 但是出了个问题. 先看一下pcap的头文件中对于两个头部信息的定义:

pcap/pcap.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct pcap_file_header {
    bpf_u_int32 magic;
    u_short version_major;
    u_short version_minor;
    bpf_int32 thiszone; /* gmt to local correction */
    bpf_u_int32 sigfigs;    /* accuracy of timestamps */
    bpf_u_int32 snaplen;    /* max length saved portion of each pkt */
    bpf_u_int32 linktype;   /* data link type (LINKTYPE_*) */
};

/* 
 * Generic per-packet information, as supplied by libpcap. 
 * 
 * The time stamp can and should be a "struct timeval", regardless of 
 * whether your system supports 32-bit tv_sec in "struct timeval", 
 * 64-bit tv_sec in "struct timeval", or both if it supports both 32-bit 
 * and 64-bit applications.  The on-disk format of savefiles uses 32-bit 
 * tv_sec (and tv_usec); this structure is irrelevant to that.  32-bit 
 * and 64-bit versions of libpcap, even if they're on the same platform, 
 * should supply the appropriate version of "struct timeval", even if 
 * that's not what the underlying packet capture mechanism supplies. 
 */
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) */
};

尤其注意pcap_pkghdr中对于struct timeval的定义. 起初我直接引用该头文件, 在读取pcap_pkthdr结构时总是读错. 这里推荐自己手动定义这个结构体.

相对于自己手动写, 使用libpcap来读取信息可要简单多了. 官方的文档主要介绍了抓包相关的api, 读写cap文件可以参考tcpdump的源码. 下面是我自己写的一个小例子:

dump.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>

#include <netinet/ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void
dump_cap(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt)
{
    struct ip     *iphdr;
    char           ip_src[32];
    char           ip_dst[32];

    iphdr = (struct ip *)(pkt + sizeof(struct ethhdr));

    printf("%s => %s\n",
            inet_ntop(AF_INET, &iphdr->ip_src, ip_src, 32),
            inet_ntop(AF_INET, &iphdr->ip_dst, ip_dst, 32));

    return;
}

void
dump(const char *path)
{
    int                      cnt = -1;
    char                     buf[1024] = {0};
    pcap_t                  *pd;

    pd = pcap_open_offline(path, buf);
    if (pd == NULL) {
        printf("open %s failed: %s\n", path, buf);
        return;
    }

    pcap_loop(pd, cnt, dump_cap, NULL);

    pcap_close(pd);

    return;
}

int
main(int argc, char *argv[])
{
    const char *cap_file_path;

    cap_file_path = NULL;

    if (argc == 1) {
        printf("please give the path of the cap file\n");
        return -1;
    }

    cap_file_path = argv[1];

    dump(cap_file_path);

    return 0;
}

利用cap文件

仅仅知道cap文件的读取感觉没什么意思, 如果我们能够来改写它就好了, 比如mac地址, ip, 端口之类的. 这样就可以便于进行实验与测试. wireshark本身带了两个工具: editcapmergecap. 一个用来进行格式转换, 一个用来将多个cap文件合并成一个, 但是没法进行改写, 因此这里推荐两个项目tcpreplaybittwist. 前者包含了cap包的改写以及回放功能, 工程比较大, 后者主要是改写, 代码也比较精简. 比较有意思的是前者主要使用libpcap的库, 而后者基本都是根据协议自己来操作.

这里简单介绍下tcpreplay中的tcprewrite的使用. 这个工具可以数据包中的协议头中的相关的数据. 主要分两部走:

  1. tcpprep来进行cap文件的预解析, 主要为tcprewrite作准备. 这一步可以省略
  2. tcprewrite进行改写, 部分参数需要依赖第一步.

比如假设我们需要将包中的所有源与目的ip都改成ip_aip_b, 可以走如下流程:

1
2
3
4
5
# step1, -p means use port to determine src and dst
tcpprep -i test.cap -o test.cache.cap -p

# step2
tcprewrite -i test.cap -c test.cache.cap -o rewrited.cap -e ip_a:ip_b

如果要改端口, 使用对应的-r <old_port>:<new_port>[,更多]即可. 更多例子请参考这里

通过改写包我们就可以生成我们所需要的cap文件, 方便测试了~

Comments