[作者:浩海孤帆]
網(wǎng)絡數(shù)據(jù)截獲方法
    網(wǎng)絡數(shù)據(jù)包截獲機制是網(wǎng)絡入侵檢測系統(tǒng)的基礎組件。一般指通過截獲整個網(wǎng)絡的所有信息流量,根據(jù)信息源主機,目標主機,服務協(xié)議端口等信息簡單過濾掉不關心的數(shù)據(jù),再將用戶感興趣的數(shù)據(jù)發(fā)送給更高層的應用程序進行分析。流程圖如下:

圖5.1 網(wǎng)絡數(shù)據(jù)截獲流程
一方面要,網(wǎng)絡截取模塊要能保證截取到所有網(wǎng)絡上的數(shù)據(jù)包,尤其是檢測到被分片的數(shù)據(jù)包(這可能蘊涵著攻擊)。
另方面,數(shù)據(jù)截取模塊截取數(shù)據(jù)包的效率也是很重要的。
它直接影響整個入侵檢測系統(tǒng)的運行速度。

5.2
各種數(shù)據(jù)流截獲方法
5.2.1 利用廣播截取網(wǎng)絡數(shù)據(jù)流
數(shù)據(jù)包的截取技術是依賴網(wǎng)卡的。而網(wǎng)卡可以通過廣播監(jiān)聽到以太網(wǎng)絡上的數(shù)據(jù)包,這就是數(shù)據(jù)包截取技術的基礎。
要想截獲不是給自己數(shù)據(jù)流,就必須繞開系統(tǒng)正常工作的機制,直接通過網(wǎng)卡的混雜模式,使之可以接受目標地址不是自己的MAC地址的數(shù)據(jù)包,直接訪問數(shù)據(jù)鏈路層,取數(shù)據(jù)。
5.2.2各系統(tǒng)截取數(shù)據(jù)包機制
Linux系統(tǒng)為用戶提供一種在理論上是數(shù)據(jù)鏈路層的,基于網(wǎng)卡驅動程序的,可以不用操作系統(tǒng)自身協(xié)議棧的接口(也稱套接字)-SockPacket. 這種套接字可以從數(shù)據(jù)鏈路層(就是網(wǎng)線)上直接截取所有鏈路層數(shù)據(jù)包。而Unix則是通過Libpcap庫直接與內核交互,實現(xiàn)網(wǎng)絡截取。如:Libpcap,Tcpdump等。如圖:

圖5.2 Unix系統(tǒng)監(jiān)聽機制

BSD Packet Filter(BPF)機制來截取數(shù)據(jù)包。BPF可以說是各系統(tǒng)中最棒的截獲方式。很多開源嗅探工具就是基于它而開發(fā)的。Windows系統(tǒng)也有類似情況,如:win系列上有*.vxd (VxD,VirtualDeviceDrive)(packet*.vxd) 和 網(wǎng)卡.sys(為網(wǎng)卡芯片所開發(fā))來驅動網(wǎng)卡截取數(shù)據(jù)包。

圖5.3 Windows系統(tǒng)監(jiān)聽機制
5.2.3 BPF過濾
Unix&Linux系統(tǒng)有兩種數(shù)據(jù)鏈路層截取機制,分別是BSD系列系統(tǒng)(NetBSD,OpenBSD,FreeBSD等)的BPF和Linux的SOCKET_PACKET。
BPF過濾
BPF主要由兩大部分組成:
網(wǎng)絡頭接口
數(shù)據(jù)包過濾器。
網(wǎng)絡頭接口從網(wǎng)絡設備驅動程序處收集數(shù)據(jù)包復制(在提交給系統(tǒng)協(xié)議棧之前),并傳遞給正在截獲數(shù)據(jù)包的應用程序。而過濾器決定某一數(shù)據(jù)包是被接受或者拒絕以及如果被接受,數(shù)據(jù)包的那些部分會被復制給應用程序。BPF結構圖如下:

圖5.4 BPF結構示意圖
如:TCPDump注:(1), Libpcap, Sniffer, eeye,等。一般情況,網(wǎng)卡驅動通過網(wǎng)卡把網(wǎng)絡上的電平信號轉化成數(shù)據(jù)包,再把截取到的數(shù)據(jù)傳給系統(tǒng)自帶的協(xié)議棧,然后在交由系統(tǒng)處理。這種方式與Unix下的BPF不同,它使得網(wǎng)卡驅動在把從網(wǎng)絡截取的數(shù)據(jù)提交給系統(tǒng)之前,先拷貝一份給BPF,再由BPF 決定是否符合規(guī)則,不符合則丟棄,符合則存放到內存指定區(qū),等待處理。
當然,BPF對網(wǎng)卡驅動交給系統(tǒng)協(xié)議棧的數(shù)據(jù)包不做任何干涉。
注1:TCPDump是伯克利實驗室的Van Jacobson,Craig Leres和Stenven McCanne為分析TCP性能問題而寫的跨平臺的監(jiān)聽程序。

5.3
基于Libpcap庫的通用數(shù)據(jù)截獲技術
Libpcap是用戶態(tài)的數(shù)據(jù)包截獲API函數(shù)接口,有獨立和可移植行。最初,Libpcap是為了強大的,健壯TCPDump而編寫的。它支持BPF過濾機制。Snort就是依賴于libpcap庫進行數(shù)據(jù)包截取的程序之一(還有Ethereal,eeye等)。 它的優(yōu)點是可以從任何Unix內核平臺上截取數(shù)據(jù)包,而不考慮什么芯片類型的網(wǎng)卡和驅動程序。更重要的是,它可以使開發(fā)人員編寫自己的解碼,顯示,記錄等程序。

5.2.2.1 Libpcap庫主要函數(shù)
1. 頭文件特征(pcap.h)
Libpcap庫(數(shù)據(jù)流存儲頭文件<Pcap.h>的結構定義如下圖)。前半部分是數(shù)據(jù)庫存儲文件頭的數(shù)據(jù)結構定義。

圖5.5 頭文件(pcap.h)定義部分截圖
后半部是信息包頭文件數(shù)據(jù)結構定義。
2. 打開并讀取設備,設置過濾器部分
與最基層設備打交道。有三個函數(shù):pcap_read() pcap_open_live 和 pcap_setfilter()
3. 脫機方式截取數(shù)據(jù)
及讀取存儲在營盤上的文件。有兩個Pcap_open_offline()和Pcap_offline_read().
4. 本地網(wǎng)絡設置檢測部分
主要檢測網(wǎng)絡設置的函數(shù)有幾個,包括Pcap_lookupdev() pcap_lookupnet()等。 因為前面提過,Libpcap可移植。所以各種平臺的Socket借口都是兼容的。
5. 主程序
都在Pcap.c中,該文件定義了讀取數(shù)據(jù)的統(tǒng)一接口函數(shù)pcap_next(),調用此函數(shù)獲取下一個數(shù)據(jù)包。
5.4 Snort調用Libpcap
在Snort運行啟動時,Snort調用Libpcap庫。當調用libpcap函數(shù)并初始化接口時,進入截取數(shù)據(jù)的循環(huán)模塊—pcap循環(huán)。
在這個主循環(huán)—Pcaploop(),當網(wǎng)卡從網(wǎng)絡介質上接收數(shù)據(jù)開始,Pcap_loop便對采集來的每個數(shù)據(jù)包都ProcessPacket()函數(shù)處理,如果出錯或達到指定的處理包數(shù)就退出。(相關代碼如下)
具體就是,Pcap_loop()最后根據(jù)數(shù)據(jù)鏈路類型來選擇數(shù)據(jù)包,然后由ProcessPacket()來進行協(xié)議分析,實施信息流的匹配。
如:ProcessPaceket函數(shù)調用DecodeEthPkt函數(shù)來對以太網(wǎng)數(shù)據(jù)進行解碼。其中DecodeEthPkt()函數(shù)再調用子函數(shù)Decode IP來對IP協(xié)議進行解碼……
Libpcap函數(shù)功能列舉:
Pcap_open_live(): 獲得數(shù)據(jù)包通用句柄。
Pcap_lookup_dev(): 指向網(wǎng)絡可用設備。
Pcap_looknet(): 初步判斷網(wǎng)絡設備本身的IP & netmask。
Pcap_Dump(): 基于TCPDump的,將網(wǎng)絡數(shù)據(jù)包保存成文件。
程序部分代碼如下:
/* Read all packets on the device. Continue until cnt packets read */

pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
{
register int n;

for (;;) { //for循環(huán)
if (p->sf.rfile != NULL) {
n = pcap_offline_read(p, cnt, callback, user);
} else {
// XXX keep reading until we get something
do {
n = p->read_op(p, cnt, callback, user);
} while (n == 0);
}
if (n <= 0)
return (n); //遇錯誤,返回
if (cnt > 0) {
cnt -= n;
if (cnt <= 0)
return (0); //達到制定數(shù)量,返回
}
}
}
pcap_loop()有幾個重要參數(shù):
參數(shù)是pv.pkt_cnt,表示總共要捕捉的包的數(shù)量。在main函數(shù)初始化時,缺省設置為-1,成為永真循環(huán),一直捕捉直到程序退出:
/* initialize the packet counter to loop forever */
pv.pkt_cnt = -1;
或者在命令行中設置要捕捉的包的數(shù)量。ParseCmdLine函數(shù)的調用中,遇到參數(shù)n,重新設定pv.pkt_cnt的值。ParseCmdLine中相關語句如下:
case 'n': /* grab x packets and exit */
pv.pkt_cnt = atoi(optarg);
Snort.c主程序中,Pcap_loop()函數(shù)有兩種提取數(shù)據(jù)模式:
打開網(wǎng)卡和打開文件。
/* get the device file descriptor,打開網(wǎng)卡接口 */

pd = pcap_open_live(pv.interface, snaplen,
pv.promisc_flag ? PROMISC : 0, READ_TIMEOUT, errorbuf);
或者
/* open the file,打開文件 */

pd = pcap_open_offline(intf, errorbuf);
只有以上兩種返回情況。
Snort把真實的數(shù)據(jù)包存儲在內存中指針指向的數(shù)據(jù)結構中。在decode.h中,定義了所有Snort要使用到的數(shù)據(jù)結構,包括Tcp,Ip,以太楨,vlan楨等。Snort的這些結構將指針指向截獲的包上來代表相應的協(xié)議。如指向以太楨用數(shù)據(jù)指針使用_EthrHdr頭。
數(shù)據(jù)結構:
Typededf struct _etherhdr
{
u_int8_t ether_dst[6]; //目的地址;
u_int8_t ether_src[6]; //源地址;
u_int16_t ether_type; //協(xié)議類型;
}
typedef struct _Tcphdr
{
u_int16_t th_sport; //源端口;
u_int16_t th_dport; //目端口;
u_int32_t th_seq; //sequence number;
u_int32_t th_ack; //acknowledgement number;
u_int8_t th_offx2; //offset and reserved;
u_int8_t th_flags; //flags;
u_int16_t th_win; //window;
u_int16_t th_sum; //checksum;
u_int16_t th_urp; //urgent pointer;
}
小結
    網(wǎng)絡數(shù)據(jù)的截取是入侵檢測系統(tǒng)的基礎。本節(jié)從網(wǎng)絡數(shù)據(jù)截取機制開始講,講述了常見的兩種機制。重點講述了Libpcap庫的網(wǎng)絡數(shù)據(jù)包截取機制。