suricata抓包方式之一 AF_PACKET
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, ðreq) != -) {
ethreq.ifr_flags &= ~IFF_PROMISC;
ioctl(sock, SIOCSIFFLAGS, ðreq);
}
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, ðreq) == -) {
perror("ioctl");
close(sock);
exit();
} ethreq.ifr_flags |= IFF_PROMISC;
if(ioctl(sock, SIOCSIFFLAGS, ðreq) == -) {
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的更多相关文章
- suricata抓包方式之一AF_PACKET
suricata抓包方式之一AF_PACKET 噜拯渡 睦坚削 曜纡宄 式犒藿氆 咬焚桤φ 要蒯钮 喃俚夼 币噎嶂颐 话千叶舞就后悔了怎么想都容易让人引 虻谮м 及葚雏钏 看着表面平静实际 ...
- 常用 tcpdump 抓包方式
目录 文章目录 目录 tcpdump 指令 关键字 常用指令选项 常规操作示例 过滤主机 过滤端口 过滤网络(网段) 过滤协议 复杂的逻辑表达式过滤条件 参考资料 tcpdump 指令 tcpdump ...
- 使用 Google Chrome 数据抓包方式免费下载收费音乐
对于每个有音乐细胞的 boys & girls 来说,听音乐的时候是不是会经常遇到如下图的问题. 自从音乐进入正版之后,很多歌曲只有付费用户才可以下载.虽然可以在线听,可是以我的倔脾气,就是喜 ...
- 开源网络抓包与分析框架学习-Packetbeat篇
开源简介packbeat是一个开源的实时网络抓包与分析框架,内置了很多常见的协议捕获及解析,如HTTP.MySQL.Redis等.在实际使用中,通常和Elasticsearch以及kibana联合使用 ...
- android 移动网络实时抓包
2G.3G环境,那就必须root进去tcpdump 方式抓. 准备: 一.root CF-auto-root: http://autoroot.chainfire.eu/ 需要清理全部数据,注意备份 ...
- PHP中使用CURL请求页面,使用fiddler进行抓包
在PHP中使用CURL访问页面: <?php $ch = curl_init('http://www.baidu.com'); curl_setopt($ch, CURLOPT_RETURNTR ...
- 浅析手机抓包方法实践(zt)
原文:http://drops.wooyun.org/tips/12467 0x00 摘要 在移动逆向分析以及 App 开发的时候,总会需要对其网络行为进行监控测试,本文总结一些抓包思路,并对其使用方 ...
- 安卓APP测试之使用Burp Suite实现HTTPS抓包方法
APP的测试重点小部分在APP本身,大部分还是在网络通信上(单机版除外).所以在安卓APP测试过程中,网络抓包非常重要,一般来说,app开发会采用HTTP协议.Websocket.socket协议,一 ...
- 手机数据抓包以及wireshark技巧
本文主要讨论一种非常方便的抓取Android和iphone手机网络数据包的办法,以及介绍wireshark最常用的技巧 抓包工具介绍 (1).网页抓包工具 Chrome浏览器插件 FireBug 插件 ...
随机推荐
- STM32 CANBus RAM Layout
F8 FF FF FF //F0R0 F0R1 F8 FF FF FF //F1R0 F1R1 F8 FF FF FF F8 FF FF FF F8 FF FF FF F8 FF FF FF F8 F ...
- (转)unity中基于alpha通道的shadow volume实现
转自:http://blog.163.com/wmk_2000_ren/blog/static/138846192201019114117466/ 实现呢,Aras大神已经给出了, http://fo ...
- spring 4.2.0后jdbcTemplate中不用queryForLong了(之系统升级发现)
在spring 3.2.2之后,jdbcTemplate.queryForInt已经被取消了! 原来是这样写的: String sql = "SELECT count(*) FROM USE ...
- iOS:Tools:快速注释Doxygen
Xcode5有个新特性就是自己定义的函数也会被检测集成到代码提示里面,也就是在Quick Help有提示.如 /** * @brief 设置id * * @param id 要设置的id */ +(v ...
- 【LeetCode】338. Counting Bits (2 solutions)
Counting Bits Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num ...
- LOB字段存放在指定表空间 清理CLOB字段及压缩CLOB空间
LOB字段存放在指定表空间 清理CLOB字段及压缩CLOB空间 把LOB字段的SEGMENT 存放在指定表空间.清理CLOB字段及压缩CLOB空间 1.创建LOB字段存放表空间:create ...
- LCLFramework框架之开发约束
Entity编写 1:所有的实体类都必须继承DomainEntity 2:所有的表都必须有 ID 3:所有表的关系字段必须是ID [Serializable] public class User: D ...
- 简单配置nginx使之支持pathinfo
只需要修改3个地方就可以了 location ~ \.php { #去掉$ root H:/PHPServer/WWW; fastcgi_pass 127.0.0.1:90 ...
- golang append
1) Append a slice b to an existing slice a: a = append(a, b...) 2) Copy a slice a to a new slice b: ...
- Unity Remote 4安卓机使用指南
必须U3D版本为4.5以上,可以在Public目录下载.想实时调试IOS版本必须是MAC系统! 优点:可以在不编译的情况下实时的去调试真实Android设备的各种情况,包括使用触摸功能(Remote接 ...