IEEE802.11数据帧在Linux上的抓取 80211格式转8023帧格式

转:http://blog.csdn.net/dog250/article/details/7749372linux

终于获得了梦寐的《802.11无线网络权威指南》,虽然是复印版本,看起来也同样舒服,光看书是不行的,关键仍是本身练习,这就须要搭建一个舒服的实验环境,抓包是必不可少的了,由于只有详细分析802.11数据帧,才能深刻理解协议的细节。软件上就是这个理,手上没设备仍是不行,这但是搭建实验环境的第一步,巧妇难为无米之炊。设备问题很好解决,买一个就好了,最好买适合DIY的那种,既便宜又不怕折腾坏了,所以淘宝是一个好去处。我搞到了一个ralink的802.11bgn的无线网卡,USB2.0的,除了有点发热以外别的都很好,软AP信号足,功率够,速度快。就是驱动不太给力啊。编程

        硬件有了,先驱起来再说,完事以后就要想办法抓包了,千万别觉得抓包很容易,一个软件就搞定,想抓取802.11的数据包,还真得下一番功夫啊,本文接下来的部分就谈谈个人802.11抓包经历。windows

        Linux内核的抓包机制软件上全在ptype_all这个链表,而不存在所谓“网卡混杂模式”,混杂模式主要是硬件上的概念,有些网卡会在芯片内部完成MAC地址的过滤,所以必须让芯片“知道”不要过滤任何地址这一件事,所以就有了混杂模式的概念,对于无线网络,混杂模式在理解上更加复杂,所以“不要过滤任何数据帧”和802.11规定的AP地址过滤某些行为上是矛盾的。举个例子,一个没有和此AP创建关联的移动节点发来的包,AP按照802.11规范是要丢掉它的,然而混杂模式又要求接受它,这就是矛盾。矛盾的本质缘由在于无线链路的“无边界”特征,这是电磁波的物理特性致使的。对于此谁也无能改变,所以ESS无线网络就不能设计成一个彻底的广播网络,不然一个三维空间的广播形成的冲突要浪费多少资源啊--咱们知道有线局域网以太网最初是一个一维线缆上的广播而已,如今有了交换机,CSMA/CD基本已经再也不被使用了。只能由AP接入点来负责在移动节点之间转发数据帧,由于只有它知道谁跟本身创建了关联。这种区别形成了无线网络抓包的尴尬。网络

        在设计上,操做系统彻底避免了这个尴尬,要么它根本不容许在802.11这个层次上进行抓包,要么由驱动决定如何实现抓包。对于Linux,高版本内核实现了统一的802.11适配层框架,驱动的实现可选使用,低版本内核彻底由驱动来决定可否实现抓包;对于Windows,若是你使用WireShark工具在无线网卡上进行抓包,会获得如下错误:app

在Wireshark的网站上,也有相似的说法:框架


这个may not未免太无耻了。然而tcpdump的手册上却说能够在monitor mode下抓取802.11帧,然而这却须要驱动来支持。对Linux而言,虽然较高版本的内核支持了802.11适配层,然而却不是每一个驱动的实现都遵循了这个框架,好比我手上的ralink的驱动就很扯,彻底按照windows的那一套来写的。所以依靠monitor mode来抓取802.11帧这彻底靠不住!接下来分析Linux的抓包机制和802.11适配层的关系以及衔接。less

        对于进入的包,包在netif_receive_skb中被截取,对于出去的包,则在dev_queue_xmit,而这两个地方所有都在802.11逻辑的上面,这就涉及到了802.11的设计。
1.做为有线局域网的扩展,要能够和有线局域网无缝桥接,也就是对arp协议透明;
2.因为无线网络物理链路的特殊性,须要设计特殊的帧格式以知足须要。
tcp

这两点看上去是矛盾的,其实否则。有两种方案可实现,其一就是将以太帧封装在802.11帧里面,相似隧道那样,其二就是直接经过以太帧构造802.11帧,两种帧只要能保证能够互相生成便可。802.11采用的正是这种方式,数据帧发往无线链路时,由以太帧生成802.11帧,反过来由802.11帧生成以太帧。函数

        这就须要作一个适配层,用来转换两种帧,在linux中,这种适配层表述的很清晰。基于2.6.32的内核,$SRC/driver/net/mac80211目录下面的代码就是适配层的实现。值得注意的是,旧一点的内核版本虽然没有这么清晰的目录结构,代码也是很明白的,总的实现以下图所示:工具

