IEEE802 11資料幀在Linux上的抓取

滿舅舅發表於2018-11-04

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                終於得到了夢寐的《802.11無線網路權威指南》,雖然是影印版本,看起來也一樣舒服,光看書是不行的,關鍵還是自己練習,這就需要搭建一個舒服的實驗環境,抓包是必不可少的了,因為只有詳細分析802.11資料幀,才能深入理解協議的細節。軟體上就是這個理,手上沒裝置還是不行,這可是搭建實驗環境的第一步,巧婦難為無米之炊。裝置問題很好解決,買一個就行了,最好買適合DIY的那種,既便宜又不怕折騰壞了,因此淘寶是一個好去處。我搞到了一個ralink的802.11bgn的無線網路卡,USB2.0的,除了有點發熱之外別的都很好,軟AP訊號足,功率夠,速度快。就是驅動不太給力啊。

        硬體有了,先驅起來再說,完事之後就要想辦法抓包了,千萬別以為抓包很容易,一個軟體就搞定,想抓取802.11的資料包,還真得下一番功夫啊,本文接下來的部分就談談我的802.11抓包經歷。

        Linux核心的抓包機制軟體上全在ptype_all這個連結串列,而不存在所謂“網路卡混雜模式”,混雜模式主要是硬體上的概念,有些網路卡會在晶片內部完成MAC地址的過濾,因此必須讓晶片“知道”不要過濾任何地址這一件事,因此就有了混雜模式的概念,對於無線網路,混雜模式在理解上更加複雜,因此“不要過濾任何資料幀”和802.11規定的AP地址過濾某些行為上是矛盾的。舉個例子,一個沒有和此AP建立關聯的移動節點發來的包,AP按照802.11規範是要丟掉它的,然而混雜模式又要求接受它,這就是矛盾。矛盾的本質原因在於無線鏈路的“無邊界”特徵,這是電磁波的物理特性導致的。對於此誰也無能改變,因此ESS無線網路就不能設計成一個完全的廣播網路,否則一個三維空間的廣播造成的衝突要浪費多少資源啊--我們知道有線區域網乙太網最初是一個一維線纜上的廣播而已,現在有了交換機,CSMA/CD基本已經不再被使用了。只能由AP接入點來負責在移動節點之間轉發資料幀,因為只有它知道誰跟自己建立了關聯。這種區別造成了無線網路抓包的尷尬。

        在設計上,作業系統完全避免了這個尷尬,要麼它根本不允許在802.11這個層次上進行抓包,要麼由驅動決定如何實現抓包。對於Linux,高版本核心實現了統一的802.11適配層框架,驅動的實現可選使用,低版本核心完全由驅動來決定能否實現抓包;對於Windows,如果你使用WireShark工具在無線網路卡上進行抓包,會得到以下錯誤:


在Wireshark的網站上,也有類似的說法:


這個may not未免太無恥了。然而tcpdump的手冊上卻說可以在monitor mode下抓取802.11幀,然而這卻需要驅動來支援。對Linux而言,雖然較高版本的核心支援了802.11適配層,然而卻不是每個驅動的實現都遵循了這個框架,比如我手上的ralink的驅動就很扯,完全按照windows的那一套來寫的。因此依靠monitor mode來抓取802.11幀這完全靠不住!接下來分析Linux的抓包機制和802.11適配層的關係以及銜接。

        對於進入的包,包在netif_receive_skb中被擷取,對於出去的包,則在dev_queue_xmit,而這兩個地方全部都在802.11邏輯的上面,這就涉及到了802.11的設計。
1.作為有線區域網的擴充套件,要可以和有線區域網無縫橋接,也就是對arp協議透明;
2.由於無線網路物理鏈路的特殊性,需要設計特殊的幀格式以滿足需要。

這兩點看上去是矛盾的,其實不然。有兩種方案可實現,其一就是將以太幀封裝在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呼叫之前,對於其它的核心版本,也是類似的。找到了這個之後,接下來需要將以下的程式碼新增到你找到的位置:

list_for_each_entry_rcu(ptype, &ptype_all, list) {    if (ptype->dev == null_or_orig || ptype->dev == skb->dev ||        ptype->dev == orig_dev)        ret = deliver_skb(skb, ptype, skb->dev);    }}

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中式這麼實現的:
list_for_each_entry_rcu(sdata, &local->interfaces, list) {    if (sdata->vif.type != NL80211_IFTYPE_MONITOR)        continue;    if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)        continue;    if (!ieee80211_sdata_running(sdata))        continue;    if (prev_dev) {        skb2 = skb_clone(skb, GFP_ATOMIC);        if (skb2) {            skb2->dev = prev_dev;            netif_receive_skb(skb2);  //直接呼叫裝置介面層的函式實現之        }    }    prev_dev = sdata->dev;    sdata->dev->stats.rx_packets++;    sdata->dev->stats.rx_bytes += skb->len;}

這種方式看起來和我的方法沒什麼兩樣,實則更妙,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函式,就是它了:
// 1. skip 802.11 HEADER{    pRxBlk->pData += LENGTH_802_11;    pRxBlk->DataSize -= LENGTH_802_11;}

一下子就找準了,skip 802.11 HEADER說明沒有skip之前就是802.11幀了,於是將上述的list_for_each_entry_rcu程式碼加到這個程式碼上面,然而且慢,我們需要自己構造skb啊,這可真的麻煩了,因為這個驅動完全是自己處理的,沒有用到skb...不管怎麼說,在寫構造skb的代嗎之前先用printk將802.11幀列印出來再說,於是新增的代嗎變成了下面的樣子:
{    int i = 0;    char *p = pRxBlk->pData;    printk("###################### begin:%d\n", pRxBlk->DataSize);    for (i = 0; i < pRxBlk->DataSize; i++) {        printk("%02X ", *p & 0x000000ff);        p++;    }    printk("%d \n", ret);    printk("###################### end\n");}

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

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述

相關文章