当前位置:   article > 正文

C++ winpcap网络抓包代码实现,以及抓包内容解析。_c++ winpcap网络抓包代码实现,以及抓包内容解析。_mr丶h(e)的博客-csdn博客

c++ winpcap网络抓包代码实现,以及抓包内容解析。_mr丶h(e)的博客-csdn博客

c++实现抓包代码
1.安装winpcap(windows packet capture)是windows平台下一个免费,公共的网络访问系统。开发winpcap这个项目的目的在于为win32应用程序提供访问网络底层的能力。它用于windows系统下的直接的网络编程。Winpcap提供了一个强大的编程接口,它很容易地在各个操作系统之间进行移植。

百度网盘:
链接:https://pan.baidu.com/s/1ot7H3Vz_KBvdmYKjJqu4dQ
提取码:1hzP

阿里网盘
https://www.aliyundrive.com/s/huPxENSACgH

WpdPack_4_1_2.exe安装运行环境
pcap.h在WpdPack_4_1_2文件夹中。路径:WpdPack_4_1_2\WpdPack\Include

以下为实现代码

头文件:
一定要先define HAVA_REMOTE 再include “wpcap.h” 否则会报错

#define HAVE_REMOTE
#include <pcap.h>
  • 1
  • 2

获取本机网卡设备列表:
原型:int pcap_findalldevs_ex(char* source, struct pcap_rmtauth auth, pcap_if_t* alldevs, char* errbuf );
返回值:0表示查找成功。-1表示查找失败
参数说明:

source:
指定是本地适配器或者远程适配器
本地适配器:‘rpcap://’
远程适配器:‘rpcap://host:port’
抓包文件。‘file://c:/myfolder/’.

