1、前言

    linux提供了原始套接字RAW_SOCKET,可以抓取数据链路层的报文。这样可以对报文进行深入分析。今天介绍一下AF_PACKET的用法,分为两种方式。第一种方法是通过套接字,打开指定的网卡,然后使用recvmsg读取,实际过程需要需要将报文从内核区拷贝到用户区。第二种方法是使用packet_mmap,使用共享内存方式,在内核空间中分配一块内核缓冲区,然后用户空间程序调用mmap映射到用户空间。将接收到的skb拷贝到那块内核缓冲区中,这样用户空间的程序就可以直接读到捕获的数据包了。PACKET_MMAP减少了系统调用,不用recvmsg就可以读取到捕获的报文,相比原始套接字+recvfrom的方式,减少了一次拷贝和一次系统调用。libpcap就是采用第二种方式。suricata默认方式也是使用packet mmap抓包。

2、测试例子

  为了方便测试,可以使用linux提供的sock_filter过滤ip地址。使用tcpdump可以反汇编出来过滤的条件。以www.qq.com为例进行说明:

ping www.qq.com得到ip地址:101.226.103.106

tcpdump ip -s  -d host 101.226.103.106
() ldh []
() jeq #0x800 jt jf
() ld []
() jeq #0x65e2676a jt jf
() ld []
() jeq #0x65e2676a jt jf
() ret #
() ret #
 tcpdump -dd host 101.226.103.106
{ 0x28, , , 0x0000000c },
{ 0x15, , , 0x00000800 },
{ 0x20, , , 0x0000001a },
{ 0x15, , , 0x65e2676a },
{ 0x20, , , 0x0000001e },
{ 0x15, , , 0x65e2676a },
{ 0x15, , , 0x00000806 },
{ 0x15, , , 0x00008035 },
{ 0x20, , , 0x0000001c },
{ 0x15, , , 0x65e2676a },
{ 0x20, , , 0x00000026 },
{ 0x15, , , 0x65e2676a },
{ 0x6, , , 0x0000ffff },
{ 0x6, , , 0x00000000 }

更新详细过滤细节可以参考: http://blog.csdn.net/eqiang8271/article/details/8489769

(1)第一种方法:

 #include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/types.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <net/if.h>
