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. PHP之算法偶遇隨感

    php真的很棒,很多函數把我們想要的功能都簡單實現了,是項目快速開發的首選.說實話,在BS程序開發方面我認為最好的兩種語言是PHP和JSP,我之前曾學過一段時間的java,確實很棒完全的OOP,但是它 ...

  2. 一道SQL面试例题 if...else 与聚集函数

    晚上回来,同学说面试遇到了一个SQL面试题目,自己做了一下,总结总结. 题目如下: 下面是产品数据表(产品id,颜色col,数量num),其中每种产品有1~2种颜色. 求每种产品各颜色的数量差值(对于 ...

  3. windows server 2008 R2 FTP登陆错误。

    建立了一个域用户ftp. 始终登陆不上winserver 2008 R2上的FTP. 错误如下: 530-User cannot log in. Win32 error:   Logon failur ...

  4. 用git写书

    apebook.org  www.apebook.org 最好的程序员图书免费托管服务 apebook 提供了 gitbook.com 类似的云端图书托管能力,图书基于广受欢迎的 gitbook 工具 ...

  5. ECshop鼠标划过弹出 微信扫一扫代码

     效果如上图 安装步骤:1,将以下代码放到page_header.lbi里   <div class="f_l"><a href="../index.p ...

  6. 【Linux】——ctags

    ctags的功能:扫描指定的源文件,找出其中所包含的语法元素,并将找到的相关内容记录下来. ctags 可以在官网上下载源代码,然后编译安装.最后在 ~/.vimrc 文件中写入以下配置: " ...

  7. jQuery的ajax 方法提交多个对象数组问题 C# traditional $.param

    当用$.ajax()向后台提交参数时,如果参数中数组的话一般在后台会用List<T>接收;但老是不成功如下面代码 "}]; "}]; function addUser( ...

  8. 使用pathogen管理Vim插件并托管到Github

    参照文章[1][2]的办法,将vim打造成一个Python开发环境.文章中使用的是 pathogen + git 来管理 Vim 插件的.对这种方式还不太明白的同学可以参考[3]中的介绍.pathog ...

  9. Spark源码系列(八)Spark Streaming实例分析

    这一章要讲Spark Streaming,讲之前首先回顾下它的用法,具体用法请参照<Spark Streaming编程指南>. Example代码分析 val ssc = )); // 获 ...

  10. android自定义viewgroup之我也玩瀑布流

    先看效果图吧, 继上一篇<android自定义viewgroup实现等分格子布局>中实现的布局效果,这里稍微有些区别,每个格子的高度不规则,就是传说的瀑布流布局,一般实现这种效果,要么用第 ...