亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
123下一頁(yè)
最近訪問(wèn)板塊 發(fā)新帖
查看: 12387 | 回復(fù): 21
打印 上一主題 下一主題

Hacking the Linux Kernel Network Stack(譯本) [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2006-05-20 22:27 |只看該作者 |倒序?yàn)g覽
前一段時(shí)間看到這篇帖子,確實(shí)很經(jīng)典,于是翻出了英文原版再讀,順便再翻譯出來(lái)供大家學(xué)習(xí),這篇文章的中文版也早都有了,不過(guò)出于完全理解的目的,我還是將它翻譯了出來(lái),加進(jìn)了自己的代碼,雖然在上一周的翻譯過(guò)程中,我盡量保留文章的原汁原味,但錯(cuò)誤肯定在所難免,在末尾附上原文和我自己調(diào)試通過(guò)的代碼,已經(jīng)夠構(gòu)運(yùn)行,大家可以參考一下!(有錯(cuò)誤之處請(qǐng)指出)

深入Linux內(nèi)核網(wǎng)絡(luò)堆棧

作者:bioforge alkerr@yifan.net
原名: <<Hacking the Linux Kernel Network Stack>>
翻譯,修改: duanjigang <duanjigang1983@126.com>
翻譯參考:raodan (raod_at_30san.com) 2003-08-22

第一章  簡(jiǎn)介

本文將描述如何利用Linux網(wǎng)絡(luò)堆棧的竅門(mén)(不一定都是漏洞)來(lái)達(dá)到一些目的,或者是惡意的,或者是出于其它意圖的。文中會(huì)就后門(mén)通訊對(duì)Netfilter鉤子進(jìn)行討論,并在本地機(jī)器上實(shí)現(xiàn)將這個(gè)傳輸從基于Libpcap的嗅探器(sniffer)中隱藏。
    Netfilter是2.4內(nèi)核的一個(gè)子系統(tǒng)。Netfilter可以通過(guò)在內(nèi)核的網(wǎng)絡(luò)代碼中使用各種鉤子來(lái)實(shí)現(xiàn)數(shù)據(jù)包過(guò)濾,網(wǎng)絡(luò)地址轉(zhuǎn)換(NAT)和連接跟蹤等網(wǎng)絡(luò)欺騙。這些鉤子被放置在內(nèi)核代碼段,或者靜態(tài)編譯進(jìn)內(nèi)核,或者作為一個(gè)可動(dòng)態(tài)加載/卸載的可卸載模塊,然后就可以注冊(cè)稱(chēng)之為網(wǎng)絡(luò)事件的函數(shù)(比如數(shù)據(jù)包的接收)。

1.1 本文論述的內(nèi)容

本文將講述內(nèi)核模塊的編寫(xiě)者如何利用Netfilter的鉤子來(lái)達(dá)到任何目的,以及怎樣將網(wǎng)絡(luò)傳輸從一個(gè)Libpcap的應(yīng)用中隱藏掉。盡管Linux2.4支持對(duì)IPV4,IPV6以及DECnet的鉤子,本文只提及IPV4的鉤子。但是,對(duì)IPV4的大多數(shù)應(yīng)用內(nèi)容同樣也可以應(yīng)用于其他協(xié)議。出于教學(xué)目的,我們?cè)诟戒汚給出了一個(gè)可以工作的內(nèi)核模塊,實(shí)現(xiàn)基本的數(shù)據(jù)包過(guò)濾功能。針對(duì)本文中所列技術(shù)的所有開(kāi)發(fā)和試驗(yàn)都在Intel機(jī)子上的Linux2.4.5系統(tǒng)上進(jìn)行過(guò)。對(duì)Netfilte 鉤子行為的測(cè)試使用的是回環(huán)設(shè)備(Loopback device),以太網(wǎng)設(shè)備和一個(gè)點(diǎn)對(duì)點(diǎn)接口的調(diào)制解調(diào)器。
對(duì)Netfilter進(jìn)行完全理解是我撰寫(xiě)本文的另一個(gè)初衷。我不能保證這篇文章所附的代碼100%的沒(méi)有差錯(cuò),但是所列舉的所有代碼我都事先測(cè)試過(guò)了。我已經(jīng)飽嘗了內(nèi)核錯(cuò)誤帶來(lái)的磨礪,而你卻不必再經(jīng)受這些。同樣,我不會(huì)為按照這篇文檔所說(shuō)的任何東西進(jìn)行的作所所為帶來(lái)的損失而負(fù)責(zé)。閱讀本篇文章的讀者最好熟悉C程序設(shè)計(jì)語(yǔ)言,并且對(duì)內(nèi)核可卸載模塊有一定的經(jīng)驗(yàn)。
如果我在文中犯了任何錯(cuò)誤的話(huà),請(qǐng)告知我。我對(duì)于你們的建議和針對(duì)此文的改進(jìn)或者其它的Netfilter應(yīng)用會(huì)傾心接受。

1.2 本文不會(huì)涉及到的方面

本文并不是Netfilter的完全貫穿(或者進(jìn)進(jìn)出出的講解)。也不是iptables命令的介紹。如果你想更好的學(xué)習(xí)iptables的命令,可以去咨詢(xún)man手冊(cè)。
讓我們從介紹Nerfilter的使用開(kāi)始吧……….

第二章  各種NetFilter 鉤子及其用法

2.1 Linux內(nèi)核對(duì)數(shù)據(jù)包的處理

我將盡最大努力去分析內(nèi)核處理數(shù)據(jù)包的詳細(xì)內(nèi)幕,然而對(duì)于事件觸發(fā)處理以及之后的Netfilter 鉤子不做介紹。原因很簡(jiǎn)單,因?yàn)镠arald Welte 關(guān)于這個(gè)已經(jīng)寫(xiě)了一篇再好不過(guò)的文章<<Journey  of a Packet Through the Linux 2.4 Network Stack>>,如果你想獲取更多關(guān)于Linux對(duì)數(shù)據(jù)包的相關(guān)處理知識(shí)的話(huà),我強(qiáng)烈建議你也閱讀一下這篇文章。目前,就認(rèn)為數(shù)據(jù)包只是經(jīng)過(guò)了Linux內(nèi)核的網(wǎng)絡(luò)堆棧,它穿過(guò)幾層鉤子,在經(jīng)過(guò)這些鉤子時(shí),數(shù)據(jù)包被解析,保留或者丟棄。這就是所謂的Netfilter 鉤子。

2.2 Ipv4中的Netfilter鉤子

Netfilter為IPV4定義了5個(gè)鉤子?梢栽 linux/netfilter-ipv4.h里面找到這些符號(hào)的定義,表2.1列出了這些鉤子。

表 2.1. ipv4中定義的鉤子

  1. 鉤子名稱(chēng)                              調(diào)用時(shí)機(jī)  
  2. NF_IP_PRE_ROUTING                完整性校驗(yàn)之后,路由決策之前
  3. NF_IP_LOCAL_IN                         目的地為本機(jī),路由決策之后
  4. NF_IP_FORWARD                         數(shù)據(jù)包要到達(dá)另外一個(gè)接口去
  5. NF_IP_LOCAL_OUT                        本地進(jìn)程的數(shù)據(jù),發(fā)送出去的過(guò)程中
  6. NF_IP_POST_ROUTING        向外流出的數(shù)據(jù)上線(xiàn)之前
復(fù)制代碼

NF_IP_PRE_ROUTING 鉤子稱(chēng)為是數(shù)據(jù)包接收后第一個(gè)調(diào)用的鉤子程序,這個(gè)鉤子在我們后面提到的模塊當(dāng)中將會(huì)被用到。其他的鉤子也很重要,但是目前我們只集中探討NF_IP_PRE_ROUTING這個(gè)鉤子。
不管鉤子函數(shù)對(duì)數(shù)據(jù)包做了哪些處理,它都必須返回表2.2中的一個(gè)預(yù)定義好的Netfilter返回碼。
表2.2 Netfilter 返回碼

  1. 返回碼                  含義
  2. NF_DROP              丟棄這個(gè)數(shù)據(jù)包
  3. NF_ACCEPT        保留這個(gè)數(shù)據(jù)包
  4. NF_STOLEN        忘掉這個(gè)數(shù)據(jù)包
  5. NF_QUEUE        讓這個(gè)數(shù)據(jù)包在用戶(hù)空間排隊(duì)
  6. NF_REPEAT        再次調(diào)用這個(gè)鉤子函數(shù)
復(fù)制代碼

NF_DROP 表示要丟棄這個(gè)數(shù)據(jù)包,并且為這個(gè)數(shù)據(jù)包申請(qǐng)的所有資源都要得到釋放。NF_ACCEPT告訴Netfilter到目前為止,這個(gè)數(shù)據(jù)包仍然可以被接受,應(yīng)該將它移到網(wǎng)絡(luò)堆棧的下一層。NF_STOLEN是非常有趣的一個(gè)返回碼,它告訴Netfilter讓其忘掉這個(gè)數(shù)據(jù)包。也就是說(shuō)鉤子函數(shù)會(huì)在這里對(duì)這個(gè)數(shù)據(jù)包進(jìn)行完全的處理,而Netfilter就應(yīng)該放棄任何對(duì)它的處理了。然而這并不意味著為該數(shù)據(jù)包申請(qǐng)的所有資源都要釋放掉。這個(gè)數(shù)據(jù)包和它各自的sk_buff結(jié)構(gòu)體依然有效,只是鉤子函數(shù)從Netfilter奪取了對(duì)這個(gè)數(shù)據(jù)包的掌控權(quán)。不幸的是,我對(duì)于NF_QUEUE這個(gè)返回碼的真實(shí)作用還不是很清楚,所在目前不對(duì)它進(jìn)行討論。最后一個(gè)返回值NF_REPEAT請(qǐng)求Netfilter再次調(diào)用這個(gè)鉤子函數(shù),很明顯,你應(yīng)該慎重的應(yīng)用這個(gè)返回值,以免程序陷入死循環(huán)。

第三章  注冊(cè)和注銷(xiāo)NetFilter 鉤子

注冊(cè)一個(gè)鉤子函數(shù)是一個(gè)圍繞nf_hook_ops結(jié)構(gòu)體的很簡(jiǎn)單的過(guò)程,在linux/netfilter.h中有這個(gè)結(jié)構(gòu)體的定義,定義如下:

  1. struct nf_hook_ops
  2. {
  3.                   struct list_head list;

  4.                   /* User fills in from here down. */
  5.                   nf_hookfn *hook;
  6.                   int pf;
  7.                   int hooknum;
  8.                   /* Hooks are ordered in ascending priority. */
  9.                   int priority;
  10. };
