首页 > 在Ubuntu 14.04 64位上使用libpcap进行抓包和解包

在Ubuntu 14.04 64位上使用libpcap进行抓包和解包

为了开发需要,我决定使用最新libpcap源码包安装。在Unix环境下安装libpcap库,需要

c编译器,flex,bison等,安装Ubuntu系统时,没有这些包。安装flex需要m4编译环境,否则会提示“GNU M4 is required”错误。





1.安装系统依赖包

sudo apt-get install gcc libc6-dev

sudo apt-get install m4 

sudo apt-get install flex bison





2.下载libpcap源码并安装

从官网http://www.tcpdump.org/下载最新的libpcap版本

cd /usr/local/src

wget http://www.tcpdump.org/release/libpcap-1.5.3.tar.gz

tar zxvf libpcap-1.5.3.tar.gz

cd libpcap-1.5.3

./configure

make

sudo make install





3.安装开发需要用到的依赖库

sudo apt-get install libpcap-dev





4.测试libpcap的小程序,命名为pcap_demo.c,以检验环境是否配置正确

#include

#include





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 80"; /* 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 ", 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 ", 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 ", 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 ", filter_exp, pcap_geterr(handle));

return(2);

}

if (pcap_setfilter(handle, &fp) == -1) {

fprintf(stderr, "Couldn't install filter %s: %s ", 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] ", header.len);

/* And close the session */

pcap_close(handle);

return(0);

}

开始编译:

    gcc -g pcap_demo.c -o pcap_demo -lpcap  

开始执行

    ./pcap_demo





5.注意的问题

5.1.注意使用root用户来执行,或者对普通用户使用sudo来提升权限

    sudo pcap_demo

5.2.对一些PCAP API函数要有全面地理解,并时刻更新文档,比如pcap_loop这个函数,下面是官网的man page地址

http://www.tcpdump.org/manpages/pcap.3pcap.html

5.3,对一些函数的理解:

    pcap_loop和pcap_dispatch的区别,前者不会超时返回,而是会尽量去读取更多的包,直至发生一个错误才返回。正常返回的值是0,否则就是负值。我目前需要连续不断地抓包,所以应该使用pcap_loop。pcap_dispatch内部会调用pcap_loop。pcap_loop中的第四个参数在某些应用中很有用,但是在通常情况下被设为NULL。这种定义很有用,比如假如你想给pcap_loop的回调函数再额外传递一些参数,你可以使用u_char类型的指针(字符串),传人到回调函数中再做具体地处理。pcap_dispatch只会处理它收到的第一批包。

从pcap_loop返回后,我们应该显式调用pcap_close来关闭pcap以便释放资源。为了表示连续不间断的抓包,在pcap_loop中要尽量使用-1而不是0.关于其中回调函数的说明,里面有3个参数。第一个参数user是pcap_loop中最后一个参数,它表示某个不太重要的信息,可以忽略,第二个参数是包头信息,含有包的时间戳和长度,第三个参数是包的数据,从链路层头开始,它在该回调函数中并不会被释放,但是当该回调返回时不包装这些数据再合法,所以要再次使用它们,请事先复制出来。



下面再深入研究一下pcap_loop这个回调函数的定义:

首先注意函数的返回值是void类型,这完全符合逻辑,因为pcap_loop根本不知道如何处理回调函数的返回值,就是给他传出返回值也没有意义。然后就是回调函数的第一个参数对应pcap_loop中的最后一个参数,不管你给pcap_loop的最后一个参数传递什么值,当回调函数被调用时,它都会作为第一个参数传递给回调函数,第二个参数是pcap头,它包含一些信息:抓包时间,包多大,等等,这个结构在pcap.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) */

};



抓包方式有两种,一种是每次抓一个包,另一种是循环抓包,分为循环抓有限个数的包,或者是无限循环抓包。其中每次抓一个包使用pcap_next函数。pcap_next()表示只抓取一个数据包,它的第一个参数是session handle,第二个参数是包头,返回值是包数据。函数原型如下

u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

另一种循环抓包的方法,就是使用pcap_loop和回调函数,代码样例参见我整理出的两个例子。





5.4.网卡设备的设置有两种方法,第一种方法是用户直接指定网卡名称(必须要真实可用),从命令行参数传递进去,比如

#include

#include





int main(int argc, char *argv[])

{

char *dev = argv[1];





printf("Device: %s ", dev);

return(0);

}

另一种方法,是让pcap自己去探测网卡设备,如果出错,会给出出错信息,但是这种方法很多情况下不靠谱,对此的改进是,让pcap探测出所有的网卡设备,让用户区选择使用那个网络设备抓包,参见下面的代码

#include

#include





int main(int argc, char *argv[])

{

char *dev, errbuf[PCAP_ERRBUF_SIZE];





dev = pcap_lookupdev(errbuf);

if (dev == NULL) {

fprintf(stderr, "Couldn't find default device: %s ", errbuf);

return(2);

}

printf("Device: %s ", dev);

return(0);

}





5.5.对我们抓到的数据包,它的结构,大体分为Ethernet header,IP header,TCP header等等,分别对应14,20,20个字节,IP header至少20个字节,TCP头也至少20个字节。

