在Linux
系统中用wireshark
或tcpdump
抓无线网卡数据包,每一数据帧前面都有一个叫radiotap
的协议头,它包含了信号强度、噪声强度、信道、时间戳等信息。radiotap
比传统的Prism
或AVS
头更有灵活性,成为ieee802.11
事实上的标准。支持radiotap
的系统较多,如Linux
、FreeBSD
、NetBSD
、OpenBSD
,还有Windows
(需使用AirPcap
)。它的头部定义如下:
struct ieee80211_radiotap_header {
u_int8_t it_version; /* set to 0 */
u_int8_t it_pad;
u_int16_t it_len; /* entire length */
u_int32_t it_present; /* fields present */
} __attribute__((__packed__));
it_version
:表示版本号,当前为0
。it_pad
:没有使用,仅仅是为了结构体对齐。it_len
:表示长度,包括了radiotap
头部和数据两部分,如果不需要了解radiotap
,则可以直接跳到ieee802.11
头部。it_present
:表示radiotap
数据的位掩码。radiotap
的数据紧跟其头部。当其中的位掩码为true
时,表示有对应的数据,可以认为每一比特表示一种类型。比如bit5
为1
表示有通道数据,则可以获取到信号强度,反之就是没有对应的数据。因此,radiotap
的长度其实是不固定的。bit31
为1
表示还有多个it_present
。
radiotap
的每个类型都是有严格顺序的,数据的字序是小端格式(little endian byte-order
)——包括头部的it_len
和it_present
。
目前应用比较广的解析库是radiotap-library
,在horst
软件和Linux
内核中都使用到。关于每个类型的解释,可以参考radiotap.h
文件的ieee80211_radiotap_type
注释。
通过以下方式,设置Ubuntu
系统进入monitor
模式:
$ sudo iw dev wlp5s0 interface add mon0 type monitor
$ sudo iw dev wlp5s0 del
$ sudo iw dev mon0 set channel 6
$ sudo ifconfig mon0 up
接着使用tcpdump
抓包:
$ sudo tcpdump -i mon0 -w test.pcap
tcpdump: listening on mon0, link-type IEEE802_11_RADIO (802.11 plus radiotap header), capture size 262144 bytes
^C2288 packets captured
2344 packets received by filter
0 packets dropped by kernel
5 packets dropped by interface
然后使用wireshark
分析刚抓取到的包,如下图所示:
使用radiotap-library
库解析,示例代码如下:
#include <unistd.h>
#include <stdint.h>
#include <endian.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include "radiotap_iter.h"
// 根据wireshark抓包抽取的radiotap头部数据
char radiotap_buf[][18] = {
{0x00, 0x00, 0x12, 0x00, 0x2e, 0x48,
0x00, 0x00, 0x00, 0x02, 0x85, 0x09,
0xc0, 0x00, 0xc9, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x12, 0x00, 0x2e, 0x48,
0x00, 0x00, 0x00, 0x02, 0x85, 0x09,
0xa0, 0x00, 0xa8, 0x00, 0x00, 0x00}};
#define IEEE80211_CHAN_A (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
#define IEEE80211_CHAN_G (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
static void print_radiotap_namespace(struct ieee80211_radiotap_iterator *iter)
{
char signal = 0;
uint32_t phy_freq = 0;
switch (iter->this_arg_index) {
case IEEE80211_RADIOTAP_TSFT:
printf("\tTSFT: %llu\n", le64toh(*(unsigned long long *)iter->this_arg));
break;
case IEEE80211_RADIOTAP_FLAGS:
printf("\tflags: %02x\n", *iter->this_arg);
break;
case IEEE80211_RADIOTAP_RATE:
printf("\trate: %.2f Mbit/s\n", (double)*iter->this_arg/2);
break;
case IEEE80211_RADIOTAP_CHANNEL:
phy_freq = le16toh(*(uint16_t*)iter->this_arg); // 信道
iter->this_arg = iter->this_arg + 2; // 通道信息如2G、5G,等
int x = le16toh(*(uint16_t*)iter->this_arg);
printf("\tfreq: %d type: ", phy_freq);
if ((x & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) {
printf("A\n");
} else if ((x & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) {
printf("G\n");
} else if ((x & IEEE80211_CHAN_2GHZ) == IEEE80211_CHAN_2GHZ) {
printf("B\n");
}
break;
case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
signal = *(signed char*)iter->this_arg;
printf("\tsignal: %d dBm\n", signal);
break;
case IEEE80211_RADIOTAP_RX_FLAGS:
printf("\tRX flags: %#.4x\n", le16toh(*(uint16_t *)iter->this_arg));
break;
case IEEE80211_RADIOTAP_ANTENNA:
printf("\tantenna: %x\n", *iter->this_arg);
break;
case IEEE80211_RADIOTAP_RTS_RETRIES:
case IEEE80211_RADIOTAP_DATA_RETRIES:
case IEEE80211_RADIOTAP_FHSS:
case IEEE80211_RADIOTAP_DBM_ANTNOISE:
case IEEE80211_RADIOTAP_LOCK_QUALITY:
case IEEE80211_RADIOTAP_TX_ATTENUATION:
case IEEE80211_RADIOTAP_DB_TX_ATTENUATION:
case IEEE80211_RADIOTAP_DBM_TX_POWER:
case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
case IEEE80211_RADIOTAP_DB_ANTNOISE:
case IEEE80211_RADIOTAP_TX_FLAGS:
break;
default:
printf("\tBOGUS DATA\n");
break;
}
}
int main(int argc, char** argv)
{
struct ieee80211_radiotap_iterator iter;
int err;
int i, j;
for (i = 0; i < sizeof(radiotap_buf)/sizeof(radiotap_buf[0]); i++) {
printf("parsing [%d]\n", i);
err = ieee80211_radiotap_iterator_init(&iter, (struct ieee80211_radiotap_header *)radiotap_buf[i],
sizeof(radiotap_buf[i]), NULL);
if (err) {
printf("not valid radiotap...\n");
return -1;
}
j = 0;
/**
* 遍历时,this_arg_index表示当前索引(如IEEE80211_RADIOTAP_TSFT等),
* this_arg表示当前索引的值,this_arg_size表示值的大小。只有flag为true时才会进一步解析。
*/
while (!(err = ieee80211_radiotap_iterator_next(&iter))) {
printf("next[%d]: index: %d size: %d\n",
j, iter.this_arg_index, iter.this_arg_size);
if (iter.is_radiotap_ns) { // 表示是radiotap的命名空间
print_radiotap_namespace(&iter);
}
j++;
}
printf("==================================\n");
}
return 0;
}
参考文章
Ubuntu 14.04中碰到的wlan抓包问题
无线网络嗅探中的Radiotap
ieee802.11数据radiotap介绍
关于 802.11 协议
Radiotap
radiotap-library
【装】802.11封装成帧
802.11成帧封装实现(五)
pcap文件的文件头的link type
LINK-LAYER HEADER TYPES
用pcap编写网络嗅探器(Programming with pcap译文)
LINKTYPE_IEEE802_11_PRISM
libpcap抓包简单例子
Ubuntu下用wireshark抓取802.11封包并进行过滤分析
tcpdump源码分析
PCAP(3PCAP)
linux下libpcap抓包分析
使用libpcap分析网络报文 (1)
IEEE802.11数据帧在Linux上的抓取
802.11协议帧格式、Wi-Fi连接交互过程、无线破解入门研究
ubuntu下使用airmon-ng和wireshark抓取802.11管理包
IEEE802.11数据帧在Linux上的抓取
帧格式
802.11帧格式
802.11帧格式解析
组播MAC地址
Guidelines for 802.11 Promiscuous Receive Operations
Indicating Raw 802.11 Packets
Network Monitor Operation Mode
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。