復(fù)制代碼

這個(gè)結(jié)構(gòu)體的成員列表主要是用來(lái)維護(hù)注冊(cè)的鉤子函數(shù)列表的,對(duì)于用戶(hù)來(lái)說(shuō),在注冊(cè)時(shí)并沒(méi)有多么重要。hook是指向nf_hookfn函數(shù)的指針。也就是為這個(gè)鉤子將要調(diào)用的所有函數(shù)。nf_hookfn同樣定義在linux/netfilter.h這個(gè)文件中。pf字段指定了協(xié)議簇(protocol family)。Linux/socket.h中定義了可用的協(xié)議簇。但是對(duì)于IPV4我們只使用PF_INET。hooknum 域指名了為哪個(gè)特殊的鉤子安裝這個(gè)函數(shù),也就是表2.1中所列出的條目中的一個(gè)。Priority域表示在運(yùn)行時(shí)這個(gè)鉤子函數(shù)執(zhí)行的順序。為了演示例子模塊,我們選擇NF_IP_PRI_FIRST這個(gè)優(yōu)先級(jí)。
   注冊(cè)一個(gè)Netfilter鉤子要用到nf_hook_ops這個(gè)結(jié)構(gòu)體和nf_register_hook()函數(shù)。nf_register_hook()函數(shù)以一個(gè)nf_hook_ops結(jié)構(gòu)體的地址作為參數(shù),返回一個(gè)整型值。如果你閱讀了net/core/netfilter.c中nf_register_鉤子()的源代碼的話(huà),你就會(huì)發(fā)現(xiàn)這個(gè)函數(shù)只返回了一個(gè)0。下面這個(gè)例子注冊(cè)了一個(gè)丟棄所有進(jìn)入的數(shù)據(jù)包的函數(shù)。這段代碼同時(shí)會(huì)向你演示Netfilter的返回值是如何被解析的。

代碼列表1. Netfilter鉤子的注冊(cè)

  1. /* Sample code to install a Netfilter hook function that will
  2. * drop all incoming packets. */
  3. #define __KERNEL__
  4. #define MODULE
  5. #include <linux/module.h>
  6. #include <linux/kernel.h>
  7. #include <linux/netfilter.h>
  8. #include <linux/netfilter_ipv4.h>

  9. /* This is the structure we shall use to register our function */

  10. static struct nf_hook_ops nfho;

  11. /* This is the hook function itself */

  12. unsigned int hook_func(unsigned int hooknum,
  13. struct sk_buff **skb,
  14. const struct net_device *in,
  15. const struct net_device *out,
  16. int (*okfn)(struct sk_buff *))
  17. {

  18. return NF_DROP;           /* Drop ALL packets */
  19. }

  20. /* Initialisation routine */
  21. int init_module()
  22. {

  23. /* Fill in our hook structure */
  24. nfho.hook = hook_func;         /* Handler function */
  25. nfho.hooknum  = NF_IP_PRE_ROUTING; /* First hook for IPv4 */
  26. nfho.pf       = PF_INET;
  27. nfho.priority = NF_IP_PRI_FIRST;   /* Make our function first */
  28. nf_register_hook(&nfho);
  29. return 0;
  30. }

  31. /* Cleanup routine */
  32. void cleanup_module()
  33. {
  34. nf_unregister_hook(&nfho);
  35. }
復(fù)制代碼


這就是注冊(cè)所要做的一切。從代碼列表1你可以看到注銷(xiāo)一個(gè)Netfilter鉤子也是很簡(jiǎn)單的一件事情,只需要調(diào)用nf_unregister_hook()函數(shù),并將注冊(cè)時(shí)用到的結(jié)構(gòu)體地址再次作為注銷(xiāo)函數(shù)參數(shù)使用就可以了。
第四章  基本的NetFilter數(shù)據(jù)包過(guò)濾技術(shù)
4.1 鉤子函數(shù)近距離接觸
現(xiàn)在是我們來(lái)查看獲得的數(shù)據(jù)如何傳入鉤子函數(shù)并被用來(lái)進(jìn)行過(guò)濾決策的時(shí)候了。所以,我們需要更多的關(guān)注于nf_hookfn函數(shù)的模型。Linux/netfilter.h給出了如下的接口定義:
  1. typedef unsigned int nf_hookfn(unsigned int hooknum,
  2.                               struct sk_buff **skb,
  3.                               const struct net_device *in,
  4.                               const struct net_device *out,
  5.                               int (*okfn)(struct sk_buff *));

復(fù)制代碼

nf_hookfn函數(shù)的第一個(gè)參數(shù)指定了表2.1給出的鉤子類(lèi)型中的一種。第二個(gè)參數(shù)更有趣,它是一個(gè)指向指針(這個(gè)指針指向一個(gè)sk_buff類(lèi)型的結(jié)構(gòu)體)的指針,它是網(wǎng)絡(luò)堆棧用來(lái)描述數(shù)據(jù)包的結(jié)構(gòu)體。這個(gè)結(jié)構(gòu)體定義在linux/skbuff.h中,由于這個(gè)結(jié)構(gòu)體的定義很大,這里我只著重于它當(dāng)中更有趣的一些域。
或許sk_buff結(jié)構(gòu)體中最有用的域就是其中的三個(gè)聯(lián)合了,這三個(gè)聯(lián)合描述了傳輸層的頭信息(例如 UDP,TCP,ICMP,SPX),網(wǎng)絡(luò)層的頭信息(例如ipv4/6, IPX, RAW)和鏈路層的頭信息(Ethernet 或者RAW)。三個(gè)聯(lián)合相應(yīng)的名字分別為:h,nh和mac。根據(jù)特定數(shù)據(jù)包使用的不同協(xié)議,這些聯(lián)合包含了不同的結(jié)構(gòu)體。應(yīng)當(dāng)注意,傳輸層的頭和網(wǎng)絡(luò)層的頭極有可能在內(nèi)存中指向相同的內(nèi)存單元。在TCP數(shù)據(jù)包中也是這樣的情況,h和nh都是指向IP頭結(jié)構(gòu)體的指針。這就意味著,如果認(rèn)為h->th指向TCP頭,從而想通過(guò)h->th來(lái)獲取一個(gè)值的話(huà),將會(huì)導(dǎo)致錯(cuò)誤發(fā)生。因?yàn)閔->th實(shí)際指向IP頭,等同于nh->iph。
其他比較有趣的域就是len域和data域了。len表示包中從data開(kāi)始的數(shù)據(jù)總長(zhǎng)度。因此,現(xiàn)在我們就知道如何通過(guò)一個(gè)skbuff結(jié)構(gòu)體去訪問(wèn)單個(gè)的協(xié)議頭或者數(shù)據(jù)包本身的數(shù)據(jù)。還有什么有趣的數(shù)據(jù)位對(duì)于Netfilter的鉤子函數(shù)而言是有用的呢?
跟在sk_buff之后的兩個(gè)參數(shù)都是指向net_device結(jié)構(gòu)體的指針。net_devices結(jié)構(gòu)體是Linux內(nèi)核用來(lái)描述各種網(wǎng)絡(luò)接口的。第一個(gè)結(jié)構(gòu)體,in,代表了數(shù)據(jù)包將要到達(dá)的接口,當(dāng)然 out就代表了數(shù)據(jù)包將要離開(kāi)的接口。有很重要的一點(diǎn)必須認(rèn)識(shí)到,那就是通常情況下這兩個(gè)參數(shù)最多只提供一個(gè)。 例如,in通常情況下只會(huì)被提供給NF_IP_PRE_ROUTING和NF_IP_LOCAL_IN鉤子。out通常只被提供給NF_IP_LOCAL_OUT和NF_IP_POST_ROUTING鉤子。在這個(gè)階段,我沒(méi)有測(cè)試他們中的那個(gè)對(duì)于NF_IP_FORWARD是可用的。如果你能在廢棄之前確認(rèn)它們(in和out)不空的話(huà),那么你很優(yōu)秀。
最后,傳給鉤子函數(shù)的最后一個(gè)參數(shù)是一個(gè)名為okfn的指向函數(shù)的指針,這個(gè)函數(shù)有一個(gè)sk_buff的結(jié)構(gòu)體作為參數(shù),返回一個(gè)整型值。我也不能確定這個(gè)函數(shù)做什么,在net/core/netfilter.c中有兩處對(duì)此函數(shù)的調(diào)用。這兩處調(diào)用就是在函數(shù)nf_hook_slow()和函數(shù)nf_reinject()里,在這兩個(gè)調(diào)用處當(dāng)Netfilter鉤子的返回值為NF_ACCEPT時(shí),此函數(shù)被調(diào)用。如果有誰(shuí)知道關(guān)于okfn更詳細(xì)的信息,請(qǐng)告訴我。
現(xiàn)在我們已經(jīng)對(duì)Netfilter接收到的數(shù)據(jù)中最有趣和最有用的部分進(jìn)行了分析,下面就要開(kāi)始介紹如何利用這些信息對(duì)數(shù)據(jù)包進(jìn)行各種各樣的過(guò)濾。

4.2 基于接口的過(guò)濾
這將是我們能做的最簡(jiǎn)單的過(guò)濾技術(shù)。是否還記得我們的鉤子函數(shù)接收到的net_device結(jié)構(gòu)體?利用net_device結(jié)構(gòu)體中的name鍵值,我們可以根據(jù)數(shù)據(jù)包的目的接口名或者源接口名來(lái)丟棄這些數(shù)據(jù)包。為了拋棄所有發(fā)向”eth0”的數(shù)據(jù),我們只需要比較一下“in->name”和“eth0”,如果匹配的話(huà),鉤子函數(shù)返回NF_DROP,然后這個(gè)數(shù)據(jù)包就被銷(xiāo)毀了。它就是這樣的簡(jiǎn)單。列表2給出了示例代碼。請(qǐng)注意輕量級(jí)防火墻(LWFW)會(huì)使用到這里提到的所有過(guò)濾方法。LWFW同時(shí)還包含了一個(gè)IOCTL方法來(lái)動(dòng)態(tài)改變自身的行為。