Defined:
#define PCAP_SRC_FILE_STRING "file://"
String that will be used to determine the type of source in use (file, remote/local interface).
#define PCAP_SRC_IF_STRING "rpcap://"
String that will be used to determine the type of source in use (file, remote/local interface).
auth:一个指向’struct pcap_rmtauth’的指针,保存当一个用户登录到某个远程机器上时的必要信息。假如不是远程抓包,该指针被设置为NULL。
alldevs:参数用于存放获取的适配器数据。如果查找失败,alldevs的值为NULL.
errbuf:参数存放查找失败的信息。

    pcap_if_t* alldevs;
    char errbuf[PCAP_ERRBUF_SIZE];
    /* 获取本机设备列表 */
    if (pcap_findalldevs_ex((char*)PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
    {
        fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
        exit(1);
    }
    /* 释放设备列表 */
    pcap_freealldevs(alldevs);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

打开一个通用源,以便捕获/发送(仅限WinPcap)流量。
原型:pcap_t* pcap_open (
const char * source,
int snaplen,
int flags,
int read_timeout,
struct pcap_rmtauth * auth,
char * errbuf
)
source:以/0终止的字符串,其中包含要打开的源名称。为了使源语法更容易,请记住:
1.pcap_findalldevs_ex()返回的适配器可以由pcap_open()立即使用,
2.如果用户想要将自己的源字符串传递给pcap_open(),则pcap_createsrcstr()有助于创建正确的源标识符。

snaplen: 必须保留的包的长度。对于由过滤器接收的每个数据包,只有第一个“snaplen”字节存储在缓冲区中并传递给用户应用程序。例如,snaplen等于100表示只存储每个数据包的前100个字节。

flags: 保留捕获数据包可能需要的几个标志。允许的标志在pcap_open()标志中定义。

read_timeout: 以毫秒为单位读取超时。读取超时用于安排在看到数据包时读取不一定立即返回,但是在一次操作中,它会等待一段时间来允许更多数据包到达并从操作系统内核读取多个数据包。并非所有平台都支持读取超时; 在没有的平台上,读取超时被忽略。

auth: 指向“结构pcap_rmtauth”的指针,它保留在远程机器上验证用户所需的信息。如果这不是远程捕获,则该指针可以设置为NULL。

errbuf: 指向用户分配的缓冲区的指针,该缓冲区将在该函数失败的情况下包含错误。

返回值:
指向’pcap_t’的指针,可以用作以下调用(pcap_compile()等)的参数,并指定打开的WinPcap会话。如果出现问题,它返回NULL,'errbuf’变量保留错误消息。

    /* 打开设备 */
    if ((adhandle = pcap_open(d->name,          // 设备名
        65536,            // 要捕捉的数据包的部分
                          // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
        PCAP_OPENFLAG_PROMISCUOUS,    // 混杂模式
        1000,             // 读取超时时间
        NULL,             // 远程机器验证
        errbuf            // 错误缓冲池
    )) == NULL)
        {
        fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
        /* 释放设列表 */
        pcap_freealldevs(alldevs);
        return;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

设置过滤器:
过滤器语法详看 http://www.doc88.com/p-8466091442168.html

    char packet_filter[] = "src host 172.16.21.174 and port 8080"; //过滤(源IP:172.16.21.174且端口8080)

    if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) >= 0)
    {
        //设置过滤器
        if (pcap_setfilter(adhandle, &fcode) < 0)
        {
            fprintf(stderr, "\nError setting the filter.\n");
            /* 释放设备列表 */
            pcap_freealldevs(alldevs);
            return -1;
        }
    }
    else
    {
        fprintf(stderr, "\nError setting the filter.\n");
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

捕获报文:
原型:pcap_next_ex(pcap_t* p,struct pcap_pkthdr** pkt_header,const u_char** pkt_data)
功能: 从interface或离线记录文件获取一个报文

参数: p: 已打开的捕捉实例的描述符
pkt_header: 报文头
pkt_data: 报文内容
返回值: 1: 成功
0: 获取报文超时
-1: 发生错误
-2: 获取到离线记录文件的最后一个报文

其中:
struct pcap_pkthdr
{
struct timeval ts; ts是一个结构struct timeval,它有两个部分,第一部分是1900开始以来的秒数,第二部分是当前秒之后的毫秒数
bpf_u_int32 caplen; 表示抓到的数据长度
bpf_u_int32 len; 表示数据包的实际长度
}

/* 获取数据包 */
while ((res = pcap_next_ex(fp, &header, &pkt_data)) >= 0)
    {
        //超时
        if (res == 0)
            continue;
        //处理报文
     }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

示例代码:

#define HAVE_REMOTE
#include <pcap.h>

using namespace std;

int main(int argc, const char* argv[])
{
    pcap_if_t* alldevs;
    pcap_if_t* d;
    int inum;
    int i = 0;
    pcap_t* adhandle;
    int res;
    char errbuf[PCAP_ERRBUF_SIZE];
    struct tm* ltime;
    char timestr[16];
    struct pcap_pkthdr* header;
    const u_char* pkt_data;
    time_t local_tv_sec;
    u_int netmask;
    char packet_filter[] = "src host 192.168.9.101 and port 8080"; //过滤器
    struct bpf_program fcode;

    /* 获取本机设备列表 */
    if (pcap_findalldevs_ex((char*)PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
    {
        fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
        exit(1);
    }
    /* 打印列表 */
    for (d = alldevs; d; d = d->next)
    {
        printf("%d. %s", ++i, d->name);
        if (d->description)
            printf(" (%s)\n", d->description);
        else
            printf(" (No description available)\n");
    }
    if (i == 0)
    {
        printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
        return -1;
    }

    printf("Enter the interface number (1-%d):", i);
    scanf("%d", &inum);
    if (inum < 1 || inum > i)
    {
        printf("\nInterface number out of range.\n");
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }

    /* 跳转到已选中的适配器 */
    for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++);

    /* 打开设备 */
    if ((adhandle = pcap_open(d->name,          // 设备名
        65536,            // 要捕捉的数据包的部分
                          // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
        PCAP_OPENFLAG_PROMISCUOUS,    // 混杂模式
        1000,             // 读取超时时间
        NULL,             // 远程机器验证
        errbuf            // 错误缓冲池
    )) == NULL)
    {
        fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
        /* 释放设列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }
    printf("\nlistening on %s...\n", d->description);

    /* 设置过滤器 */
    if (d->addresses != NULL)
        /* 获取接口第一个地址的掩码 */
        netmask = ((struct sockaddr_in*)(d->addresses->netmask))->sin_addr.S_un.S_addr;
    else
        /* 如果这个接口没有地址,那么我们假设这个接口在C类网络中 */
        netmask = 0xffffff;
        if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) >= 0)
    {
        //设置过滤器
        if (pcap_setfilter(adhandle, &fcode) < 0)
        {
            fprintf(stderr, "\nError setting the filter.\n");
            /* 释放设备列表 */
            pcap_freealldevs(alldevs);
            return -1;
        }
    }
    else
    {
        fprintf(stderr, "\nError setting the filter.\n");
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }
    /* 释放设备列表 */
    pcap_freealldevs(alldevs);

    /* 获取数据包 */
    while ((res = pcap_next_ex(adhandle, &header, &pkt_data)) >= 0) {

        if (res == 0)
            /* 超时时间到 */
            continue;

        /* 将时间戳转换成可识别的格式 */
        local_tv_sec = header->ts.tv_sec;
        ltime = localtime(&local_tv_sec);
        strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);

        printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
        /* 打印包 */
        int len = header->caplen + 1;
        for (i = 1; i < len; i++)
        {
            printf("%.2x ", pkt_data[i - 1]);
            if ((i % 16) == 0)
                printf("\n");
        }
        printf("\n-----------------------------------------------------------------\n");
    }
    if (res == -1) {
        printf("Error reading the packets: %s\n", pcap_geterr(adhandle));
        return -1;
    }
    return 0;
  • 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
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130

编译运行,选择网卡编号后,捕获符合报文。
在这里插入图片描述截取其中一段做分析
9c 29 76 0e 4a 57 ac b5 7d 8a 12 5c 08 00 45 00
00 2a 72 3d 40 00 80 06 f4 73 c0 a8 09 65 c0 a8
09 67 38 b6 1f 90 ed 4a d5 6a 7c d7 7d 3d 50 18
40 29 95 41 00 00 31 32 33 34 35 36 37

报文信息字节长度解析
9c 29 76 0e 4a 575本机mac地址9C-29-76-0E-4A-57
ac b5 7d 8a 12 5c5它机mac地址AC-B5-7D-8A-12-5C
08 002IPV4(0x0800)
4510100(version:4)0101(header Len:20bytes(5))
001显式拥塞通知:不支持ECN传输(0)
00 2a2Total Length:42
72 3d2标识:0x723d(29245)
40 002标志:0x40
801生存时间:128
061协议:TCP(6)
f4 732标头校验和:0xf473
c0 a8 09 654源地址:192.168.9.101
c0 a8 09 674目的地地址:192.168.9.103
38 b62源端口:14518
1f 902目标端口:8080
ed 4a d5 6a4序列号(原始):3981104490
7c d7 7d 3d4确认号(原始):2094497085
50 182标志:0x018(PSH,ACK)
40 292Window:16425
95 412校验和:0x9541[未验证]
00 002紧急指针:0
31 32 33 34 35 36 377TCP段数据(ascii内容:1234567)
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/162513
推荐阅读
相关标签
  

闽ICP备14008679号