注意,适配器适配的两种帧并非平行的,在协议栈上是上下的关系,这是由于咱们已经习惯了以太网,所以须要将802.11适配到以太网,而不须要反过来适配,所以对外只须要呈现一个以太网卡接口便可,没有必要再显示一个“无线网卡设备”接口。最终的结果就是802.11和802.3更像是协议栈上的上下层关系,而不是两种平行的协议。

        因为802.11的处理逻辑彻底在“网卡设备”接口如下,而抓包逻辑则在“网卡设备”接口以上(netif_XX/dev_YY),而Linux内核看到实现了802.11的网卡设备时,全部路径均已经通过了适配层,所以抓包逻辑看到的“无线网卡”上的流量就都成了以太帧了。另外对于实现了基础结构BSS无线AP的无线网卡而言,有时它转发的是两个无线站点之间的流量,为了使“网卡设备”接口这种流量,须要将全部的802.11数据帧在抉择是发往有线网仍是发给另外一个无线站点以前所有进行适配,适配成以太帧,以下图所示:

这样,咱们知道了为什么没法使用tcpdump抓取802.11帧了,接下来就是想办法抓取到这种帧。最关键的事情就是找到在哪里802.11帧转化为了以太帧以及相反的转化,而这很容易,在2.6.32内核中,ieee80211_invoke_rx_handlers函数里面取到的就是802.11帧,不过注意,必定要在__ieee80211_data_to_8023调用以前,对于其它的内核版本,也是相似的。找到了这个以后,接下来须要将如下的代码添加到你找到的位置:

[plain]  view plain  copy
 
  1. list_for_each_entry_rcu(ptype, &ptype_all, list) {  
  2.     if (ptype->dev == null_or_orig || ptype->dev == skb->dev ||  
  3.         ptype->dev == orig_dev)  
  4.         ret = deliver_skb(skb, ptype, skb->dev);  
  5.     }  
  6. }  


ptype_all并无导出,那么能够经过/boot/System.map-2.6.32-5-amd64这个文件中取得,这样就可使得tcpdump抓取到802.11的数据帧,原汁原味的。一样的方法也能够用于抓取发出的802.11数据帧。        到此为止,不得不插一句,来看看依靠802.11适配层也就是wireless框架如何来实现抓包,正如tcpdump的手册上写的那样,注意这只能在高版本的内核上才行得通,我手上的2.6.37则恰好。在框架内部,实现了ieee80211_rx函数,该函数被驱动调用,接收来自驱动的802.11数据帧,其中调用了ieee80211_rx_monitor来实现802.11数据帧直接上传到“网卡设备接口”,从而越过了适配层将802.11帧适配到以太帧这一步,ieee80211_rx_monitor中式这么实现的:

[plain]  view plain  copy
 
  1. list_for_each_entry_rcu(sdata, &local->interfaces, list) {  
  2.     if (sdata->vif.type != NL80211_IFTYPE_MONITOR)  
  3.         continue;  
  4.     if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)  
  5.         continue;  
  6.     if (!ieee80211_sdata_running(sdata))  
  7.         continue;  
  8.     if (prev_dev) {  
  9.         skb2 = skb_clone(skb, GFP_ATOMIC);  
  10.         if (skb2) {  
  11.             skb2->dev = prev_dev;  
  12.             netif_receive_skb(skb2);  //直接调用设备接口层的函数实现之  
  13.         }  
  14.     }  
  15.     prev_dev = sdata->dev;  
  16.     sdata->dev->stats.rx_packets++;  
  17.     sdata->dev->stats.rx_bytes += skb->len;  
  18. }  

 

这种方式看起来和个人方法没什么两样,实则更妙,hack一个链表头总显得不那么正规,而导出一个函数却很显而易见。然而若是驱动不遵守这个框架来写,框架里面再好的东西也无法使用,事实上现有的Linux无线网卡驱动不少都是基于ndiswrapper的,这样能够直接加载windows上的驱动程序,若是事情是这样,那还好,糟糕的是,有人写的驱动居然是个四不像。下文分解。

        事情每每要比你想的复杂得多,若是你认为上面的工做已经完成了一大半,那就大错特错了,实际完成的工做才不到1%...!!!个人ralink无线网卡的驱动在2.6.32上根本就编译不过,后来找了一个Debian4的虚拟机,编译过了,但是无法识别USB2.0的无线网卡,因而索性安装VMWare-tools,折腾了一上午,费了好大劲,终于装上了VMWare-tools,仍是没法识别,因而就又开了一个Redhat虚拟机,内核版本2.6.18-92.el5,终于既能够编译驱动又可识别网卡了,...期间,装一台物理机器的心都有了!接下来终于改修改代码了,一打开驱动源码,MD,所有是从Windows的NDIS驱动里面移植过来的,Linux内核提供的诸多的802.11适配代码彻底没有使用,所有都是本身编写的,仅仅在最上端使用register_netdevice之类的接口和内核衔接。
        困难一波接着一波,那就一个一个解决。虽然NDIS的代码不是很熟悉,但基本的逻辑仍是清楚的。在$SRC/sta/rtmp_data.c中找到了STAHandleRxDataFrame函数,就是它了:

[plain]  view plain  copy
 
  1. // 1. skip 802.11 HEADER  
  2. {  
  3.     pRxBlk->pData += LENGTH_802_11;  
  4.     pRxBlk->DataSize -= LENGTH_802_11;  
  5. }  


一会儿就找准了,skip 802.11 HEADER说明没有skip以前就是802.11帧了,因而将上述的list_for_each_entry_rcu代码加到这个代码上面,然并且慢,咱们须要本身构造skb啊,这可真的麻烦了,由于这个驱动彻底是本身处理的,没有用到skb...无论怎么说,在写构造skb的代吗以前先用printk将802.11帧打印出来再说,因而添加的代吗变成了下面的样子:

[plain]  view plain  copy
 
  1. {  
  2.     int i = 0;  
  3.     char *p = pRxBlk->pData;  
  4.     printk("###################### begin:%d\n", pRxBlk->DataSize);  
  5.     for (i = 0; i < pRxBlk->DataSize; i++) {  
  6.         printk("%02X ", *p & 0x000000ff);  
  7.         p++;  
  8.     }  
  9.     printk("%d \n", ret);  
  10.     printk("###################### end\n");  
  11. }  


而后编译,加载rt3070sta.ko,调用如下的命令:
ifconfig ra0 up
iwconfig ra0 mode Managed
iwcondig ra0 essid "zhaoya"
ifconfig ra0 190.168.1.123/24
ping 190.168.1.100

使用dmesg查看的结果以下:

这里就再也不解释802.11帧每个字段的具体含义了,涉及到不少细节,好比to ap,from ap等等,无论怎么说,成功抓取了802.11的帧,接下来就是如何使用正规的方法来作了,所谓的正规方法就是使用AF_PACKET套接字。

        唉,真的不明白Taiwan那些写ralink驱动的那帮家伙为什么如此写驱动呢,Linux内核现成的难道很差吗?搞的不三不四的,光那些函数变量命名就和GNU的风格不符合,也许驱动是首先在Windows上调通的吧,这样为了重用也没什么不能够,然而可苦了咱们DIY一族了,固然Win粉这里不论...

        可我搞不明白,为什么不直接用ndiswrapper来安装Windows的驱动程序呢,而非要把Windows的驱动移植到Linux,最终不三不四的...ndiswrapper是什么?下面简述一下。

        ndiswrapper给那些不想本身编译无线网卡的家伙们带来了福音,你能够直接使用windows的二进制驱动程序(sys文件,本质上是PE格式)。这怎么可能呢,其实很简单,ndiswrapper就是为Windows驱动程序构造了一个“家园”,一个基本的运行环境,知足Windows驱动的各种调用,这个环境分为两类,第一类就是Windows的内核基本环境,第二类就是NDIS的环境,说白了就是提供一些函数而且导出便可,ndiswrapper的示意图以下:

ndiswrapper给出了两套接口以及其Linux实现,这样Windows的PE驱动文件通过简单处理后就能够正常运行了,Windows驱动程序自己看来外部环境和Windows的如出一辙,该调用的函数都有,它只管接口,并无论实现(针对接口编程!)。这个原理和Linux用户态的Wine是同样的,Wine也是提供了一个外部环境,好比诸多的dll,Wine的实现要比ndiswrapper更加复杂,由于库太多了。

        若是使用ndiswrapper,怎么实现802.11的抓包呢?虽说Wireshark的网站上的一篇WLAN (IEEE 802.11) capture setup明确提到:
Windows

Capturing WLAN traffic on Windows depends on WinPcap and on the underlying network adapters and drivers. Unfortunately,  most drivers/adapters support neither monitor mode, nor seeing 802.11 headers when capturing, nor capturing non-data  frames.

Promiscuous mode can be set; unfortunately, it's often crippled. In this mode many drivers don't supply packets at all, or don't supply packets sent by the host. 

可是修改ndiswrapper仍是很容易的,注意,这个NDIS并非WIndows上的NDIS,这个NDIS只是一个接口集合,其实现仍是Linux,具体怎么作,不说了。
神说:再给你一个月自由自在的时间...享受吧...

相关文章
相关标签/搜索