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. LPC43xx I2S

  2. C++/Php/Python/Shell 程序按行读取文件或者控制台

    写程序经常需要用到从文件或者标准输入中按行读取信息,这里汇总一下.方便使用 1. C++ 读取文件 #include<stdio.h> #include<string.h> i ...

  3. Linux环境安装jdk

    Linux中JDK1.6的安装和配置方法 一.安装 创建安装目录,在/usr/java下建立安装路径,并将文件考到该路径下: # mkdir /usr/java 1.jdk-6u11-linux-i5 ...

  4. linux上挂载windows共享文件夹

    linux上挂载windows共享文件夹 1.共享windows目录 挂载之前得创建一个有password的用户(当前用户也能够),并将你要挂载的目录进行共享,并赋予读写权限 如图. watermar ...

  5. Android 使用java.net.socket 的接收问题

    // 初始化socketsocket = new Socket(InetAddress.getByName(sip), sport);InputStream sin = socket.getInput ...

  6. Codeforces Beta Round #17 A - Noldbach problem 暴力

    A - Noldbach problem 题面链接 http://codeforces.com/contest/17/problem/A 题面 Nick is interested in prime ...

  7. android studio 修改成自己jks(keystore)签名文件

    项目中有微信分享和微信支付,微信支付后台设置是正式签名md5值不便调试,最初直接在后台创建二个应用一个测试一个正式的,但二个人同时开发这个测试版本的md5又遇到麻烦,所以想到android studi ...

  8. 每日英语:Mistrust Between U.S., Malaysia Strains Probe

    Mistrust between U.S. and Malaysian air-accident investigators has hampered a multinational probe in ...

  9. 小试ijkplayer编译

    同步发表于 http://avenwu.net/ijkplayer/2015/05/07/hands_on_ijkplayer_preparation 谈到视频播放大家都知道ffmpeg,基于其的衍生 ...

  10. IIS兼容模式设置

    X-UA-Compatible IE=EmulateIE7 来自为知笔记(Wiz)