列表2. 基于源接口(網(wǎng)卡名)的數(shù)據(jù)過(guò)濾技術(shù)
  1. /* Sample code to install a Netfilter hook function that will
  2.           * drop all incoming packets from an IP address we specify */

  3.           #define __KERNEL__
  4.           #define MODULE

  5.           #include <linux/module.h>
  6.           #include <linux/kernel.h>
  7.           #include <linux/skbuff.h>
  8.           #include <linux/ip.h>                  /* For IP header */
  9.           #include <linux/netfilter.h>
  10.           #include <linux/netfilter_ipv4.h>

  11.           /* This is the structure we shall use to register our function */
  12.           static struct nf_hook_ops nfho;

  13.           /* IP address we want to drop packets from, in NB order */
  14.           static unsigned char *drop_ip = "\x7f\x00\x00\x01";

  15.           /* This is the hook function itself */
  16.           unsigned int hook_func(unsigned int hook_num,
  17.                                  struct sk_buff **skb,
  18.                                  const struct net_device *in,
  19.                                  const struct net_device *out,
  20.                                  int (*okfn)(struct sk_buff *))
  21.           {
  22.               struct sk_buff *sb = *skb;

  23.               if (sb->nh.iph->saddr == drop_ip) {
  24.                   printk("Dropped packet from... %d.%d.%d.%d\n",
  25.                             *drop_ip, *(drop_ip + 1),
  26.                           *(drop_ip + 2), *(drop_ip + 3));
  27.                   return NF_DROP;
  28.               } else {
  29.                   return NF_ACCEPT;
  30.               }
  31.           }

  32.           /* Initialisation routine */
  33.           int init_module()
  34.           {
  35.               /* Fill in our hook structure */
  36.               nfho.hook     = hook_func;
  37.               /* Handler function */
  38.               nfho.hook_num  = NF_IP_PRE_ROUTING; /* First for IPv4 */
  39.               nfho.pf       = PF_INET;
  40.               nfho.priority = NF_IP_PRI_FIRST;   /* Make our func first */
  41.          
  42.               nf_register_hook(&nfho);

  43.               return 0;
  44.           }
  45.          
  46.              /* Cleanup routine */
  47.           void cleanup_module()
  48.           {
  49.               nf_unregister_hook(&nfho);
  50.           }
復(fù)制代碼

現(xiàn)在看看,是不是很簡(jiǎn)單?下面讓我們看看基于IP地址的過(guò)濾技術(shù)。
4.3 基于IP地址的過(guò)濾
類(lèi)似基于接口的數(shù)據(jù)包過(guò)濾技術(shù),基于源/目的IP地址的數(shù)據(jù)包過(guò)濾技術(shù)也很簡(jiǎn)單。這次我們對(duì)sk_buff結(jié)構(gòu)體比較感興趣,F(xiàn)在應(yīng)該記起來(lái),Skb參數(shù)是一個(gè)指向sk_buff結(jié)構(gòu)體的指針的指針。為了避免運(yùn)行時(shí)出現(xiàn)錯(cuò)誤,通常有一個(gè)好的習(xí)慣就是另外聲明一個(gè)指針指向sk_buff結(jié)構(gòu)體的指針,把它賦值為雙重指針?biāo)赶虻膬?nèi)容,像這樣:
  1. struct sk_buff *sb = *skb;    /* Remove 1 level of indirection* /
復(fù)制代碼

然后你只需要引用一次就可以訪問(wèn)結(jié)構(gòu)體中的成員了?梢允褂胹k_buff結(jié)構(gòu)體中的網(wǎng)絡(luò)層頭信息來(lái)獲取此數(shù)據(jù)包的IP頭信息。這個(gè)頭包含在一個(gè)聯(lián)合中,可以通過(guò)sk_buff->nh.iph來(lái)獲取。列表3的函數(shù)演示了當(dāng)給定一個(gè)數(shù)據(jù)包的sk_buff結(jié)構(gòu)時(shí),如何根據(jù)給定的要拒絕的IP對(duì)這個(gè)數(shù)據(jù)包進(jìn)行源IP地址的檢驗(yàn)。這段代碼是直接從LWFW中拉出來(lái)的。唯一的不同之處就是LWFW中對(duì)LWFW統(tǒng)計(jì)量的更新被去掉了。
列表3.檢測(cè)接收到數(shù)據(jù)包的源IP地址
  1. unsigned char *deny_ip = "\x7f\x00\x00\x01";  /* 127.0.0.1 */

  2.           ...
  3.           static int check_ip_packet(struct sk_buff *skb)
  4.           {
  5.               /* We don't want any NULL pointers in the chain to
  6.                * the IP header. */
  7.               if (!skb )return NF_ACCEPT;
  8.               if (!(skb->nh.iph)) return NF_ACCEPT;
  9.               if (skb->nh.iph->saddr == *(unsigned int *)deny_ip)
  10. {
  11.                     return NF_DROP;
  12.                }
  13.                return NF_ACCEPT;
  14.           }
復(fù)制代碼

如果源IP地址與我們想拋棄數(shù)據(jù)包的IP地址匹配的話(huà),數(shù)據(jù)包就會(huì)被丟棄。為了使函數(shù)能正常工作,deny_ip的值應(yīng)該以網(wǎng)絡(luò)字節(jié)序的方式存儲(chǔ)(與intel相反的Big-endian格式)。盡管這個(gè)函數(shù)在被調(diào)用的時(shí)候有一個(gè)空指針作參數(shù)這種情況不太可能,但是稍微偏執(zhí)(小心)一點(diǎn)總不會(huì)有什么壞處。當(dāng)然,如果調(diào)用時(shí)出現(xiàn)了差錯(cuò)的話(huà),函數(shù)將會(huì)返回一個(gè)NF_ACCEPT值,以便于Netfilter能夠繼續(xù)處理這個(gè)數(shù)據(jù)包。列表4 展現(xiàn)了一個(gè)簡(jiǎn)單的基于IP地址的數(shù)據(jù)包過(guò)濾的模塊,這個(gè)模塊是由基于接口的過(guò)濾模塊修改得到的。你可以修改IP地址來(lái)實(shí)現(xiàn)對(duì)指定IP地址發(fā)來(lái)的數(shù)據(jù)包的丟棄。

列表4. 基于數(shù)據(jù)包源IP地址的過(guò)濾技術(shù)
  1. /* Sample code to install a Netfilter hook function that will
  2.           * drop all incoming packets from an IP address we specify */

  3.           #define __KERNEL__
  4. #define MODULE
  5. #include <linux/module.h>
  6.           #include <linux/kernel.h>
  7.           #include <linux/skbuff.h>
  8.           #include <linux/ip.h>                  /* For IP header */
  9.           #include <linux/netfilter.h>
  10.           #include <linux/netfilter_ipv4.h>

  11.           /* This is the structure we shall use to register our function */
  12.           static struct nf_hook_ops nfho;

  13.           /* IP address we want to drop packets from, in NB order */
  14.           static unsigned char *drop_ip = "\x7f\x00\x00\x01";

  15.           /* This is the hook function itself */
  16.           unsigned int hook_func(unsigned int hooknum,
  17.                                  struct sk_buff **skb,
  18.                                  const struct net_device *in,
  19.                                  const struct net_device *out,
  20.                                  int (*okfn)(struct sk_buff *))
  21.           {
  22.               struct sk_buff *sb = *skb;

  23.               if (sb->nh.iph->saddr == drop_ip) {
  24.                   printk("Dropped packet from... %d.%d.%d.%d\n",
  25.                             *drop_ip, *(drop_ip + 1),
  26.                           *(drop_ip + 2), *(drop_ip + 3));
  27.                   return NF_DROP;
  28.               } else {
  29.                   return NF_ACCEPT;
  30.               }
  31.           }

  32.           /* Initialisation routine */
  33.           int init_module()
  34.           {
  35.               /* Fill in our hook structure */
  36.               nfho.hook     = hook_func;
  37.               /* Handler function */
  38.               nfho.hooknum  = NF_IP_PRE_ROUTING; /* First for IPv4 */
  39.               nfho.pf       = PF_INET;
  40.               nfho.priority = NF_IP_PRI_FIRST;   /* Make our func first */
  41.               nf_register_hook(&nfho);
  42.               return 0;
  43.           }
  44.               /* Cleanup routine */
  45.           void cleanup_module()
  46.           {
  47.               nf_unregister_hook(&nfho);
  48.           }
復(fù)制代碼


4.4 基于TCP端口的過(guò)濾
另外一個(gè)要執(zhí)行的簡(jiǎn)單的規(guī)則就是基于TCP目的端口的數(shù)據(jù)包過(guò)濾。這比檢驗(yàn)IP地址稍微復(fù)雜一點(diǎn),因?yàn)槲覀円约簞?chuàng)建一個(gè)指向TCP頭的指針。還記得前面關(guān)于傳輸層頭和網(wǎng)絡(luò)層頭所做的討論嗎?獲得一個(gè)TCP頭指針很簡(jiǎn)單,只需要申請(qǐng)一個(gè)指向tcphdr(定義在linux/tcp.h中)結(jié)構(gòu)體的指針,并將它指向包數(shù)據(jù)中的IP頭后面。或許一個(gè)例子就可以了。列表5展示了怎樣檢測(cè)一個(gè)數(shù)據(jù)包的TCP目的端口與我們想丟棄數(shù)據(jù)的指定端口是否一致。與列表3一樣,這段代碼也是從LWFW中拿出來(lái)的
列表5. 檢測(cè)接收到數(shù)據(jù)包的TCP目的端口
  1. unsigned char *deny_port = "\x00\x19";   /* port 25 */
  2.           ...
  3.           static int check_tcp_packet(struct sk_buff *skb)
  4.           {
  5.               struct tcphdr *thead;
  6.               /* We don't want any NULL pointers in the chain
  7.                * to the IP header. */
  8.               if (!skb ) return NF_ACCEPT;
  9.               if (!(skb->nh.iph)) return NF_ACCEPT;
  10.               /* Be sure this is a TCP packet first */
  11.               if (skb->nh.iph->protocol != IPPROTO_TCP) {
  12.                   return NF_ACCEPT;
  13.               }
  14.               thead = (struct tcphdr *)(skb->data  + (skb->nh.iph->ihl * 4));
  15.               /* Now check the destination port */
  16.               if ((thead->dest) == *(unsigned short *)deny_port) {
  17.                   return NF_DROP;
  18.               }   
  19.               return NF_ACCEPT;
  20.           }
復(fù)制代碼

世紀(jì)上非常簡(jiǎn)單。不要忘了deny_port是網(wǎng)絡(luò)字節(jié)序時(shí),這個(gè)函數(shù)才能工作。數(shù)據(jù)包過(guò)濾技術(shù)的基礎(chǔ)就是:對(duì)于一個(gè)特定的數(shù)據(jù)包,你必須對(duì)怎樣到達(dá)你想要的信息段的方法非常了解。下面,我們將進(jìn)入更有趣的世界。

[ 本帖最后由 duanjigang 于 2006-5-21 18:45 編輯 ]

souce.rar

39.03 KB, 下載次數(shù): 202

論壇徽章:
0
2 [報(bào)告]
發(fā)表于 2006-05-21 09:13 |只看該作者

Hacking the Linux Kernel Network Stack(譯本[chp5-end])

第五章  NetFilter鉤子其他可能的用法

在這里我將會(huì)就Netfilter在其它方面的更有趣的應(yīng)用給你作一些建議。在5.1我會(huì)給你提供一些思想源泉。5.2節(jié)將會(huì)討論并提供能運(yùn)行的代碼,這個(gè)代碼使一個(gè)基于內(nèi)核的FTP密碼嗅探器,能夠遠(yuǎn)程獲取密碼。事實(shí)上,它運(yùn)行的很好以至于我有些驚恐,所以將它寫(xiě)了出來(lái)。

5.1 隱藏后門(mén)守護(hù)進(jìn)程

內(nèi)核模塊編程實(shí)際上是Linux開(kāi)發(fā)最有意思的領(lǐng)域之一。在內(nèi)核中寫(xiě)代碼意味著你在一個(gè)只被你的想象力限制的地方寫(xiě)代碼。從惡意一點(diǎn)的觀點(diǎn)來(lái)思考,你可以隱藏一個(gè)文件,一個(gè)進(jìn)程,或者說(shuō)你能做任何rootkit能實(shí)現(xiàn)的很酷的事情;蛘哒f(shuō)從不太惡意(有這種觀點(diǎn)的人)的觀點(diǎn)來(lái)說(shuō),你可以隱藏文件,進(jìn)程,和各種各樣很酷的動(dòng)作,內(nèi)核真正是一個(gè)很迷人的地方。
        擁有一個(gè)內(nèi)核級(jí)的程序員所具有的所有能力,許多事情都是可能的;蛟S最有趣(對(duì)于系統(tǒng)管理員來(lái)說(shuō)這可是很恐怖的事情)的一件事情就是在內(nèi)核植入一個(gè)后門(mén)程序。畢竟,當(dāng)一個(gè)后門(mén)沒(méi)有作為進(jìn)程而運(yùn)行的時(shí)候,你怎么會(huì)知道它在運(yùn)行?當(dāng)然肯定存在一些可以使你的內(nèi)核能夠嗅到這些后門(mén)的方法,但是這些方法卻絕不會(huì)象運(yùn)行PS命令那樣的簡(jiǎn)單。將后門(mén)代碼植入內(nèi)核中并不是一個(gè)很新的話(huà)題。我這里要講的,卻是利用(你能夠猜到的)Netfilter鉤子植入簡(jiǎn)單的網(wǎng)絡(luò)服務(wù),將之作為內(nèi)核后門(mén)。
如果你有必要的技能并且愿意承擔(dān)在做實(shí)驗(yàn)時(shí)將你的內(nèi)核導(dǎo)致崩潰的風(fēng)險(xiǎn)的話(huà),你可以構(gòu)造一個(gè)簡(jiǎn)單而有用的網(wǎng)絡(luò)服務(wù),將能夠完全的裝入內(nèi)核并能進(jìn)行遠(yuǎn)程訪問(wèn);旧险f(shuō),Netfilter可以從所有接收到的數(shù)據(jù)包中查找指定的“神秘”數(shù)據(jù)包,當(dāng)這個(gè)神秘的數(shù)據(jù)包被接收到的時(shí)候,可以進(jìn)行一些特殊的處理。結(jié)果可以通過(guò)Netfilter鉤子函數(shù)發(fā)送出去,Netfilter鉤子函數(shù)然后返回一個(gè)NF_STOLEN結(jié)果以便這個(gè)神秘的數(shù)據(jù)包不會(huì)被繼續(xù)傳遞下去。但是必須注意一點(diǎn),以這樣的方式來(lái)發(fā)送輸出數(shù)據(jù)的時(shí)候,向外發(fā)送的數(shù)據(jù)包對(duì)于輸出Netfilter鉤子函數(shù)仍然是可見(jiàn)的。因此對(duì)于用戶(hù)空間來(lái)說(shuō),完全看不到這個(gè)“神秘”數(shù)據(jù)包曾經(jīng)來(lái)過(guò),但是他們卻能夠看到你發(fā)送出來(lái)的數(shù)據(jù)。你必須留意,泄密主機(jī)上的Sniffer程序不能發(fā)現(xiàn)這個(gè)數(shù)據(jù)包并不意味著中間的宿主機(jī)上的嗅探器(sniffer)也不能發(fā)現(xiàn)這個(gè)數(shù)據(jù)包。
Kossak和lifeline曾為Phrack雜志寫(xiě)過(guò)一篇精彩的文章,文中描述了如何通過(guò)注冊(cè)數(shù)據(jù)包類(lèi)型處理器的方法來(lái)坐這些事情。雖然這片文章是關(guān)于Netfilter鉤子的,我還是強(qiáng)烈建議你閱讀一下那片文章(Issue 55, file 12),這片文章非常有趣,向你展示了很多有趣的思想。
那么,后門(mén)的Netfilter鉤子到底能做哪種工作呢?好的,下面給出一些建議:
-------遠(yuǎn)程訪問(wèn)的擊鍵記錄器。模塊會(huì)記錄鍵盤(pán)的點(diǎn)擊并在遠(yuǎn)程客戶(hù)機(jī)發(fā)送一個(gè)Ping包的時(shí)候,將結(jié)果發(fā)送給客戶(hù)機(jī)。因此,一連串的擊鍵記錄信息流會(huì)被偽裝成穩(wěn)定的Ping包返回流發(fā)送回來(lái)。你也可以進(jìn)行簡(jiǎn)單的加密以便按鍵的ASC 值不會(huì)馬上暴露出來(lái),一些警覺(jué)的系統(tǒng)管理員回想:“堅(jiān)持,我以前都是通過(guò)SSH會(huì)話(huà)來(lái)鍵入這些的,Oh $%@T%&!”
--------簡(jiǎn)單的管理任務(wù),例如獲取機(jī)器當(dāng)前的登錄用戶(hù)列表,或者獲取打開(kāi)的網(wǎng)絡(luò)連接信息。
--------一個(gè)并非真正的后門(mén),而是位于網(wǎng)絡(luò)邊界的模塊,并且阻擋任何被疑為來(lái)自特洛伊木馬、ICMP隱蔽通道或者像KaZaa這樣的文件共享工具的通信。
--------文件傳輸服務(wù)器。我最近已經(jīng)實(shí)現(xiàn)了這個(gè)想法。最終得到的Linux內(nèi)核模塊會(huì)給你帶來(lái)數(shù)小時(shí)的愉悅。
--------數(shù)據(jù)包跳躍。將發(fā)送到裝有后門(mén)程序主機(jī)的特定端口的數(shù)據(jù)重新定向到另外一個(gè)IP主機(jī)的不同端口。并且將這個(gè)客戶(hù)端發(fā)送的數(shù)據(jù)包返回給發(fā)起者。沒(méi)有創(chuàng)建進(jìn)程,最妙的是,沒(méi)有打開(kāi)網(wǎng)絡(luò)套接字。
--------利用上面說(shuō)到的數(shù)據(jù)包跳躍技術(shù)已以一種半傳輸?shù)姆绞綄?shí)現(xiàn)與網(wǎng)絡(luò)上關(guān)鍵系統(tǒng)的交互。例如配置路由等。
--------FTP/POP3/Telnet的密碼嗅探器。嗅探向外發(fā)送的密碼并保存起來(lái),直到神秘?cái)?shù)據(jù)包到來(lái)所要這些信息的時(shí)候,就將它發(fā)送出去。
好了,上面是一些簡(jiǎn)單的思想列表。最后一個(gè)想法將會(huì)在下一節(jié)中進(jìn)行詳細(xì)的介紹,因?yàn)檫@一節(jié)為讀者提供了一個(gè)很好的機(jī)會(huì),使得我們能夠接觸更多的內(nèi)核內(nèi)部的網(wǎng)段絡(luò)代碼。

5.2 基于內(nèi)核的FTP密碼獲取Sniffer