data=recv_data[42:]这样做的原因是,recv_data 抓到的都是raw packet, tcp/ip是分了5层,一个udp包,会带有 14Bytes的Ethenet_II Frame的头,然后是20个字节的ip包头,而udp包头有8个字节,所以偏移量42之后的才是udp的实际内容

tcp包的包头大小为20Bytes,所以其实际内容在偏移量54之后





5.6.对数据包的解包和组包,需要用到Ethernet header,IP header,TCP header,UDP header等常用数据结构的定义,我们并不需要重新定义这些头,直接引用相关的头文件定义就可以了。

#include

#include

#include

#include

#include

#include  



5.6.对我们目前的程序,通常需要从命令行传人如下参数信息:

  wlan0        待抓包的网卡名称

  tcp port 80  过滤表达式

  10000 or -1  抓包模式,是抓指定个数的包(这里是10000个包),还是无限抓包下去

当然,对这些参数,你也可以写死在程序里。



参考文献

[1].http://www.tcpdump.org/pcap.html

更多相关:

  •       由前两节的铺垫,我们已经知道如何获取本地适配器的信息了,接下来我们来学习一下如何打开适配器并捕获数据包,听起来相当诱惑,那么咱们立刻进入主题吧!在贴源码之前先介绍一个将要用到的很重要的函数--pcap_open(),下面是pcap_open()在remote-ex.h中的声明: pcap_t *pcap_open(con...

  • 情况一:后台给的日期是Sat Jul 31 2021 21:50:01 GMT+0800 (中国标准时间),如果直接呈现给用户,他们一定会吐槽你不说人话~~~ 情况二:后台给的百分数是小数没有转化成00%格式 采用vue的过滤机制就可以解决这种情况,有两种方式: 第一种:全局写法,在main.js里面加入 // 【...

  • 问题描述 使用main函数的参数,实现一个整数计算器,程序可以接受三个参数,第一个参数“-a”选项执行加法,“-s”选项执行减法,“-m”选项执行乘法,“-d”选项执行除法,后面两个参数为操作数。 例如:输入test.exe  -a 1 2       执行1+2输出3 问题分析 上面的逻辑思维很简单,但是问题在于如何在VS中向...

  • ------------------------siwuxie095                         MyBatis 中 #{} 和 ${} 的区别       1、在 MyBatis 的映射配置文件中,动态传递参数有两种方式:    (1)#{} 占位符    (2)${} 拼接符          2、#{} 和...

  •     #2.6 map()# 第一个参数传入一个函数,,第二个参数为一个可迭代对象li_1 = (1,3,5,7)def funcA(x): return x*xm1 = map(funcA,li_1)print(type(m1))print(m1())# 2.6 reduce()# 第一个参数传入一个函数,第二个参数 可以迭...

  • 列表,元组,字典的转换。 list列表是一组可变的元素集合 列表是'[]'括号组成的,[]括号包含所有元素,列表的创建可以传递字符串,也可以传递多个字符串来创建列表。如"asd", / "a","b" ... tuple元组的创建和列表一致,区别在于 元组是以'()'创建的,并且元组数据不可变。 dict字典不同于列表和元组,他...

  • 草色新雨中, 松声晚窗里。之前我们学习 Power Query 都是用鼠标就完成了很多复杂的操作。虽然 PowerQuery 已经将大部分常用功能内置成到功能区。基本能完成我们大部分的报表自动化功能。但是总有些复杂的或者个性化的问题是开发团队没有预先想到的,这时我们就需要学习 M 语言。一、M 语言在哪里?M语言的函数公式有三个地...

  • 前言从2020年3月份开始,计划写一系列文档--《小白从零开始学编程》,记录自己从0开始学习的一些东西。第一个系列:python,计划从安装、环境搭建、基本语法、到利用Django和Flask两个当前最热的web框架完成一个小的项目第二个系列:可能会选择Go语言,也可能会选择Vue.js。具体情况待定,拭目以待吧。。。基本概念表达式表...

  • 1.1函数1.1.1什么是函数函数就是程序实现模块化的基本单元,一般实现某一功能的集合。函数名:就相当于是程序代码集合的名称参数:就是函数运算时需要参与运算的值被称作为参数函数体:程序的某个功能,进行一系列的逻辑运算return 返回值:函数的返回值能表示函数的运行结果或运行状态。1.1.2函数的作用函数是组织好的,可重复使用的,用来...

  • 原标题:基于Python建立深度神经网络!你学会了嘛?图1 神经网络构造的例子(符号说明:上标[l]表示与第l层;上标(i)表示第i个例子;下标i表示矢量第i项)单层神经网络图2 单层神经网络示例神经元模型是先计算一个线性函数(z=Wx+b),接着再计算一个激活函数。一般来说,神经元模型的输出值是a=g(Wx+b),其中g是激活函数(...

  • 在学习MySQL的时候你会发现,它有非常多的函数,在学习的时候没有侧重。小编刚开始学习的时候也会有这个感觉。不过,经过一段时间的学习之后,小编发现尽管函数有很多,但是常用的却只有那几个。今天小编就把常用的函数汇总一下,为大家能够能好的学习MySQL中的函数。MySQL常使用的函数大概有四类。时间函数、数学函数、字符函数、控制函数。让我...