由于工作上的需要,最近简单学习了抓包函数库libpcap,顺便记下笔记,方便以后查看

一、libpcap简介
    libpcap(Packet Capture Library),即数据包捕获函数库,是Unix/Linux平台下的网络数据包捕获函数库。它是一个独立于系统的用户层包捕获的API接口,为底层网络监测提供了一个可移植的框架.
Libpcap可以在绝大多数类unix平台下工作,libpcap库安装也很简单,Libpcap 软件包可从 http://www.tcpdump.org/ 下载,然后依此执行下列三条命令即可安装
./configure
make
make install
二、pcap基本工作流程
(1)确定将要嗅探的接口,在linux下是类似eth0的东西。在BSD下是类似xll的东西。可以在一个字符串中声明设备,也可以让pcap提供备选接口(我们想要嗅探的接口)的名字。
(2)初始化pcap,此时才真正告诉pcap我们要嗅探的具体接口,只要我们愿意,我们可以嗅探多个接口。但是如何区分多个接口呢,使用文件句柄。就像读写文件时使用文件句柄一样。我们必须给嗅探任务命名,以至于区分不同的嗅探任务。
(3)指定过滤规则,当我们只想嗅探特殊的流量时(例如,仅仅嗅探TCP/IP包、仅仅嗅探经过端口80的包,等等)我们必须设定一个规则集,“编译”并应用它。这是一个三相的并且紧密联系的过程,规则集存储与字符串中,在“编译”之后会转换成pcap可以读取的格式。“编译过程”实际上是调用自定义的函数完成的,不涉及外部的函数。然后我们可以告诉pcap在我们想要过滤的任何任务上实施。
(4)抓包,最后,告诉pcap进入主要的执行循环中,在此阶段,在接收到任何我们想要的包之前pcap将一直循环等待。在每次抓取到一个新的数据包时,它将调用另一个自定义的函数,我们可以在这个函数中肆意妄为,例如,解析数据包并显示数据内容、保存到文件或者什么都不做等等。
 当嗅探完美任务完成时,记得关掉任务。

下面是pcap工作流程图(摘自官网)


下面我们看一下具体的步骤实施:
(1)确定我们将要嗅探的接口
这一步操作我们可以手动指定接口或者调用pcap库提供的接口来查找网络设备
手动指定:

 #include <stdio.h>
#include <string.h>
#include <stdlib.h> int main(int argc, char **argv)
{
char *dev = argv[]; printf("Device: %s\n", dev); return ;
}

使用pcap API查找网络设备

pcap_lookupdev()函数用于查找网络设备

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pcap.h> 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\n", errbuf);
return();
} printf("Device: %s\n", dev);
return();
}

编译时需要连接pcap库 -lpcap
(2)打开嗅探设备

pcap_open_live()函数用于打开网络设备,并且返回用于捕获网络数据包的数据包捕获描述字。对于此网络设备的操作都要基于此网络设备描述字。

函数原型如下:

     pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *errbuf);

 /*函数说明:该函数用于打开一个嗅探设备
参数:device 需要打开的设备
snaplen int型,表示pcap可以捕获的最大字节数(最大为65535)
promisc 是否开启混杂模式(1打开,0关闭),设置开启混杂模式,需要对应的网卡也开启混杂模式
to_ms 是读取时间溢出,单位为毫秒(ms), 0表示没有时间溢出
errbuf 保存错误的返回值
*/

下面是具体实现:

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pcap.h> int main(int argc, char *argv[])
{
char *dev, errbuf[PCAP_ERRBUF_SIZE];
pcap_t *handle; dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
return();
}
printf("Device: %s\n", dev); handle = pcap_open_live(dev, BUFSIZ, , , errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
return();
}
printf("Open Device success!\n"); return();
}

(3)过滤指定流量
      很多时候,我只需要我们指定的流量,比如我们需要劫持http请求(80端口),劫持DNS服务(53端口),因此,我们大多数时候都不会盲目的抓取全部的报文。
    相关过滤函数pcap_compile()and pcap_setfilter(),当我们调用pcap_open_live()后,我们会得到一个建立的嗅探会话,此时我们就可以开始过滤我们想要流量了;
    过滤器表达式是基于正则表达式来编写的,官网tcpdump有详细的规则说明,在使用过滤器之前我们必须”编译“.

函数原型如下:

     int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
/*函数说明:将str参数指定的字符串编译到过滤程序中。
参数: p是嗅探器回话句柄
fp是一个bpf_program结构的指针,在pcap_compile()函数中被赋值。o
ptimize参数控制结果代码的优化。
netmask参数指定本地网络的网络掩码。
*/

编译完过滤表达式后,我们就可以应用它了,下面是int pcap_setfilter(),具体用法看man手册:

     int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
//第一个参数嗅探器回话句柄,第二参数是存储过滤器编译版本的结构体指针(跟pcap_compile 一个参数一样)

下面是简单实例:

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pcap.h> int main(int argc, char *argv[])
{
char *dev, errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp; /* The compiled filter expression */
char filter_exp[] = "port 53"; /* The filter expression (filter 53 port)*/
pcap_t *handle;
bpf_u_int32 mask; /* The netmask of our sniffing device */
bpf_u_int32 net; /* The IP of our sniffing device */ dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
return();
}
printf("Device: %s\n", dev); /*get network mask*/
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -) {
fprintf(stderr, "Can't get netmask for device %s\n", dev);
net = ;
mask = ;
}
/*Open the session in promiscuous mode*/
handle = pcap_open_live(dev, BUFSIZ, , , errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
return();
}
/* Compile and apply the filter */
if (pcap_compile(handle, &fp, filter_exp, , net) == -) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return();
}
if (pcap_setfilter(handle, &fp) == -) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
return();
} return();
}

以上代码中的pcap_lookupnet()函数获得指定网络设备的网络号和掩码。函数原型如下:

 int pcap_lookupnet(const char *device, bpf_u_int32 *netp,
bpf_u_int32 *maskp, char *errbuf);
/* 参数:device 指定的嗅探设备名称
netp 指定设备的网络号
maskp 掩码
errbuf 保存错误信息
*/

下面是具体实例:

 #include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pcap.h> #define DEVICE "enp0s3" int main()
{
char errBuf[PCAP_ERRBUF_SIZE];
struct pcap_pkthdr packet;
pcap_t *dev;
bpf_u_int32 netp, maskp;
char *net, *mask;
struct in_addr addr;
int ret; if(pcap_lookupnet(DEVICE, &netp, &maskp, errBuf)) {
printf("get net failure\n");
return -;
}
addr.s_addr = netp;
net = inet_ntoa(addr);
printf("network: %s\n", net); addr.s_addr = maskp;
mask = inet_ntoa(addr);
printf("mask: %s\n", mask); return ;
}
//运行结果
[root@localhost pacp_1st]# ./pacp
network: 192.168.16.0
mask: 255.255.255.0

(4)进行抓包处理  

  通过以上内容,我们已经知道了如何指定获取以及初始化一个嗅探器设备,如何编译及使用过滤器;下面我们就开始进行抓包,抓包程序有抓一次包(pcap_next())和循环一直抓包几个函数;
下面我们我们先用pcap_next()进行一次抓包
函数原型:

     const 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) */
};
//ts——时间戳
//caplen——真正实际捕获的包的长度
//len——这个包的长度 因为在某些情况下你不能保证捕获的包是完整的,例如一个包长1480,但是你捕获到1000的时候,
可能因为某些原因就中止捕获了,所以caplen是记录实际捕获的包长,也就是1000,而len就是1480。

下面是使用pcap_next()抓包程序

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <pcap.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 53"; /* 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();
}
/* Find the properties for the device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -) {
fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
net = ;
mask = ;
}
/* Open the session in promiscuous mode */
handle = pcap_open_live(dev, BUFSIZ, , , errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
return();
}
/* Compile and apply the filter */
if (pcap_compile(handle, &fp, filter_exp, , net) == -) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return();
}
if (pcap_setfilter(handle, &fp) == -) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
return();
}
/* Grab a packet */
packet = pcap_next(handle, &header);
/* Print header info */
printf("Packet length: %d\n", header.len);
printf("Number of bytes: %ud\n", header.caplen);
printf("Recieved time: %s\n", ctime((const time_t *)&header.ts.tv_sec));
/* And close the session */
pcap_close(handle); return();
}
//运行结果
[root@localhost pacp_5th]# ./pacp
Packet length:
Number of bytes: 3372236960d
Recieved time: Sat Aug ::

上面的代码在promisc模式下嗅探所有由pcap_lookupdev()返回的设备。它发现第一个经过端口53(DNS)的数据包并打印包的相关信息。
在大多数情况下我们很少的嗅探器使用pcap_next(),更多的是使用pcap_loop()或者pcap_dispatch()(pcap_dispatch()内部调用pcap_next())
pcap_loop()及pcap_dispatch()的具体使用在下篇博客中介绍

参考:http://www.tcpdump.org/
libpcap 官方有很多关于libpcap的说明文档,讲的非常详细;

 

初识函数库libpcap的更多相关文章

  1. 菜鸟Python学习笔记第一天:关于一些函数库的使用

    2017年1月3日 星期二 大一学习一门新的计算机语言真的很难,有时候连函数拼写出错查错都能查半天,没办法,谁让我英语太渣. 关于计算机语言的学习我想还是从C语言学习开始为好,Python有很多语言的 ...

  2. ABP(现代ASP.NET样板开发框架)系列之21、ABP展现层——Javascript函数库

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之21.ABP展现层——Javascript函数库 ABP是“ASP.NET Boilerplate Project ...

  3. 重温JSP学习笔记--El函数库

    EL函数库(由JSTL提供的) * 导入标签库:<%@ tablib prefix="fn" uri="http://java.sun.com/jsp/jstl/f ...

  4. 为开发者准备的 Android 函数库(2016 年版)

    转载:http://www.androidchina.net/5922.html第三方函数库(译者注:包括第三方提供的 SDK,开源函数库)以惊人的方式助力着 Android 开发,借助这些其他开发人 ...

  5. 如何持续集成/交付一个开源.NET函数库到Nuget.org

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:这是一个简单的入门向导,涉及到GitHub.AppVeyor和Nuget.org. 最 ...

  6. ajax的使用:(ajaxReturn[ajax的返回方法]),(eval返回字符串);分页;第三方类(page.class.php)如何载入;自动加载函数库(functions);session如何防止跳过登录访问(构造函数说明)

    一.ajax例子:ajaxReturn("ok","eval")->thinkphp中ajax的返回值的方法,返回参数为ok,返回类型为eval(字符串) ...

  7. PHP PDO函数库详解

    PDO是一个“数据库访问抽象层”,作用是统一各种数据库的访问接口,与mysql和mysqli的函数库相比,PDO让跨数据库的使用更具有亲和力:与ADODB和MDB2相比,PDO更高效.目前而言,实现“ ...

  8. PHP用mb_string函数库处理与windows相关中文字符

    昨天想批处理以前下载的一堆文件,把文件里的关键内容用正则匹配出来,集中处理.在操作文件时遇到一个问题,就是windows操作系统中的编码问题. 我们都知道windows中(当然是中文版),文件名和文件 ...

  9. 【C++实现python字符串函数库】strip、lstrip、rstrip方法

    [C++实现python字符串函数库]strip.lstrip.rstrip方法 这三个方法用于删除字符串首尾处指定的字符,默认删除空白符(包括'\n', '\r', '\t', ' '). s.st ...

随机推荐

  1. Linux自动安装JDK的shell脚本

    Linux自动安装JDK的shell脚本 A:本脚本运行的机器,Linux B:待安装JDK的机器, Linux 首先在脚本运行的机器A上确定可以ssh无密码登录到待安装jdk的机器B上,然后就可以在 ...

  2. 关于Service中bindService注意的几个问题

    最近有用到Activity需要不断的从Service中获取数据,第一个想法肯定就是通过bind回调机制了,有几点概念模糊特此记录下: 单独使用bindService(),unbindService() ...

  3. iOS和OS X中的bundle

    bundle也可以称之为包(package). 它在iOS和OS X中实际为一个文件夹但却当成单独的文件来对待. 每一个app都有一个bundle,并且你可以通过在xxx.app图标上右击鼠标然后选择 ...

  4. 系统性能监测(使用nmon、nmonanalyser)

    系统性能监测使用工具: l系统性能监测使用的主要监测工具是:nmon(AIX6.1及以上版本系统自带). l系统性能监测使用的主要分析工具是:nmonanalyser. NMON工具简介: NMON工 ...

  5. UnityEditor下文件操作方法汇总(Unity3D开发之二十四)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/50595585 ...

  6. Android实训案例(一)——计算器的运算逻辑

    Android实训案例(一)--计算器的运算逻辑 应一个朋友的邀请,叫我写一个计算器,开始觉得,就一个计算器嘛,很简单的,但是写着写着发现自己写出来的逻辑真不严谨,于是搜索了一下,看到mk(没有打广告 ...

  7. MaterialDesign学习项目

    概述 该项目主要用来学习Material Design Support Library和一些android其他技术,也借鉴了网上一些其他优秀的学习资源.该项目目前主要分为俩大部分(后期可能会有一些增加 ...

  8. 关于ARC的介绍和ARC与MRC混编解决

    1. ARC & MRC 混合开发 在项目开发中,遇到使用MRC开发的第三方库怎么办? 例如:ASI 1> 尝试使用Xcode的转换工具(失败率比较高) 2> 在编译选项中,为MR ...

  9. OpenLayers3的轨迹回放

    OpenLayers3实现轨迹回放需要动画操作,官网上的例子用的是postcompose,但是还可以使用javascript中setInterval和setTime. 我的例子是按官网上来的http: ...

  10. Ubuntu18.04教程

    pre.ctl { font-family: "Liberation Mono", monospace } h1 { margin-bottom: 0.21cm } h1.west ...