針對(duì)前面談到的概念,這里給出了一個(gè)例證—一個(gè)后門(mén)Netfilter程序。這個(gè)模塊嗅探流向服務(wù)器的外出的FTP數(shù)據(jù)包,尋找USER和PASSWD命令對(duì),當(dāng)獲取到一對(duì)用戶(hù)名和密碼時(shí),模塊就會(huì)等待一個(gè)神秘的并且有足夠大空間能存儲(chǔ)用戶(hù)名和密碼的ICMP包(Ping包)的到來(lái),收到這個(gè)包后,模塊會(huì)將用戶(hù)名和密碼返回。很快的發(fā)送一個(gè)神秘的數(shù)據(jù)包,獲取回復(fù)并且打印信息。一旦一對(duì)用戶(hù)名和密碼從模塊中讀走都,模塊便會(huì)開(kāi)始下一對(duì)數(shù)據(jù)的嗅探。注意模塊平時(shí)最多能存儲(chǔ)一對(duì)信息。已經(jīng)大致介紹過(guò)了,我們現(xiàn)在對(duì)模塊具體怎樣工作進(jìn)行詳盡的講解。當(dāng)模塊被加載的時(shí)候,init_module()函數(shù)簡(jiǎn)單的注冊(cè)兩個(gè)Netfilter鉤子。第一個(gè)鉤子負(fù)責(zé)從進(jìn)入的數(shù)據(jù)包(在NF_IP_PRE_ROUTING時(shí)機(jī)調(diào)用)中尋找神秘的ICMP數(shù)據(jù)包。另外一個(gè)負(fù)責(zé)監(jiān)視離開(kāi)(在NF_IP_POST_ROUTING時(shí)調(diào)用)安裝本模塊的機(jī)器的數(shù)據(jù)包。在這里尋找和俘獲FTP的登錄用戶(hù)名和密碼,cleanup_module()負(fù)責(zé)注銷(xiāo)這兩個(gè)鉤子。
        watch_out()函數(shù)是在NF_IP_POST_ROUTING時(shí)調(diào)用的鉤子函數(shù)?匆幌逻@個(gè)函數(shù)你就會(huì)發(fā)現(xiàn)它的動(dòng)作很簡(jiǎn)單。當(dāng)一個(gè)數(shù)據(jù)包進(jìn)入的時(shí)候,它會(huì)被經(jīng)過(guò)多重的檢測(cè)以便確認(rèn)這個(gè)數(shù)據(jù)包是否是一個(gè)FTP數(shù)據(jù)包。如果不是一個(gè)FTP數(shù)據(jù)包,將會(huì)立即返回一個(gè)NF_ACCEPT。如果是一個(gè)FTP數(shù)據(jù)包,模塊會(huì)確認(rèn)是否已經(jīng)獲取并存儲(chǔ)了一對(duì)用戶(hù)名和密碼。如果已經(jīng)存儲(chǔ)了的話(huà)(這時(shí) have_pari變量的值非零),那么就會(huì)返回一個(gè)NF_ACCPET值,并且數(shù)據(jù)包最終可以離開(kāi)這個(gè)系統(tǒng)。否則的話(huà),check_ftp()方法將會(huì)被調(diào)用。通常在這里密碼被提取出來(lái),如果以前沒(méi)有接收到數(shù)據(jù)包的話(huà),target_ip和target_port這兩個(gè)變量將會(huì)被清空。
        Check_ftp()一開(kāi)始在數(shù)據(jù)段的開(kāi)頭尋找“USER”,“PASS”或者“QUIT”字段。注意,在沒(méi)有“USER”字段被處理之前通常不處理“PASS”字段。這是為了防止在收到密碼后連接斷開(kāi),而這時(shí)沒(méi)有獲取到用戶(hù)名,就會(huì)陷入鎖中。同樣,當(dāng)收到一個(gè)“QUIT”字段時(shí),如果這時(shí)只有一個(gè)“USER”字段的話(huà),就將所有變量復(fù)位,以便于Sniffer能繼續(xù)對(duì)新的連接進(jìn)行嗅探。當(dāng)“PASS”或者“USER”命令被收到時(shí),在必要的完整性校驗(yàn)之后,命令的參數(shù)會(huì)被拷貝下來(lái)。通常操作中都是在check_ftp()函數(shù)結(jié)束之前,檢驗(yàn)有無(wú)用戶(hù)名和密碼者兩個(gè)命令字段。如果有的話(huà),have_pair會(huì)被設(shè)置,并且在這對(duì)數(shù)據(jù)被取走之前不會(huì)再次獲取新的用戶(hù)名和密碼。
        到目前為止你已經(jīng)知道了這個(gè)模塊怎樣安裝自己并且查找用戶(hù)名和密碼并記錄下來(lái)。下面你將會(huì)看到“神秘”數(shù)據(jù)包到來(lái)時(shí)會(huì)發(fā)生什么。在這塊兒要特別留意,因?yàn)殚_(kāi)發(fā)中的大多數(shù)問(wèn)題會(huì)在此處出現(xiàn)。如果沒(méi)有記錯(cuò)的話(huà),我在這里遇到了16個(gè)內(nèi)核錯(cuò)誤。當(dāng)數(shù)據(jù)到達(dá)安裝此模塊的機(jī)器時(shí),watch_in()將會(huì)檢查每一個(gè)數(shù)據(jù)包看他是否是一個(gè)神秘的數(shù)據(jù)包。如果數(shù)據(jù)包沒(méi)有滿(mǎn)足被判定為神秘?cái)?shù)據(jù)包的條件的話(huà),watch_in()會(huì)簡(jiǎn)單的返回一個(gè)NF_ACCEPT來(lái)忽略這個(gè)數(shù)據(jù)包。注意,神秘?cái)?shù)據(jù)包的判定標(biāo)準(zhǔn)就是這個(gè)數(shù)據(jù)包有足夠的空間能夠容納IP地址,用戶(hù)名和密碼這些字符串。這樣做是為了使得數(shù)據(jù)的回復(fù)更容易些?赡苄枰暾(qǐng)一個(gè)新的sk_buff結(jié)構(gòu)體。但是要保證所有的數(shù)據(jù)域都正確卻是件不容易的事情,所以你必須想辦法確保這些域的鍵值正確無(wú)誤。因此,我們?cè)诖瞬⒉粍?chuàng)建一個(gè)新的結(jié)構(gòu)體,而是直接修改請(qǐng)求數(shù)據(jù)包的結(jié)構(gòu),將其作為一個(gè)返回?cái)?shù)據(jù)包。為了能正確返回,需要做幾個(gè)修改。首先,IP地址進(jìn)行交換,結(jié)構(gòu)體(sk_buff)中的數(shù)據(jù)包類(lèi)型這個(gè)域的值要改為“PACKET_OUTGOING”,這個(gè)在linux/if_packet.h中定義了。第二步要確保每個(gè)鏈路層信息已經(jīng)被包含在其中。我們接收到數(shù)據(jù)包的數(shù)據(jù)域就是鏈路層頭信息后面的指向sk_buff結(jié)構(gòu)體的指針,并且指向數(shù)據(jù)包中數(shù)據(jù)開(kāi)頭的指針傳遞了數(shù)據(jù)域。所以,對(duì)于需要鏈路層頭信息的接口(以太網(wǎng)卡,回環(huán)設(shè)備和點(diǎn)對(duì)點(diǎn)設(shè)備的原始套結(jié)字)而言,我們的數(shù)據(jù)域指向mac.ethernet或者mac.raw結(jié)構(gòu)。你可以通過(guò)檢測(cè)sb->dev->type的值(sb是指向sk_buff結(jié)構(gòu)體的指針)的值來(lái)判斷這個(gè)數(shù)據(jù)包進(jìn)入了什么類(lèi)型的接口。你可以在linux/ip_arp.h中找到這些有效的值。最有用的都在表三列了出來(lái)。

表三.常見(jiàn)接口(網(wǎng)卡)類(lèi)型
  1. 類(lèi)型碼        接口類(lèi)型
  2. ARPHRD_ETHER        以太網(wǎng)卡
  3. ARPHRD_LOOPBACK        回環(huán)設(shè)備
  4. ARPHRD_PPP        點(diǎn)對(duì)點(diǎn)設(shè)備
復(fù)制代碼


要做的最后一件事就是把我們要發(fā)送的數(shù)據(jù)包拷貝到返回的消息里面去,然后就該發(fā)送數(shù)據(jù)包了。函數(shù)dev_queue_xmit()使用一個(gè)指向sk_buff結(jié)構(gòu)體的指針作為唯一的參數(shù),在發(fā)送明顯失敗時(shí)返回一個(gè)負(fù)的錯(cuò)誤碼(一個(gè)負(fù)值)。這里“明顯”的失敗指什么呢?這樣的,如果你給這個(gè)函數(shù)一個(gè)構(gòu)造的壞的套接字緩沖,你并不會(huì)得到一個(gè)明顯的失敗。當(dāng)出現(xiàn)內(nèi)核錯(cuò)誤或者內(nèi)核棧溢出時(shí)就產(chǎn)生了一個(gè)明顯的失敗。這下知道錯(cuò)誤怎樣被劃分為兩類(lèi)了吧?最后watch_in()返回一個(gè)NF_STOLEN告訴Netfilter讓它忘記曾經(jīng)看幾過(guò)這個(gè)數(shù)據(jù)包。在調(diào)用dev_queue_xmit()時(shí)不要返回NF_DROP!如果你這樣做了,你很快會(huì)得到一個(gè)骯臟的內(nèi)核錯(cuò)誤。因?yàn)閐ev_queue_xmit()會(huì)釋放掉傳遞進(jìn)去的套接字緩沖區(qū),而Netfilter卻會(huì)嘗試去對(duì)已經(jīng)釋放掉的數(shù)據(jù)包做相同的事情。好了,代碼的討論已經(jīng)足夠了,現(xiàn)在是看代碼的時(shí)候了。
5.2.1 nsniffer 的代碼
代碼超過(guò)發(fā)貼上限,見(jiàn)附件
5.2.2 getpass.c 代碼
代碼超過(guò)發(fā)貼上限,見(jiàn)附件

第六章  在Libpcap中隱藏網(wǎng)絡(luò)通訊

6.1 SOCK_PACKET, SOCK_RAW 和Libpcap

系統(tǒng)管理員經(jīng)常用到的一些軟件可“數(shù)據(jù)包嗅探器”這個(gè)標(biāo)題進(jìn)行分類(lèi)。最普通的用于一般目的的數(shù)據(jù)包嗅探器有
Tcpdump(1)和Ethreal(1)。這兩個(gè)應(yīng)用都是利用了libpcap這個(gè)庫(kù)來(lái)獲取原始套結(jié)字的數(shù)據(jù)包。網(wǎng)絡(luò)入侵檢測(cè)系統(tǒng)(NetWork Intrusion Detection System NIDS)也利用了libpcap這個(gè)庫(kù)。SNORT也需要libpcap, Libnids----一個(gè)提供IP重組和TCP流跟蹤的NIDS開(kāi)發(fā)庫(kù)(參見(jiàn)參考文獻(xiàn)[2]),也是如此。
在一臺(tái)Linux系統(tǒng)上,libpcap利用SOCK_PACKET接口。Packet套結(jié)字是一種能夠在鏈路層接收和發(fā)送數(shù)據(jù)包的特殊套結(jié)字。關(guān)于packet套結(jié)字和它的用途可以說(shuō)一大堆東西,但是本文是從它們當(dāng)中隱藏而不是講述如何利用它們的。感興趣的讀者可以從packet(7)的man手冊(cè)中了解到更詳細(xì)的信息。在此處。我們只需要知道packet套結(jié)字能夠被libpcap用來(lái)從機(jī)器上的原始套結(jié)字中獲取進(jìn)入的和發(fā)送的數(shù)據(jù)。
當(dāng)內(nèi)核的網(wǎng)絡(luò)堆棧收到一個(gè)數(shù)據(jù)包時(shí),要對(duì)其進(jìn)行一定的校驗(yàn)以便確定是否有packet套結(jié)字對(duì)它感興趣。如果有的話(huà),這個(gè)數(shù)據(jù)包就被分發(fā)給對(duì)它感興趣的套結(jié)字。如果沒(méi)有的話(huà),這個(gè)數(shù)據(jù)包繼續(xù)流向TCP層,UDP層,或者其它的真正目的地。對(duì)于SOCKET_RAW型的套結(jié)字也是這樣的情形。SOCKET_RAW非常類(lèi)似于SOCKET_PACKET型的套結(jié)字,區(qū)別就在于SOCKET_RAW不提供鏈路層的頭信息。我在附錄[3]中的SYNalert就是SOCKET_RAW利用的一個(gè)例子。
現(xiàn)在你應(yīng)該知道Linux系統(tǒng)上的數(shù)據(jù)包嗅探軟件都是利用libpcap庫(kù)了吧。Libpcap在Linux上利用PACKET_SOCKET接口從鏈路層獲取原始套結(jié)字?jǐn)?shù)據(jù)包。原始套結(jié)字可以在用戶(hù)空間被用來(lái)從IP頭中獲取所有的數(shù)據(jù)包。下一段將會(huì)講述一個(gè)Linux內(nèi)核模塊(LKM)怎樣從數(shù)據(jù)包中或者SOCKET_RAW套結(jié)字接口中隱藏一個(gè)網(wǎng)絡(luò)傳輸。

6.2 給狼披上羊皮
(這個(gè)譯法借鑒于參考譯文)

當(dāng)一個(gè)數(shù)據(jù)包被接收到并發(fā)送給一個(gè)packet套結(jié)字時(shí),packet_rcv()函數(shù)會(huì)被調(diào)用。可以在net/packet/af_packet.c中找到這個(gè)函數(shù)的源代碼。packet_rcv()負(fù)責(zé)使數(shù)據(jù)通過(guò)所有可能應(yīng)用于數(shù)據(jù)目的地的Netfilter,最終將數(shù)據(jù)投遞到用戶(hù)空間。為了從PACKET中隱藏?cái)?shù)據(jù)包,我們需要設(shè)法讓packet_rcv()對(duì)于一些特定的數(shù)據(jù)包一點(diǎn)也不調(diào)用。我們?cè)鯓訉?shí)現(xiàn)這個(gè)?當(dāng)然是優(yōu)秀的ol式的函數(shù)劫持了。
函數(shù)劫持的基本操作是:如果我們知道一個(gè)內(nèi)核函數(shù),甚至是那些沒(méi)有被導(dǎo)出的函數(shù)的入口地址,我們可以在實(shí)際的代碼運(yùn)行前將這個(gè)函數(shù)重定位到其他的位置。為了達(dá)到這樣的目的,我們首先要從這個(gè)函數(shù)的開(kāi)始,保存其原來(lái)的指令字節(jié),然后將它們換成跳轉(zhuǎn)到我們的代碼處執(zhí)行的絕對(duì)跳轉(zhuǎn)指令。例如以i386匯編語(yǔ)言實(shí)現(xiàn)該操作如下:
  1. movl  (address of our function),  %eax
  2.         jmp   *eax
復(fù)制代碼

這些指令產(chǎn)生的16進(jìn)制代碼如下(假設(shè)函數(shù)地址為0):

  1. 0xb8 0x00 0x00 0x00 0x00
  2.     0xff 0xe0
復(fù)制代碼

如果我們?cè)贚inux核心模塊的初始化時(shí)將上例中的函數(shù)地址替換為我們的鉤子函數(shù)的地址,就可以使我們的鉤子函數(shù)先運(yùn)行。當(dāng)我們想運(yùn)行原來(lái)的函數(shù)時(shí),只需要在開(kāi)始時(shí)恢復(fù)函數(shù)原來(lái)的指令,調(diào)用該函數(shù)并且替換我們的劫持代碼。簡(jiǎn)單而有效。Silvio Cesare 不久前寫(xiě)過(guò)一篇文章,講述如何實(shí)現(xiàn)內(nèi)核函數(shù)劫持,參見(jiàn)參考文獻(xiàn)[4]。
要從packet套接字隱藏?cái)?shù)據(jù)包,我們首先要寫(xiě)一個(gè)鉤子函數(shù),用來(lái)檢查這個(gè)數(shù)據(jù)包是否滿(mǎn)足被隱藏的標(biāo)準(zhǔn)。如果滿(mǎn)足,鉤子函數(shù)簡(jiǎn)單的向它的調(diào)用者返回一個(gè)0,這樣packet_rcv()函數(shù)也就不會(huì)被調(diào)用。如果packet_rcv()函數(shù)不被調(diào)用,那么這個(gè)數(shù)據(jù)包就不會(huì)遞交給用戶(hù)空間的packet套接字。注意,只是對(duì)于"packet"套接字來(lái)說(shuō),該數(shù)據(jù)包被丟棄了。如果我們要過(guò)濾送到packet套接字的FTP數(shù)據(jù)包,那么FTP服務(wù)器的TCP套接字仍然能收到這些數(shù)據(jù)包。我們所做的一切只是使運(yùn)行在本機(jī)上的嗅探軟件無(wú)法看到這些數(shù)據(jù)包。FTP服務(wù)器仍然能夠處理和記錄連接。
   
    理論上大致就這么多了,關(guān)于原始套接字的用法同理可得。不同的是我們需要鉤子的是raw_rcv()函數(shù)(在net/ipv4/raw.c中可以找到)。下一節(jié)將給出并討論一個(gè)Linux核心模塊的示例代碼,該代碼劫持packet_rcv()函數(shù)和raw_rcv()函數(shù),隱藏任何來(lái)自或去往指定的IP地址的數(shù)據(jù)包。
第七章  結(jié)束語(yǔ)
希望到現(xiàn)在為止,你對(duì)于什么是Netfilter,怎樣使用Netfilter,可以對(duì)Netfilter做些什么已經(jīng)有了一個(gè)基本的了解。你應(yīng)該也具有了在本地機(jī)器上將一些特定的網(wǎng)絡(luò)傳輸從運(yùn)行在這些機(jī)器上的嗅探型軟件中隱藏的知識(shí)了。如果你想要關(guān)于這方面的壓縮包的話(huà),可以直接給我發(fā)送E-mail郵件。我會(huì)為你做的任何修改,注釋和建議而感激。現(xiàn)在,我就把這些有趣的東西留給你,你可以自由發(fā)揮自己的想象力。

附錄A 輕量級(jí)防火墻
A.1 縱覽
輕量級(jí)防火墻(Light weight fire wall ,LWFW)是一個(gè)簡(jiǎn)單的內(nèi)核模塊,它演示了第四章介紹的基本的數(shù)據(jù)包過(guò)濾技術(shù)。LWFW并通過(guò)系統(tǒng)調(diào)用ioctl提供了一個(gè)控制接口。
由于LWFW已經(jīng)有了足夠多的文檔,所以我在此只就它怎么工作進(jìn)行簡(jiǎn)單的概述。當(dāng)LWFW模塊被安裝時(shí),第一個(gè)任務(wù)就是嘗試去注冊(cè)一個(gè)控制設(shè)備。注意,在針對(duì)于LWFW的ioctl接口能夠使用之前,需要在/dev目錄下建立一個(gè)字符設(shè)備文件,如果這個(gè)控制設(shè)備注冊(cè)成功的話(huà),“in use”標(biāo)識(shí)符將被清空,為NF_IP_PRE_ROUTE注冊(cè)的鉤子函數(shù)也就注冊(cè)上了。clean_up函數(shù)做一些與此過(guò)程相反的事情。
LWFW提供了三個(gè)丟棄數(shù)據(jù)包的判定條件,它們按照處理的順序依次是:
-----源接口(網(wǎng)卡名,如“eth0”,“eth0:1”等)
------源IP地址(如“10.0.1.4”,“192.168.1.1”等)
------目的TCP端口號(hào)(如ssh常用的22,F(xiàn)TP常用的19)
這些規(guī)則的具體設(shè)定是通過(guò)ioctl接口來(lái)實(shí)現(xiàn)的。當(dāng)一個(gè)數(shù)據(jù)包到來(lái)時(shí),LWFW會(huì)根據(jù)設(shè)定好的規(guī)則對(duì)這些數(shù)據(jù)包進(jìn)行檢測(cè)。如果某個(gè)數(shù)據(jù)包符合其中的任何一個(gè)規(guī)則,那么鉤子函數(shù)將返回一個(gè)NF_DROP結(jié)果,從而Netfilter就會(huì)默默地丟棄這個(gè)數(shù)據(jù)包。負(fù)責(zé)的話(huà),鉤子函數(shù)會(huì)返回一個(gè)NF_ACCEPT結(jié)果,這個(gè)數(shù)據(jù)包就會(huì)繼續(xù)它的旅途。
最后一個(gè)需要提到的就是LWFW的統(tǒng)計(jì)記錄。任何一個(gè)數(shù)據(jù)包到達(dá)鉤子函數(shù)時(shí),只要LWFW是活躍的,那么看到的數(shù)據(jù)包總數(shù)目將會(huì)增加。單個(gè)的規(guī)則校驗(yàn)函數(shù)負(fù)責(zé)增加由于符合此項(xiàng)規(guī)則而丟棄的數(shù)據(jù)包數(shù)目。需要注意的就是,當(dāng)某個(gè)規(guī)則的內(nèi)容變化時(shí),這個(gè)規(guī)則對(duì)應(yīng)的丟棄數(shù)據(jù)包總數(shù)也會(huì)被清零。Lwfwstats函數(shù)利用IOCTL的LWFW_GET_STATS命令獲取statistics結(jié)構(gòu)體的一份拷貝值,并顯示它的內(nèi)容。

A.2 源代碼 lwfw.c
見(jiàn)附件
A.3 lwfw.h,Makefile
見(jiàn)附件
A.4 譯者添加的測(cè)試程序
下面是譯者自己在學(xué)習(xí)時(shí)寫(xiě)的一個(gè)對(duì)LWFW的過(guò)濾規(guī)則進(jìn)行設(shè)置和改動(dòng)的例子,你也可以對(duì)此段代碼進(jìn)行修改,當(dāng)模塊成功加載之后,建立一個(gè)字符設(shè)備文件,然后這個(gè)程序就能運(yùn)行了。
  1. /*
  2. Name: test.c
  3. Author: duanjigang<duanjigang1983@gmail.com>
  4. Date: 2006-5-15
  5. */
  6. #include<sys/types.h>
  7. #include<unistd.h>
  8. #include<fcntl.h>
  9. #include<linux/rtc.h>
  10. #include<linux/ioctl.h>
  11. #include "lwfw.h"
  12. main()
  13. {
  14.         int fd;
  15.         int i;
  16.         struct lwfw_stats data;
  17.         int retval;
  18.         char msg[128];
  19.         /*來(lái)自這個(gè)IP地址的數(shù)據(jù)將被丟棄*/
  20. char * deny_ip = "192.168.1.105";
  21.         /*這個(gè)接口發(fā)出的數(shù)據(jù)將被丟棄,無(wú)法外流*/
  22. char *ifcfg = "eth0";
  23.         /*要禁止的TCP目的端口22, ssh的默認(rèn)端口*/
  24. unsigned char *  port = "\x00\x16";
  25.         /*打開(kāi)設(shè)備文件*/
  26. fd = open(LWFW_NAME, O_RDONLY);
  27.         if(fd == -1)
  28.            {
  29.           perror("open fail!");
  30.           exit(-1);
  31.         }
  32.         /*激活LWFW,設(shè)置標(biāo)志位*/
  33. if( ioctl(fd,LWFW_ACTIVATE,0) == -1 )
  34.         {
  35.              perror("ioctl LWFW_ACTIVATE fail!\n");
  36.              exit(-1);
  37.         }
  38.      /*設(shè)置禁止IP*/   
  39. if( ioctl(fd, LWFW_DENY_IP, inet_addr(deny_ip)) == -1)
  40.          {
  41.             printf("ioctl LWFW_DENY_IP fail\n");
  42.             exit(-1);
  43.          }
  44.      /*設(shè)置禁止端口*/   
  45. if(ioctl(fd, LWFW_DENY_PORT, *(unsigned short *)port) == -1)
  46.          {
  47.            printf("ioctl LWFW_DENY_PORT fail!\n");
  48.            exit(-1);
  49.          }
  50.          /*獲取數(shù)據(jù),這應(yīng)該是一段時(shí)間之后的事,此處直接獲取,不妥*/
  51.         if( ioctl(fd, LWFW_GET_STATS,*(unsigned long*)&data) == -1)
  52.          {
  53.             printf("iotcl LWFW_GET_STATS fail!\n");
  54.             exit(-1);
  55.          }
  56.         /*
  57.         禁用這個(gè)接口
  58.        if(ioctl(fd, LWFW_DENY_IF, (unsigned*)ifcfg) == -1)
  59.          {
  60.                printf("ioctl LWFW_DENY_IF fail!\n");
  61.                exit(-1);
  62.          }
  63.          */
  64.          printf("ip dropped : %d\n", data.ip_dropped);
  65.          printf("if dropped : %d\n", data.if_dropped);
  66.          printf("tcp dropped : %d\n", data.tcp_dropped);
  67.          printf("total dropped : %d\n", data.total_dropped);
  68.          printf("total seen: %d\n", data.total_seen);
  69.          close(fd);
  70. }
復(fù)制代碼

附錄B  第六部分的代碼
這里是一個(gè)簡(jiǎn)單的模塊,在這個(gè)模塊中將對(duì)packet_rcv()函數(shù)和raw_rcv()函數(shù)進(jìn)行替換,從而隱藏到達(dá)或者離開(kāi)我們指定所IP地址的數(shù)據(jù)包。默認(rèn)的IP是“127.0.0.1”,但是,可以通過(guò)修改#define IP 來(lái)改動(dòng)這個(gè)值。同樣提供了一個(gè)bash的腳本,負(fù)責(zé)從Sytem.map文件中獲取所需函數(shù)的地址,并且負(fù)責(zé)模塊的插入,在插入模塊時(shí),以所需的格式將這些函數(shù)的地址傳遞給內(nèi)核。這個(gè)加載腳本是grem寫(xiě)的。原來(lái)是為我的mod-off項(xiàng)目而寫(xiě),經(jīng)過(guò)簡(jiǎn)單的修改就能用于這里的模塊,再次感謝grem。
這里給出的模塊只是原理性的代碼,沒(méi)有任何模塊隱藏的方法。有很重要的一點(diǎn)需要記住,盡管這個(gè)模塊能夠從運(yùn)行于同一臺(tái)機(jī)子上的嗅探器中隱藏指定的傳輸,但是,位于同一個(gè)網(wǎng)段上的其他機(jī)子上的嗅探器仍然能夠看到這些數(shù)據(jù)包?戳诉@個(gè)模塊,精干的讀者很快就能設(shè)計(jì)一些Netfilter鉤子函數(shù)來(lái)阻斷任何一種想要阻斷的數(shù)據(jù)包。我就利用本文中提到的技術(shù)成功地在其它內(nèi)核模塊項(xiàng)目中實(shí)現(xiàn)了對(duì)控制和信息獲取數(shù)據(jù)包的隱藏。
(此處代碼見(jiàn)附件)

[參考文獻(xiàn)]:
[1]  The tcpdump group
      http://www.tcpdump.org
[2]  The Packet Factory
      http://www.packetfactory.net
[3]  My network tools page -
      http://uqconnect.net/~zzoklan/software/#net_tools
[4]  Silvio Cesare's Kernel Function Hijacking article
      http://vx.netlux.org/lib/vsc08.html
[5]  Man pages for:
    - raw (7)
    - packet (7)
    - tcpdump (1)
[6]  Linux kernel source files. In particular:
    - net/packet/af_packet.c     (for  packet_rcv())
    - net/ipv4/raw.c             (for  raw_rcv())
    - net/core/dev.c
    - net/ipv4/netfilter/*
[7] Harald Welte's Journey of a packet through the Linux 2.4 network
     stack
     http://gnumonks.org/ftp/pub/doc/packet-journey-2.4.html
[8] The Netfilter documentation page
     http://www.netfilter.org/documentation
[9] Phrack 55 - File 12 -
     http://www.phrack.org/show.php?p=55&a=12
[A] Linux Device Drivers 2nd Ed. by Alessandro Rubini et al.
[B] Inside the Linux Packet Filter. A Linux Journal article
     http://www.linuxjournal.com/article.php?sid=4852

[ 本帖最后由 duanjigang 于 2006-5-21 09:43 編輯 ]

souce.rar

39.03 KB, 下載次數(shù): 196

論壇徽章:
0
3 [報(bào)告]
發(fā)表于 2006-05-21 09:55 |只看該作者
好文,支持樓主

論壇徽章:
0
4 [報(bào)告]
發(fā)表于 2006-05-21 10:35 |只看該作者

  1. movl  (address of our function),  %eax
  2. jmp   *eax
復(fù)制代碼

這樣強(qiáng)行跳轉(zhuǎn)后,當(dāng)那個(gè) fun 執(zhí)行完畢,程序指針 IP 返回到哪里呢?
masm 中調(diào)用 fun 用的是 call,如果非要用 jmp 的話(huà),jmp 前要 push 一個(gè)返回地址的,因?yàn)?fun 最后會(huì)有一個(gè) ret 或 retf

論壇徽章:
0
5 [報(bào)告]
發(fā)表于 2006-05-26 17:56 |只看該作者
原帖由 duanjigang 于 2006-5-20 22:27 發(fā)表
應(yīng)當(dāng)注意,       傳輸層的頭和網(wǎng)絡(luò)層的頭極有可能在內(nèi)存中指向相同的內(nèi)存單元。   在TCP數(shù)據(jù)包中也是這樣的情況,h和nh都是指向IP頭結(jié)構(gòu)體的指針。這就意味著,如果認(rèn)為h->th指向TCP頭,從而想通過(guò)h->th來(lái)獲取一個(gè)值的話(huà),將會(huì)導(dǎo)致錯(cuò)誤發(fā)生。因?yàn)閔->th實(shí)際指向IP頭,等同于nh->iph。


傳輸層的頭和網(wǎng)絡(luò)層的頭極有可能在內(nèi)存中指向相同的內(nèi)存單元。

這是為什么呢!

論壇徽章:
0
6 [報(bào)告]
發(fā)表于 2006-05-26 19:20 |只看該作者

感謝

請(qǐng)問(wèn)樓主,這個(gè)原文我咋搜不到呢。
能給個(gè)聯(lián)接嗎?

論壇徽章:
0
7 [報(bào)告]
發(fā)表于 2006-05-28 11:50 |只看該作者
原帖由 chunhui_true 于 2006-5-26 19:20 發(fā)表
請(qǐng)問(wèn)樓主,這個(gè)原文我咋搜不到呢。
能給個(gè)聯(lián)接嗎?

http://www.phrack.org/show.php?p=61&a=1
這是phrack主頁(yè)上的原文。
今天正好看到一篇關(guān)于第二章內(nèi)容更詳盡的說(shuō)明,順便貼出來(lái),希望能促進(jìn)本文的理解:
IPv6協(xié)議定義了五個(gè)鉤子:

  1. 1. NF_IP6_PRE_ROUTING 0:數(shù)據(jù)包在抵達(dá)路由之前經(jīng)過(guò)這個(gè)鉤子。目前,在這個(gè)鉤子上只對(duì)數(shù)據(jù)包作包頭檢測(cè)處理,一般應(yīng)用于防止拒絕服務(wù)攻擊和NAT;
  2. 2. NF_IP6_LOCAL_IN 1:目的地為本地主機(jī)的數(shù)據(jù)包經(jīng)過(guò)這個(gè)鉤子。防火墻一般建立在這個(gè)鉤子上;
  3. 3. NF_IP6_FORWARD 2:目的地非本地主機(jī)的數(shù)據(jù)包經(jīng)過(guò)這個(gè)鉤子;
  4. 4. NF_IP6_LOCAL_OUT 3:本地主機(jī)發(fā)出的數(shù)據(jù)包經(jīng)過(guò)這個(gè)鉤子;
  5. 5. NF_IP6_POST_ROUTING 4:數(shù)據(jù)包在離開(kāi)本地主機(jī)之前經(jīng)過(guò)這個(gè)鉤子,包括源地址為本地主機(jī)和非本地主機(jī)的。
復(fù)制代碼

我們分析數(shù)據(jù)報(bào)經(jīng)過(guò)Netfilter機(jī)制的過(guò)程。數(shù)據(jù)報(bào)進(jìn)入系統(tǒng)后,進(jìn)行IP校驗(yàn)以后,數(shù)據(jù)報(bào)經(jīng)過(guò)第一個(gè)鉤子NF_IP6_PRE_ROUTING注冊(cè)函數(shù)進(jìn)行處理;然后就進(jìn)入路由代碼,其決定該數(shù)據(jù)包是需要轉(zhuǎn)發(fā)還是發(fā)給本機(jī)的;若該數(shù)據(jù)包是發(fā)被本機(jī)的,則該數(shù)據(jù)經(jīng)過(guò)鉤子NF_IP6_LOCAL_IN注冊(cè)函數(shù)處理以后然后傳遞給上層協(xié)議;若該數(shù)據(jù)包應(yīng)該被轉(zhuǎn)發(fā)則它被NF_IP6_FORWARD注冊(cè)函數(shù)處理;經(jīng)過(guò)轉(zhuǎn)發(fā)的數(shù)據(jù)報(bào)經(jīng)過(guò)最后一個(gè)鉤子NF_IP6_POST_ROUTING注冊(cè)函數(shù)處理以后,再傳輸?shù)骄W(wǎng)絡(luò)上。
本地產(chǎn)生的數(shù)據(jù)經(jīng)過(guò)鉤子函數(shù)NF_IP6_LOCAL_OUT注冊(cè)函數(shù)處理以后,進(jìn)行路由選擇處理,然后經(jīng)過(guò)NF_IP6_POST_ROUTING注冊(cè)函數(shù)處理以后發(fā)送到網(wǎng)絡(luò)上

[ 本帖最后由 duanjigang 于 2006-5-28 17:24 編輯 ]

論壇徽章:
0
8 [報(bào)告]
發(fā)表于 2006-05-28 20:54 |只看該作者
好文 。。

論壇徽章:
0
9 [報(bào)告]
發(fā)表于 2006-05-28 22:27 |只看該作者
原帖由 Scorpioo 于 2006-5-26 17:56 發(fā)表


傳輸層的頭和網(wǎng)絡(luò)層的頭極有可能在內(nèi)存中指向相同的內(nèi)存單元。

這是為什么呢!

當(dāng)初自己在讀到此處的時(shí)候也沒(méi)有仔細(xì)想,多謝你提出這個(gè)問(wèn)題,今天翻了半天資料,自己理解了一點(diǎn),不知道正確不,因?yàn)閷?duì)于協(xié)議棧的細(xì)節(jié)不清楚,所以只能做一些膚淺的認(rèn)識(shí),日后有機(jī)會(huì)再改正此處可的得錯(cuò)誤。
sk_buff是一個(gè)控制結(jié)構(gòu),通過(guò)它,才可以訪問(wèn)網(wǎng)絡(luò)報(bào)文里的各種數(shù)
據(jù)。所以在分配網(wǎng)絡(luò)報(bào)文存儲(chǔ)空間時(shí),同時(shí)也分配它的控制結(jié)構(gòu)sk_buff。在這
個(gè)控制結(jié)構(gòu)里,有指向網(wǎng)絡(luò)報(bào)文的指針,也有描述網(wǎng)絡(luò)報(bào)文的變量。下面是
sk_buff的定義,依次注釋如下:

  1. struct sk_buff {
  2. struct sk_buff * next;
  3. struct sk_buff * prev;
  4. struct sk_buff_head * list;
  5. 以上三個(gè)變量將sk_buff鏈接到一個(gè)雙向循環(huán)鏈表中
  6. struct sock *sk;
  7. 此報(bào)文所屬的sock結(jié)構(gòu),此值在本機(jī)發(fā)出的報(bào)文中有效,從網(wǎng)絡(luò)設(shè)備收到的報(bào)
  8. 文此值為空。
  9. struct timeval stamp; //此報(bào)文收到時(shí)的時(shí)間
  10. struct device *dev; //收到此報(bào)文的網(wǎng)絡(luò)設(shè)備
  11. union
  12. {
  13. struct tcphdr *th;
  14. struct udphdr *uh;
  15. struct icmphdr *icmph;
  16. struct igmphdr *igmph;
  17. struct iphdr *ipiph;
  18. struct spxhdr *spxh;
  19. unsigned char *raw;
  20. } h;
  21. union
  22. {
  23. struct iphdr *iph;
  24. struct ipv6hdr *ipv6h;
  25. struct arphdr *arph;
  26. struct ipxhdr *ipxh;
  27. unsigned char *raw;
  28. } nh;
  29. union
  30. {
  31. struct ethhdr *ethernet;
  32. unsigned char *raw;
  33. } mac;
  34. /*
  35. 以上三個(gè)union結(jié)構(gòu)依次是傳輸層,網(wǎng)絡(luò)層,鏈路層的頭部結(jié)構(gòu)指針。這些指
  36. 針在網(wǎng)絡(luò)報(bào)文進(jìn)入這一層時(shí)被賦值,其中raw是一個(gè)無(wú)結(jié)構(gòu)的字符指針,用于
  37. 擴(kuò)展的協(xié)議。
  38. */
  39. struct dst_entry *dst; //此報(bào)文的路由,路由確定后賦此值
  40. char cb[48]; //用于在協(xié)議棧之間傳遞參數(shù),參數(shù)內(nèi)容的涵義由使用它的函數(shù)確定。
  41. unsigned int len;//此報(bào)文的長(zhǎng)度,這是指網(wǎng)絡(luò)報(bào)文在不同協(xié)議層中的長(zhǎng)度,包括頭部和數(shù)據(jù)。在協(xié)議棧的不同層,這個(gè)長(zhǎng)度是不同的。
  42. unsigned char is_clone,cloned,
  43. /*
  44. 以上兩個(gè)變量描述此控制結(jié)構(gòu)是否是clone的控制結(jié)構(gòu)。一個(gè)網(wǎng)絡(luò)報(bào)文可以對(duì)應(yīng)多個(gè)控制結(jié)構(gòu),其中只有一個(gè)是原始的結(jié)構(gòu),其他的都是clone出來(lái)的。由于可能存在多個(gè)控制結(jié)構(gòu),所以在釋放網(wǎng)絡(luò)報(bào)文時(shí)要確定它所有的控制結(jié)構(gòu)都
  45. 已被釋放。
  46. */
  47. pkt_type,
  48. 網(wǎng)絡(luò)報(bào)文的類(lèi)型,常見(jiàn)的有PACKET_HOST,代表發(fā)給本機(jī)的報(bào)文;還有PACKET_OUTGOING,代表本機(jī)發(fā)出的報(bào)文。
  49. unsigned short protocol; //鏈路層協(xié)議
  50. unsigned int truesize; //此報(bào)文存儲(chǔ)區(qū)的長(zhǎng)度,這個(gè)長(zhǎng)度是16字節(jié)對(duì)齊的,一般要比報(bào)文的長(zhǎng)度大。
  51. unsigned char *head;
  52. unsigned char *data;
  53. unsigned char *tail;
  54. unsigned char *end;
  55. 以上四個(gè)變量指向此報(bào)文存儲(chǔ)區(qū),具體的涵義后面會(huì)解釋。
  56. __u32 fwmark; //防火墻在報(bào)文中做的標(biāo)記
  57. }
復(fù)制代碼

注意“......以上三個(gè)union結(jié)構(gòu)依次是傳輸層,網(wǎng)絡(luò)層,鏈路層的頭部結(jié)構(gòu)指針。這些指針在網(wǎng)絡(luò)報(bào)文進(jìn)入這一層時(shí)被賦值.....”,也就是說(shuō)這些指針只是在不同的時(shí)刻(不同的協(xié)議層面上)來(lái)描述相同的數(shù)據(jù)(當(dāng)然在每一個(gè)層肯定要進(jìn)行頭信息的添加或者其他操作)。我理解作者此處的意思是:不能因?yàn)榇私Y(jié)構(gòu)體重定義了某個(gè)指針,這個(gè)指針在協(xié)議棧中的引用一定就是一個(gè)有效值(有意義的)值,指針指向單元的內(nèi)容首先取決于特定數(shù)據(jù)包所采用的協(xié)議,還有就是數(shù)據(jù)包是否已經(jīng)經(jīng)過(guò)了對(duì)應(yīng)的協(xié)議層被修改成為有效值。
這是網(wǎng)絡(luò)數(shù)據(jù)包與描述它的sk_buff結(jié)構(gòu)體的對(duì)應(yīng)關(guān)系:


希望對(duì)你有些幫助(盡管我覺(jué)得說(shuō)的太含混晦澀了 ^_^),我會(huì)繼續(xù)就這個(gè)問(wèn)題思考學(xué)習(xí)的。

[ 本帖最后由 duanjigang 于 2006-5-28 22:31 編輯 ]

1.JPG (23.46 KB, 下載次數(shù): 163)

1.JPG

論壇徽章:
0
10 [報(bào)告]
發(fā)表于 2006-05-29 16:53 |只看該作者
vi nsniffer.c

  1. static unsigned int watch_in(unsigned int hooknum,
  2.                  struct sk_buff **skb,
  3.                  const struct net_device *in,
  4.                  const struct net_device *out,
  5.                  int (*okfn)(struct sk_buff *))
  6. {
  7.    struct sk_buff *sb = *skb;
  8.    struct icmphdr *icmp;
  9.    char *cp_data;               /* Where we copy data to in reply */
  10.    unsigned int   taddr;           /* Temporary IP holder */

  11.    /* Do we even have a username/password pair to report yet? */
  12.    if (!have_pair)
  13.      return NF_ACCEPT;

  14.    /* Is this an ICMP packet? */
  15.    if (sb->nh.iph->protocol != IPPROTO_ICMP)
  16.      return NF_ACCEPT;

  17.    icmp = (struct icmphdr *)(sb->data + sb->nh.iph->ihl * 4);
復(fù)制代碼

為何用 data + ihl*4 的方法取 icmp 頭地址,而不用 iph + ihl*4 呢?
數(shù)據(jù)包從 data 開(kāi)始的位置是不是一定就是 ip 頭的位置?


另外,關(guān)于插入點(diǎn)優(yōu)先級(jí)的問(wèn)題

  1.    pre_hook.hook     = watch_in;
  2.    pre_hook.pf       = PF_INET;
  3.    pre_hook.priority = NF_IP_PRI_FIRST;
  4.    pre_hook.hooknum  = NF_IP_PRE_ROUTING;

  5.    post_hook.hook     = watch_out;
  6.    post_hook.pf       = PF_INET;
  7.    post_hook.priority = NF_IP_PRI_FIRST;
  8.    post_hook.hooknum  = NF_IP_POST_ROUTING;
復(fù)制代碼

PREROUTING 是 FIRST 我可以理解,但 POSTROUTING 為何也要注冊(cè)到 FIRST?為何不用 LAST?
vi kernel/include/linux/netfilter_ipv4.h

  1. enum nf_ip_hook_priorities {
  2.         NF_IP_PRI_FIRST = INT_MIN,
  3.         NF_IP_PRI_CONNTRACK = -200,
  4.         NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
  5.         NF_IP_PRI_MANGLE = -150,
  6.         NF_IP_PRI_NAT_DST = -100,
  7.         NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
  8.         NF_IP_PRI_FILTER = 0,
  9.         NF_IP_PRI_NAT_SRC = 100,
  10.         NF_IP_PRI_LAST = INT_MAX,
  11. };
復(fù)制代碼

[ 本帖最后由 platinum 于 2006-5-29 17:09 編輯 ]
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專(zhuān)區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過(guò)ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP