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. DataGridView很详细的用法(转载)

    一.DataGridView 取得或者修改当前单元格的内容: 当前单元格指的是 DataGridView 焦点所在的单元格,它可以通过 DataGridView 对象的 CurrentCell 属性取 ...

  2. MySQL实现定时任务

    如果要每30秒执行以下语句 '; 可以给MySQL建个定时任务,具体如下: delimiter // /* 设定语句终结符为 //,因存储过程语句用;结束 */ 一.查看event是否开启 show ...

  3. nodejs+express+jade安装备忘

    安装步骤 1.安装nodejs,比如安装在E:\nodejs. 确保有两个环境变量 用户环境变量:C:\Users\Administrator\AppData\Roaming\npm 系统环境变量:e ...

  4. mac os x用macport安装redis

    一.Redis简要介绍 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的 ...

  5. clearfix清除浮动进化史

    我想大家在写CSS的时候应该都对清除浮动的用法深有体会,今天我们就还讨论下clearfix的进化史吧. clearfix清除浮动 首先在很多很多年以前我们常用的清除浮动是这样的. .clear{cle ...

  6. Scala深入浅出实战经典之 List的foldLeft、foldRight、sort操作代码实战

     Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 腾讯微云:http://url.cn/TnGbdC 3 ...

  7. shell脚本编写方法

    shell脚本编写就如同一门语言,涉及到运行环境.基本语法.变量定义.函数.参数(系统参数).条件判定.执行流程控制 等等问题. 本文就以下几个方面进行描述: 运行环境: shell  shebang ...

  8. .NET Actor Model Implementations Differ in Approach

    Last week Vaughn Vernon, author of Implementing Domain-Driven Design, published Dotsero, a .NET Acto ...

  9. [CoreOS 转载] CoreOS实践指南(七):Docker容器管理服务

    转载:http://www.csdn.net/article/2015-02-11/2823925 摘要:当Docker还名不见经传的时候,CoreOS创始人Alex就预见了这个项目的价值,并将其做为 ...

  10. ooofc.com域名备案问题导致无法正常访问临时解决方案

    各位尊敬的easyradius用户: 由于临时收到ooofc.coom域名备案被删除的消息,之后ooofc.com就无法访问.导致用户无法访问控制台oa.ooofc.com,及用户中心user.ooo ...