#include <stdio.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/filter.h>
#include <stdlib.h> #define ETH_HDR_LEN 14
#define IP_HDR_LEN 20
#define UDP_HDR_LEN 8
#define TCP_HDR_LEN 20 static int sock; void sig_handler(int sig)
{
struct ifreq ethreq;
if(sig == SIGTERM)
printf("SIGTERM recieved, exiting.../n");
else if(sig == SIGINT)
printf("SIGINT recieved, exiting.../n");
else if(sig == SIGQUIT)
printf("SIGQUIT recieved, exiting.../n");
// turn off the PROMISCOUS mode
strncpy(ethreq.ifr_name, "eth1", IFNAMSIZ);
if(ioctl(sock, SIOCGIFFLAGS, &ethreq) != -) {
ethreq.ifr_flags &= ~IFF_PROMISC;
ioctl(sock, SIOCSIFFLAGS, &ethreq);
}
close(sock);
exit();
} int main(int argc, char ** argv) {
int n;
char buf[];
unsigned char *ethhead;
unsigned char *iphead;
struct ifreq ethreq;
struct sigaction sighandle; #if 0
$tcpdump ip -s -d host 192.168.1.2
() ldh []
() jeq #0x800 jt jf
() ld []
() jeq #0xc0a80102 jt jf
() ld []
() jeq #0xc0a80102 jt jf
() ret #
() ret #
#endif #if 0
测试访问www.qq.com
ping www.qq.com 得到ip地址为:101.226.103.106
tcpdump -dd host 101.226.103.106
{ 0x28, , , 0x0000000c },
{ 0x15, , , 0x00000800 },
{ 0x20, , , 0x0000001a },
{ 0x15, , , 0x65e2676a },
{ 0x20, , , 0x0000001e },
{ 0x15, , , 0x65e2676a },
{ 0x15, , , 0x00000806 },
{ 0x15, , , 0x00008035 },
{ 0x20, , , 0x0000001c },
{ 0x15, , , 0x65e2676a },
{ 0x20, , , 0x00000026 },
{ 0x15, , , 0x65e2676a },
{ 0x6, , , 0x0000ffff },
{ 0x6, , , 0x00000000 },
#endif
struct sock_filter bpf_code[] = {
{ 0x28, , , 0x0000000c },
{ 0x15, , , 0x00000800 },
{ 0x20, , , 0x0000001a },
{ 0x15, , , 0x65e2676a },
{ 0x20, , , 0x00000026 },
{ 0x15, , , 0x65e2676a },
{ 0x6, , , 0x0000ffff },
{ 0x6, , , 0x00000000 }
}; struct sock_fprog filter;
filter.len = sizeof(bpf_code)/sizeof(bpf_code[]);
filter.filter = bpf_code; sighandle.sa_flags = ;
sighandle.sa_handler = sig_handler;
sigemptyset(&sighandle.sa_mask);
//sigaddset(&sighandle.sa_mask, SIGTERM);
//sigaddset(&sighandle.sa_mask, SIGINT);
//sigaddset(&sighandle.sa_mask, SIGQUIT);
sigaction(SIGTERM, &sighandle, NULL);
sigaction(SIGINT, &sighandle, NULL);
sigaction(SIGQUIT, &sighandle, NULL); // AF_PACKET allows application to read pecket from and write packet to network device
// SOCK_DGRAM the packet exclude ethernet header
// SOCK_RAW raw data from the device including ethernet header
// ETH_P_IP all IP packets
if((sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP))) == -) {
perror("socket");
exit();
} // set NIC to promiscous mode, so we can recieve all packets of the network
strncpy(ethreq.ifr_name, "eth1", IFNAMSIZ);
if(ioctl(sock, SIOCGIFFLAGS, &ethreq) == -) {
perror("ioctl");
close(sock);
exit();
} ethreq.ifr_flags |= IFF_PROMISC;
if(ioctl(sock, SIOCSIFFLAGS, &ethreq) == -) {
perror("ioctl");
close(sock);
exit();
} #if 1
// attach the bpf filter
if(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) == -) {
perror("setsockopt");
close(sock);
exit();
}
#endif while() {
n = recvfrom(sock, buf, sizeof(buf), , NULL, NULL);
if(n < (ETH_HDR_LEN+IP_HDR_LEN+UDP_HDR_LEN)) {
printf("invalid packet\n");
continue;
} printf("%d bytes recieved\n", n); ethhead = buf;
printf("Ethernet: MAC[%02X:%02X:%02X:%02X:%02X:%02X]", ethhead[], ethhead[], ethhead[],
ethhead[], ethhead[], ethhead[]);
printf("->[%02X:%02X:%02X:%02X:%02X:%02X]", ethhead[], ethhead[], ethhead[],
ethhead[], ethhead[], ethhead[]);
printf(" type[%04x]\n", (ntohs(ethhead[]|ethhead[]<<))); iphead = ethhead + ETH_HDR_LEN;
// header length as 32-bit
printf("IP: Version: %d HeaderLen: %d[%d]", (*iphead>>), (*iphead & 0x0f), (*iphead & 0x0f)*);
printf(" TotalLen %d", (iphead[]<<|iphead[]));
printf(" IP [%d.%d.%d.%d]", iphead[], iphead[], iphead[], iphead[]);
printf("->[%d.%d.%d.%d]", iphead[], iphead[], iphead[], iphead[]);
printf(" %d", iphead[]); if(iphead[] == IPPROTO_TCP)
printf("[TCP]");
else if(iphead[] == IPPROTO_UDP)
printf("[UDP]");
else if(iphead[] == IPPROTO_ICMP)
printf("[ICMP]");
else if(iphead[] == IPPROTO_IGMP)
printf("[IGMP]");
else if(iphead[] == IPPROTO_IGMP)
printf("[IGMP]");
else
printf("[OTHERS]"); printf(" PORT [%d]->[%d]\n", (iphead[]<<|iphead[]), (iphead[]<<|iphead[]));
}
close(sock);
exit();
}

(2)第二种方法:

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <sys/mman.h>
#include <poll.h>
#include <linux/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/filter.h>
#include <net/ethernet.h> #define ETH_HDR_LEN 14
void CallBackPacket(char *data)
{
unsigned char *ethhead;
unsigned char *iphead;
printf("Recv A Packet.\n");
ethhead = data;
printf("Ethernet: MAC[%02X:%02X:%02X:%02X:%02X:%02X]", ethhead[], ethhead[], ethhead[],
ethhead[], ethhead[], ethhead[]);
printf("->[%02X:%02X:%02X:%02X:%02X:%02X]", ethhead[], ethhead[], ethhead[],
ethhead[], ethhead[], ethhead[]);
printf(" type[%04x]\n", (ntohs(ethhead[]|ethhead[]<<))); iphead = ethhead + ETH_HDR_LEN;
// header length as 32-bit
printf("IP: Version: %d HeaderLen: %d[%d]", (*iphead>>), (*iphead & 0x0f), (*iphead & 0x0f)*);
printf(" TotalLen %d", (iphead[]<<|iphead[]));
printf(" IP [%d.%d.%d.%d]", iphead[], iphead[], iphead[], iphead[]);
printf("->[%d.%d.%d.%d]", iphead[], iphead[], iphead[], iphead[]);
printf(" %d", iphead[]);
} int main()
{
int fd = , ret = ;
char *buff = NULL; fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
//可以使用ARP进行一下测试
//fd = socket(PF_PACKET, SOCK_DGRAM, htons (ETH_P_ARP));
if(fd<)
{
perror("socket");
goto failed_2;
} //PACKET_VERSION和SO_BINDTODEVICE可以省略
#if 1
const int tpacket_version = TPACKET_V1;
/* set tpacket hdr version. */
ret = setsockopt(fd, SOL_PACKET, PACKET_VERSION, &tpacket_version, sizeof (int));
if(ret<)
{
perror("setsockopt");
goto failed_2;
} //#define NETDEV_NAME "wlan0"
#define NETDEV_NAME "eth1"
/* bind to device. */
ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NETDEV_NAME, sizeof (NETDEV_NAME));
if(ret<)
{
perror("setsockopt");
goto failed_2;
}
#endif struct tpacket_req req;
#define PER_PACKET_SIZE 2048
const int BUFFER_SIZE = **; //16MB的缓冲区
req.tp_block_size = ;
req.tp_block_nr = BUFFER_SIZE/req.tp_block_size;
req.tp_frame_size = PER_PACKET_SIZE;
req.tp_frame_nr = BUFFER_SIZE/req.tp_frame_size; ret = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *)&req, sizeof(req));
if(ret<)
{
perror("setsockopt");
goto failed_2;
} #if 1
struct sock_filter bpf_code[] = {
{ 0x28, , , 0x0000000c },
{ 0x15, , , 0x00000800 },
{ 0x20, , , 0x0000001a },
{ 0x15, , , 0x65e2676a },
{ 0x20, , , 0x00000026 },
{ 0x15, , , 0x65e2676a },
{ 0x6, , , 0x0000ffff },
{ 0x6, , , 0x00000000 }
};
struct sock_fprog filter;
filter.len = sizeof(bpf_code)/sizeof(bpf_code[]);
filter.filter = bpf_code;
// attach the bpf filter
if(setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) == -) {
perror("setsockopt");
close(fd);
goto failed_2;
}
#endif buff = (char *)mmap(, BUFFER_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, );
if(buff == MAP_FAILED)
{
perror("mmap");
goto failed_2;
} int nIndex=, i=;
while()
{
//这里在poll前先检查是否已经有报文被捕获了
struct tpacket_hdr* pHead = (struct tpacket_hdr*)(buff+ nIndex*PER_PACKET_SIZE);
//如果frame的状态已经为TP_STATUS_USER了,说明已经在poll前已经有一个数据包被捕获了,如果poll后不再有数据包被捕获,那么这个报文不会被处理,这就是所谓的竞争情况。
if(pHead->tp_status == TP_STATUS_USER)
goto process_packet; //poll检测报文捕获
struct pollfd pfd;
pfd.fd = fd;
//pfd.events = POLLIN|POLLRDNORM|POLLERR;
pfd.events = POLLIN;
pfd.revents = ;
ret = poll(&pfd, , -);
if(ret<)
{
perror("poll");
goto failed_1;
} process_packet:
//尽力的去处理环形缓冲区中的数据frame,直到没有数据frame了
for(i=; i < req.tp_frame_nr; i++)
{
struct tpacket_hdr* pHead = (struct tpacket_hdr*)(buff+ nIndex*PER_PACKET_SIZE); //XXX: 由于frame都在一个环形缓冲区中,因此如果下一个frame中没有数据了,后面的frame也就没有frame了
if(pHead->tp_status == TP_STATUS_KERNEL)
break; //处理数据frame
CallBackPacket((char*)pHead+pHead->tp_net); //重新设置frame的状态为TP_STATUS_KERNEL
pHead->tp_len = ;
pHead->tp_status = TP_STATUS_KERNEL; //更新环形缓冲区的索引,指向下一个frame
nIndex++;
nIndex %= req.tp_frame_nr;
} }
success:
close(fd);
munmap(buff, BUFFER_SIZE);
return ; failed_1:
munmap(buff, BUFFER_SIZE); failed_2:
close(fd);
return -;
}

3、测试结果

执行main程序,使用curl http://www.qq.com

suricata抓包方式之一 AF_PACKET的更多相关文章

  1. suricata抓包方式之一AF_PACKET

    suricata抓包方式之一AF_PACKET 噜拯渡 睦坚削 曜纡宄 式犒藿氆 咬焚桤φ 要蒯钮 喃俚夼 币噎嶂颐 话千叶舞就后悔了怎么想都容易让人引 虻谮м 及葚雏钏 看着表面平静实际 ...

  2. 常用 tcpdump 抓包方式

    目录 文章目录 目录 tcpdump 指令 关键字 常用指令选项 常规操作示例 过滤主机 过滤端口 过滤网络(网段) 过滤协议 复杂的逻辑表达式过滤条件 参考资料 tcpdump 指令 tcpdump ...

  3. 使用 Google Chrome 数据抓包方式免费下载收费音乐

    对于每个有音乐细胞的 boys & girls 来说,听音乐的时候是不是会经常遇到如下图的问题. 自从音乐进入正版之后,很多歌曲只有付费用户才可以下载.虽然可以在线听,可是以我的倔脾气,就是喜 ...

  4. 开源网络抓包与分析框架学习-Packetbeat篇

    开源简介packbeat是一个开源的实时网络抓包与分析框架,内置了很多常见的协议捕获及解析,如HTTP.MySQL.Redis等.在实际使用中,通常和Elasticsearch以及kibana联合使用 ...

  5. android 移动网络实时抓包

    2G.3G环境,那就必须root进去tcpdump 方式抓. 准备: 一.root CF-auto-root: http://autoroot.chainfire.eu/ 需要清理全部数据,注意备份 ...

  6. PHP中使用CURL请求页面,使用fiddler进行抓包

    在PHP中使用CURL访问页面: <?php $ch = curl_init('http://www.baidu.com'); curl_setopt($ch, CURLOPT_RETURNTR ...

  7. 浅析手机抓包方法实践(zt)

    原文:http://drops.wooyun.org/tips/12467 0x00 摘要 在移动逆向分析以及 App 开发的时候,总会需要对其网络行为进行监控测试,本文总结一些抓包思路,并对其使用方 ...

  8. 安卓APP测试之使用Burp Suite实现HTTPS抓包方法

    APP的测试重点小部分在APP本身,大部分还是在网络通信上(单机版除外).所以在安卓APP测试过程中,网络抓包非常重要,一般来说,app开发会采用HTTP协议.Websocket.socket协议,一 ...

  9. 手机数据抓包以及wireshark技巧

    本文主要讨论一种非常方便的抓取Android和iphone手机网络数据包的办法,以及介绍wireshark最常用的技巧 抓包工具介绍 (1).网页抓包工具 Chrome浏览器插件 FireBug 插件 ...

随机推荐

  1. Transistor 晶体管 场效应 双极型 达林顿 CMOS PMOS BJT FET

    Transistor Tutorial Summary Transistor Tutorial Summary Bipolar Junction Transistor Tutorial We can ...

  2. Docker实践(2)—虚拟网络

    1 docker(container)的虚拟网络 docker的虚拟网络结构: host创建一个虚拟bridge,每个container对应一个虚拟网络设备(TAP设备),与bridge一起构成一个虚 ...

  3. 分享自己写的一个小工具RGB转十六进制(高手勿喷)

    由于工作经常美工给的颜色是rgb,而我们网页里面是16进制.网上也有很多类型的工具.不过似乎都用浏览器打开.没网就不爽了 实现也很简单.代码已经共享了 http://git.oschina.net/w ...

  4. Andriod Studio 开发环境安装和配置

    Android Studio安装配置详细步骤(图文):http://www.2cto.com/kf/201604/500642.html第一次使用Android Studio时你应该知道的一切配置 : ...

  5. 高级屏幕空间反射: Screen Space Reflection (SSR)

    自从CE3首倡SSR以来,发展至今,其质量与当年早已不能同日而语.不仅强调超越性的质量,而且强调超越性的性能.乘着周末有空撸了撸,以下是增强型实时SSR结果图.与我原来的SSR原始实现相比,新的增强型 ...

  6. JMeter教程01-下载和安装

    下载JMeter http://jmeter.apache.org/download_jmeter.cgi 以上是下载地址 更多课程 访问www.bamn.cn 北盟网校 下载后解压 apache-j ...

  7. c10k问题及其解决方案

    本文主要讲述高并发http应用中的c10k瓶颈问题:在很多服务器初始状态下,无法服务1w左右的并发连接.这与每次服务的资源消耗.服务器的硬件配置固然有关,但很多时候是被linux的默认配置以及软件st ...

  8. 关于stacking context和CSS z-index的总结

    HTML中决定元素叠加顺序的CSS属性最有名的应该是z-index了.但是,往往在项目中发现有些情况和我们的预期不太一致.经过研究和学习,总算搞清楚了其中的关系.简单总结如下: 只有Positione ...

  9. 个人软件过程5 git命令行方式超简洁教程

    虽然许多IDE对git的支持不错,但用命令行方式,有助于对git本身的理解.这里对实际工作中,使用git的流程,以及与其相关的命令 小结一下,基本上,掌握这些命令,就能自如的在工作中使用. 1.git ...

  10. ubuntu tomcat 部署java web

    1,安装jdk apt-get install openjdk-7-jdk 2,下载tomcat.解压到对应的文件夹 3,将xxx.war放入到 tomcat下的  webapp目录下(此目录下不要有 ...