用pcap编程抓包

Programming with pcap

流程

  1. 决定要嗅探的接口
  2. 初始化pcap,告诉pcap需要嗅探的接口,创建会话
  3. 定义嗅探的一些规则
  4. 让pcap进入执行循环
  5. 关闭嗅探会话

设置接口

通过设置参数执行参数指定需要嗅探的接口

#include <stdio.h>
#include <pcap.h>
int main(int argc, char *argv[])
{
	 char *dev = argv[1];//将命令中指定接口的参数赋值给dev
	 printf("Device: %s\n", dev);
	 return(0);
}

通过pcap自动检测出接口

#include <stdio.h>
#include <pcap.h>
int main(int argc, char *argv[])
{
	//dev用来存储接口
    //errbuf用来存放错误信息
    char *dev, errbuf[PCAP_ERRBUF_SIZE];
	
    //通过pcap_lookupdev函数检测接口
    //错误信息存放到errbuf中
    dev = pcap_lookupdev(errbuf);
	if (dev == NULL) {
		fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
		return(2);
	}
	printf("Device: %s\n", dev);
	return(0);
}

设置会话(session handler)

新建一个会话session handler

pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms,char *ebuf)
参数
  • device指定接口
  • snaplen指定可以被pcap捕获的最大的字节数量
  • promisc为真(1)就会进入混杂模式(promiscuous mode)
    • 混杂模式就是接收所有经过网卡的数据包,包括不是发给本机的包,即不验证MAC地址。
    • 普通模式下网卡只接收发给本机的包(包括广播包)传递给上层程序,其它的包一律丢弃。
  • to_ms指定超时时间,单位是毫秒ms,0表示没有超时时间
  • ebuf用来存储错误信息
返回值

这个函数返回session handler(就是一个会话)

举例子
#include <pcap.h>

pcap_t *handle;//一个会话

handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if (handle == NULL) {
    fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
    return(2);
}

检测链路层的类型

不同的设备可能支持不同的链路层类型,有时我们需要检测设备支持的类型我们,这时我们就能够使用pcap_datalink函数来获取设备的类型。

if (pcap_datalink(handle) != DLT_EN10MB) {
    fprintf(stderr, "Device %s doesn't provide Ethernet headers - not supported\n", dev);
	return(2);
}

pcap_datalink返回一个特定的志来代表链路层的类型,具体到这里看;
这里用以太网为例:

链路类型数值对应的名称描述
LINKTYPE_ETHERNET1DLT_EN10MBIEEE 802.3 Ethernet (10Mb, 100Mb, 1000Mb, and up); the 10MB in the DLT_ name is historical.

流量过滤

pcap设置的机制是:

  1. 将过滤规则编译成一个过滤器filter
  2. 将过滤器filter应用到会话session handler上

编译过滤器

int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
参数
  • p,指定会话
  • fp,存储编译完成的过滤器(filter)
  • str,指定表达式(过滤规则)
  • optimize,用来指定表达式是否需要被‘optimized’
  • netmask,指定应用过滤器的网络的子网掩码
返回值
  • -1,表示失败
  • 其他,表示成功

将过滤器应用到相应的会话上

int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
参数
  • p,指定会话
  • fp,指定过滤器filter
返回值
  • -1,表示失败
  • 其他,表示成功

探寻一个ipv4的地址和子网掩码

使用pcap_lookupnet函数探寻一个ipv4的地址和子网掩码,这一步是必须的,因为过滤器编译的时候需要子网掩码

if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
    fprintf(stderr, "Can't get netmask for device %s\n", dev);
    net = 0;
    mask = 0;
}
参数
  • dev,指定设备名
  • net,返回一个ip地址
  • mask,返回对应的子网掩码
  • errbuf,用来存储错误信息
返回值
  • -1,表示失败
  • 其他,表示成功

举个例子

下面的例子是在设备rl0上用混杂模式嗅探23端口

#include <pcap.h>

pcap_t *handle;		/* Session handle */
char dev[] = "rl0";		/* Device to sniff on */
char errbuf[PCAP_ERRBUF_SIZE];	/* Error string */
struct bpf_program fp;		/* The compiled filter expression */
char filter_exp[] = "port 23";	/* The filter expression */
bpf_u_int32 mask;		/* The netmask of our sniffing device */
bpf_u_int32 net;		/* The IP of our sniffing device */

if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
    fprintf(stderr, "Can't get netmask for device %s\n", dev);
    net = 0;
    mask = 0;
}

handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if (handle == NULL) {
    fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
	return(2);
}
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
    fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
    return(2);
}
if (pcap_setfilter(handle, &fp) == -1) {	 
    fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_gete(handle));	 
    return(2);
}

抓包

一次抓一个包

u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
参数
  • p,指定会话
  • h,存放捕获的包的信息,包括在线抓包的长度和离线包的长度,结构体代码如下:
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) 离线包的长度*/
};
/*
这里解释一下离线包
离线包 是将包括 HTML、Javascript、CSS 等页面内静态资源打包到一个压缩包内。您预先下载该离线包到本地,然后通过客户端打开,直接从本地加载离线包,从而最大程度地摆脱网络环境对 H5 页面的影响。
*/
返回

返回一个u_char指针指向所捕获的包

举例
#include <pcap.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    pcap_t *handle;                /* Session handle */
    char *dev;                     /* The device to sniff on */
    char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */
    struct bpf_program fp;         /* The compiled filter */
    char filter_exp[] = "port 23"; /* The filter expression */
    bpf_u_int32 mask;              /* Our netmask */
    bpf_u_int32 net;               /* Our IP */
    struct pcap_pkthdr header;     /* The header that pcap gives us */
    const u_char *packet;          /* The actual packet */

    /* Define the device */
    dev = pcap_lookupdev(errbuf);
    if (dev == NULL)
    {
        fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
        return (2);
    }
    /* Find the properties for the device */
    if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1)
    {
        fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
        net = 0;
        mask = 0;
    }
    /* Open the session in promiscuous mode */
    handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
    if (handle == NULL)
    {
        fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
        return (2);
    }
    /* Compile and apply the filter */
    if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1)
    {
        fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
        return (2);
    }
    if (pcap_setfilter(handle, &fp) == -1)
    {
        fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
        return (2);
    }
    /* Grab a packet */
    packet = pcap_next(handle, &header);
    /* Print its length */
    printf("Jacked a packet with length of [%d]\n", header.len);
    /* And close the session */
    pcap_close(handle);
    return (0);
}

循环抓n个包

pcap用pcap_loop和pcap_dispatch两个函数来进行循环抓包,这两个函数在抓到包时会启动指定的回掉函数(callback function)进行处理。

pcap_loop
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
参数
  • p,指定会话
  • cnt,指定需要抓包的数量
  • callback,指定回掉函数,可以是NULL
  • user,用来存放抓的包
callback函数的形式

如下就是一个回掉函数callback function:

void got_packet(u_char *args, const struct pcap_pkthdr *header,const u_char *packet);

callback function对应参数:

  • args,对应pcap_loop的最后一个参数,pcap_loop的将最后一个参数user传给它
  • 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) */
};
  • packet,它指向整个包的头一个字节(用这个指针可以轻松的找到一个包协议对应的相应的内容)
用pcap_loop实现对tcp/ip包的抓取

点击这里下载实例文件sniff.c

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值