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

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
123下一頁
最近訪問板塊 發(fā)新帖
查看: 12403 | 回復(fù): 21
打印 上一主題 下一主題

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

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

深入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

第一章  簡介

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

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

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

1.2 本文不會涉及到的方面

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

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

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

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

2.2 Ipv4中的Netfilter鉤子

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

表 2.1. ipv4中定義的鉤子

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

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

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

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

第三章  注冊和注銷NetFilter 鉤子

注冊一個鉤子函數(shù)是一個圍繞nf_hook_ops結(jié)構(gòu)體的很簡單的過程,在linux/netfilter.h中有這個結(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ù)制代碼

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

代碼列表1. Netfilter鉤子的注冊

  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ù)制代碼


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

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

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

然后你只需要引用一次就可以訪問結(jié)構(gòu)體中的成員了?梢允褂胹k_buff結(jié)構(gòu)體中的網(wǎng)絡(luò)層頭信息來獲取此數(shù)據(jù)包的IP頭信息。這個頭包含在一個聯(lián)合中,可以通過sk_buff->nh.iph來獲取。列表3的函數(shù)演示了當(dāng)給定一個數(shù)據(jù)包的sk_buff結(jié)構(gòu)時,如何根據(jù)給定的要拒絕的IP對這個數(shù)據(jù)包進行源IP地址的檢驗。這段代碼是直接從LWFW中拉出來的。唯一的不同之處就是LWFW中對LWFW統(tǒng)計量的更新被去掉了。
列表3.檢測接收到數(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地址匹配的話,數(shù)據(jù)包就會被丟棄。為了使函數(shù)能正常工作,deny_ip的值應(yīng)該以網(wǎng)絡(luò)字節(jié)序的方式存儲(與intel相反的Big-endian格式)。盡管這個函數(shù)在被調(diào)用的時候有一個空指針作參數(shù)這種情況不太可能,但是稍微偏執(zhí)(小心)一點總不會有什么壞處。當(dāng)然,如果調(diào)用時出現(xiàn)了差錯的話,函數(shù)將會返回一個NF_ACCEPT值,以便于Netfilter能夠繼續(xù)處理這個數(shù)據(jù)包。列表4 展現(xiàn)了一個簡單的基于IP地址的數(shù)據(jù)包過濾的模塊,這個模塊是由基于接口的過濾模塊修改得到的。你可以修改IP地址來實現(xiàn)對指定IP地址發(fā)來的數(shù)據(jù)包的丟棄。

列表4. 基于數(shù)據(jù)包源IP地址的過濾技術(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端口的過濾
另外一個要執(zhí)行的簡單的規(guī)則就是基于TCP目的端口的數(shù)據(jù)包過濾。這比檢驗IP地址稍微復(fù)雜一點,因為我們要自己創(chuàng)建一個指向TCP頭的指針。還記得前面關(guān)于傳輸層頭和網(wǎng)絡(luò)層頭所做的討論嗎?獲得一個TCP頭指針很簡單,只需要申請一個指向tcphdr(定義在linux/tcp.h中)結(jié)構(gòu)體的指針,并將它指向包數(shù)據(jù)中的IP頭后面;蛟S一個例子就可以了。列表5展示了怎樣檢測一個數(shù)據(jù)包的TCP目的端口與我們想丟棄數(shù)據(jù)的指定端口是否一致。與列表3一樣,這段代碼也是從LWFW中拿出來的
列表5. 檢測接收到數(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ù)制代碼

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

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

souce.rar

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

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

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

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

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

5.1 隱藏后門守護進程

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

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

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

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


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

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

6.1 SOCK_PACKET, SOCK_RAW 和Libpcap

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

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

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

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

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

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

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

A.2 源代碼 lwfw.c
見附件
A.3 lwfw.h,Makefile
見附件
A.4 譯者添加的測試程序
下面是譯者自己在學(xué)習(xí)時寫的一個對LWFW的過濾規(guī)則進行設(shè)置和改動的例子,你也可以對此段代碼進行修改,當(dāng)模塊成功加載之后,建立一個字符設(shè)備文件,然后這個程序就能運行了。
  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.         /*來自這個IP地址的數(shù)據(jù)將被丟棄*/
  20. char * deny_ip = "192.168.1.105";
  21.         /*這個接口發(fā)出的數(shù)據(jù)將被丟棄,無法外流*/
  22. char *ifcfg = "eth0";
  23.         /*要禁止的TCP目的端口22, ssh的默認端口*/
  24. unsigned char *  port = "\x00\x16";
  25.         /*打開設(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)該是一段時間之后的事,此處直接獲取,不妥*/
  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.         禁用這個接口
  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  第六部分的代碼
這里是一個簡單的模塊,在這個模塊中將對packet_rcv()函數(shù)和raw_rcv()函數(shù)進行替換,從而隱藏到達或者離開我們指定所IP地址的數(shù)據(jù)包。默認的IP是“127.0.0.1”,但是,可以通過修改#define IP 來改動這個值。同樣提供了一個bash的腳本,負責(zé)從Sytem.map文件中獲取所需函數(shù)的地址,并且負責(zé)模塊的插入,在插入模塊時,以所需的格式將這些函數(shù)的地址傳遞給內(nèi)核。這個加載腳本是grem寫的。原來是為我的mod-off項目而寫,經(jīng)過簡單的修改就能用于這里的模塊,再次感謝grem。
這里給出的模塊只是原理性的代碼,沒有任何模塊隱藏的方法。有很重要的一點需要記住,盡管這個模塊能夠從運行于同一臺機子上的嗅探器中隱藏指定的傳輸,但是,位于同一個網(wǎng)段上的其他機子上的嗅探器仍然能夠看到這些數(shù)據(jù)包?戳诉@個模塊,精干的讀者很快就能設(shè)計一些Netfilter鉤子函數(shù)來阻斷任何一種想要阻斷的數(shù)據(jù)包。我就利用本文中提到的技術(shù)成功地在其它內(nèi)核模塊項目中實現(xiàn)了對控制和信息獲取數(shù)據(jù)包的隱藏。
(此處代碼見附件)

[參考文獻]:
[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 [報告]
發(fā)表于 2006-05-21 09:55 |只看該作者
好文,支持樓主

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

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

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

論壇徽章:
0
5 [報告]
發(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)體的指針。這就意味著,如果認為h->th指向TCP頭,從而想通過h->th來獲取一個值的話,將會導(dǎo)致錯誤發(fā)生。因為h->th實際指向IP頭,等同于nh->iph。


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

這是為什么呢!

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

感謝

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

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

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

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

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

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

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

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


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

這是為什么呢!

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

  1. struct sk_buff {
  2. struct sk_buff * next;
  3. struct sk_buff * prev;
  4. struct sk_buff_head * list;
  5. 以上三個變量將sk_buff鏈接到一個雙向循環(huán)鏈表中
  6. struct sock *sk;
  7. 此報文所屬的sock結(jié)構(gòu),此值在本機發(fā)出的報文中有效,從網(wǎng)絡(luò)設(shè)備收到的報
  8. 文此值為空。
  9. struct timeval stamp; //此報文收到時的時間
  10. struct device *dev; //收到此報文的網(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. 以上三個union結(jié)構(gòu)依次是傳輸層,網(wǎng)絡(luò)層,鏈路層的頭部結(jié)構(gòu)指針。這些指
  36. 針在網(wǎng)絡(luò)報文進入這一層時被賦值,其中raw是一個無結(jié)構(gòu)的字符指針,用于
  37. 擴展的協(xié)議。
  38. */
  39. struct dst_entry *dst; //此報文的路由,路由確定后賦此值
  40. char cb[48]; //用于在協(xié)議棧之間傳遞參數(shù),參數(shù)內(nèi)容的涵義由使用它的函數(shù)確定。
  41. unsigned int len;//此報文的長度,這是指網(wǎng)絡(luò)報文在不同協(xié)議層中的長度,包括頭部和數(shù)據(jù)。在協(xié)議棧的不同層,這個長度是不同的。
  42. unsigned char is_clone,cloned,
  43. /*
  44. 以上兩個變量描述此控制結(jié)構(gòu)是否是clone的控制結(jié)構(gòu)。一個網(wǎng)絡(luò)報文可以對應(yīng)多個控制結(jié)構(gòu),其中只有一個是原始的結(jié)構(gòu),其他的都是clone出來的。由于可能存在多個控制結(jié)構(gòu),所以在釋放網(wǎng)絡(luò)報文時要確定它所有的控制結(jié)構(gòu)都
  45. 已被釋放。
  46. */
  47. pkt_type,
  48. 網(wǎng)絡(luò)報文的類型,常見的有PACKET_HOST,代表發(fā)給本機的報文;還有PACKET_OUTGOING,代表本機發(fā)出的報文。
  49. unsigned short protocol; //鏈路層協(xié)議
  50. unsigned int truesize; //此報文存儲區(qū)的長度,這個長度是16字節(jié)對齊的,一般要比報文的長度大。
  51. unsigned char *head;
  52. unsigned char *data;
  53. unsigned char *tail;
  54. unsigned char *end;
  55. 以上四個變量指向此報文存儲區(qū),具體的涵義后面會解釋。
  56. __u32 fwmark; //防火墻在報文中做的標(biāo)記
  57. }
復(fù)制代碼

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


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

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

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

1.JPG

論壇徽章:
0
10 [報告]
發(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 開始的位置是不是一定就是 ip 頭的位置?


另外,關(guān)于插入點優(yōu)先級的問題

  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 為何也要注冊到 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 編輯 ]
您需要登錄后才可以回帖 登錄 | 注冊

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

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP