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,具體怎麼作,不說了。
神說:再給你一個月自由自在的時間...享受吧...

相關文章
相關標籤/搜索