国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

Pcap程序設(shè)計(jì)

2019-11-17 05:14:07
字體:
供稿:網(wǎng)友

  By 阿美

Tim Carstens
此文的最近更新見于 http://broker.dhs.org/pcap.htm
好,讓我們從看看這篇文章寫給誰開始。顯而易見的,需要一些C語(yǔ)言基礎(chǔ)知識(shí),除非你只想了解基本的理論。你不必是一個(gè)編碼專家,因?yàn)檫@個(gè)領(lǐng)域只有經(jīng)驗(yàn)豐富的程序員涉足,而我將盡可能具體的描述這些概念。另外,考慮到這是有關(guān)一個(gè)包嗅探器的,所以對(duì)網(wǎng)絡(luò)基礎(chǔ)知識(shí)的理解是有幫助的。所有在此出現(xiàn)的代碼示例都已在FreeBSD 4.3平臺(tái)上測(cè)試通過。
開始:pcap應(yīng)用程序的格式
我們所要理解的第一件事情是一個(gè)基于pcap的嗅探器程序的總體布局。流程如下:
1.我們從決定用哪一個(gè)接口進(jìn)行嗅探開始。在linux中,這可能是eth0,而在BSD系統(tǒng)中則可能是xl1等等。我們也可以用一個(gè)字符串來定義這個(gè)設(shè)備,或者采用pcap提供的接口名來工作。
2.初始化pcap。在這里我們要告訴pcap對(duì)什么設(shè)備進(jìn)行嗅探。假如愿意的話,我們還可以嗅探多個(gè)設(shè)備。怎樣區(qū)分它們呢?使用 文件句柄。就像打開一個(gè)文件進(jìn)行讀寫一樣,必須命名我們的嗅探“會(huì)話”,以此使它們各自區(qū)別開來。
3.假如我們只想嗅探特定的傳輸(如TCP/ip包,發(fā)往端口23的包等等),我們必須創(chuàng)建一個(gè)規(guī)則集合,編譯并且使用它。這個(gè)過程分為三個(gè)相互緊密關(guān)聯(lián)的階段。規(guī)則集合被置于一個(gè)字符串內(nèi),并且被轉(zhuǎn)換成能被pcap讀的格式(因此編譯它)。編譯實(shí)際上就是在我們的程序里調(diào)用一個(gè)不被外部程序使用的函數(shù)。接下來我們要告訴 pcap使用它來過濾出我們想要的那一個(gè)會(huì)話。
4.最后,我們告訴pcap進(jìn)入它的主體執(zhí)行循環(huán)。在這個(gè)階段內(nèi)pcap一直工作到它接收了所有我們想要的包為止。每當(dāng)它收到一個(gè)包就調(diào)用另一個(gè)已經(jīng)定義好的函數(shù),這個(gè)函數(shù)可以做我們想要的任何工作,它可以剖析所部獲的包并給用戶打印出結(jié)果,它可以將結(jié)果保存為一個(gè)文件,或者什么也不作。
5.在嗅探到所需的數(shù)據(jù)后,我們要關(guān)閉會(huì)話并結(jié)束。
這是實(shí)際上一個(gè)很簡(jiǎn)單的過程。一共五個(gè)步驟,其中一個(gè)(第3個(gè))是可選的。我們?yōu)槭裁床豢匆豢词窃鯓訉?shí)現(xiàn)每一個(gè)步驟呢?
設(shè)置設(shè)備
這是很簡(jiǎn)單的。有兩種方法設(shè)置想要嗅探的設(shè)備。
第一種,我們可以簡(jiǎn)單的讓用戶告訴我們。考察下面的程序:
#include <stdio.h>
#include <pcap.h>
int main(int argc, char *argv[])
{

char *dev = argv[1];
return(0);
}
用戶通過傳遞給程序的第一個(gè)參數(shù)來指定設(shè)備。字符串“dev”以pcap能“理解”的格式保存了我們要嗅探的接口的名字(當(dāng)然,用戶必須給了我們一個(gè)真正存在的接口)。
另一種也是同樣的簡(jiǎn)單。來看這段程序:
#include <stdio.h>
#include <pcap.h>
int main()
{
char *dev, errbuf[PCAP_ERRBUF_SIZE];
dev = pcap_lookupdev(errbuf);
printf("Device: %s", dev);
return(0);
}
在這個(gè)例子里,pcap就自己設(shè)置設(shè)備。“但是,等一下,Tim”,你會(huì)說,“字符串errbuf是做什么的?”大多數(shù)的pcap命令答應(yīng)我們向它們傳遞字符串作為參數(shù)。這個(gè)字符串的目的是什么呢?假如命令失敗,它將傳給這個(gè)字符串關(guān)于錯(cuò)誤的描述。這樣,假如pcap_lookupdev()失敗,它將在 errbuf存儲(chǔ)錯(cuò)誤信息。很好,是不是?這就是我們?cè)鯓尤ピO(shè)置設(shè)備。
打開設(shè)備進(jìn)行嗅探
創(chuàng)建一個(gè)嗅探會(huì)話的任務(wù)真的非常簡(jiǎn)單。為此,我們使用pcap_open_live()函數(shù)。此函數(shù)的原型(根據(jù)pcap的手冊(cè)頁(yè))如下:
pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
其第一個(gè)參數(shù)是我們?cè)谏弦还?jié)中指定的設(shè)備,snaplen是整形的,它定義了將被pcap捕捉的最大字節(jié)數(shù)。當(dāng)promisc設(shè)為true時(shí)將置指定接口為混雜模式(然而,當(dāng)它置為false時(shí)接口仍處于混雜模式的非凡情況也是有可能的)。to_ms是讀取時(shí)的超時(shí)值,單位是毫秒(假如為0則一直嗅探直到錯(cuò)誤發(fā)生,為-1則不確定)。最后,ebuf是一個(gè)我們可以存入任何錯(cuò)誤信息的字符串(就像上面的errbuf)。此函數(shù)返回其會(huì)話句柄。
舉個(gè)例子,考察以下代碼片斷:
#include <pcap.h>
...
pcap_t *handle;
handle = pcap_open_live(somedev, BUFSIZ, 1, 0, errbuf);
這個(gè)代碼片斷打開字符串somedev的設(shè)備,告訴它讀取被BUFSIZ指定的字節(jié)數(shù)(BUFSIZ在pcap.h里定義)。我們告訴它將設(shè)備置為混雜模式,一直嗅探到錯(cuò)誤發(fā)生,假如有了錯(cuò)誤,把它存放在字符串errbuf中。
混雜模式與非混雜模式的區(qū)別:這兩種方式區(qū)別很大。一般來說,非混雜模式的嗅探器中,主機(jī)僅嗅探那些跟它直接有關(guān)的通信,如發(fā)向它的,從它發(fā)出的,或經(jīng)它路由的等都會(huì)被嗅探器捕捉。而在混雜模式中則嗅探傳輸線路上的所有通信。在非交換式網(wǎng)絡(luò)中,這將是整個(gè)網(wǎng)絡(luò)的通信。這樣做最明顯的優(yōu)點(diǎn)就是使更多的包被嗅探到,它們因你嗅探網(wǎng)絡(luò)的原因或者對(duì)你有幫助,或者沒有。但是,混雜模式是可被探測(cè)到的。一個(gè)主機(jī)可以通過高強(qiáng)度的測(cè)試判定另一臺(tái)主機(jī)是否正在進(jìn)行混雜模式的嗅探。其次,它僅在非交換式的網(wǎng)絡(luò)環(huán)境中有效工作(如集線器,或者交換中的ARP層面)。再次,在高負(fù)荷的網(wǎng)絡(luò)中,主機(jī)的系統(tǒng)資源將消耗的非常嚴(yán)重。

過濾通信
通常,我們的嗅探器僅對(duì)某特定的通信感愛好。例如,有時(shí)我們想嗅探到端口23(telnet)的包以獲得密碼;或者我們想截獲一個(gè)正通過端口21 (FTP)傳送的文件;可能我們僅想要得到DNS的通信(端口53,UDP)。無論哪種情況,我們都很少盲目的嗅探整個(gè)網(wǎng)絡(luò)的通信。下面討論pcap_compile()與pcap_setfilter()。
這個(gè)過程非常簡(jiǎn)單。當(dāng)我們已經(jīng)調(diào)用了pcap_open_live()從而建立了一個(gè)嗅探會(huì)話之后就可以應(yīng)用我們自己的過濾器了。為什么要用我們自己的過濾器呢?有兩個(gè)原因。第一,pcap的過濾器太強(qiáng)大了,因?yàn)樗苯邮褂?BPF過濾器,我們通過使用BPF驅(qū)動(dòng)直接過濾跳過了很多的關(guān)節(jié)。第二,這樣做要輕易的多。
在使用我們自己的過濾器前必須編譯它。過濾表達(dá)式被保存在一個(gè)字符串中(字符數(shù)組)。其句法在tcpdump的手冊(cè)頁(yè)中被證實(shí)非常好。我建議你親自閱讀它。但是我們將使用簡(jiǎn)單的測(cè)試表達(dá)式,這樣你可能很輕易理解我的例子。
我們調(diào)用pcap_compile()來編譯它,其原型是這樣定義的:
int pcap_compile(pcap_t *p, strUCt bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
第 一個(gè)參數(shù)是會(huì)話句柄(pcap_t *handle在前一節(jié)的示例中)。接下來的是我們存儲(chǔ)被編譯的過濾器版本的地址的引用。再接下來的則是表達(dá)式本身,存儲(chǔ)在規(guī)定的字符串格式里。再下邊是一個(gè)定義表達(dá)式是否被優(yōu)化的整形量(0為false,1為true,標(biāo)準(zhǔn)規(guī)定)。最后,我們必須指定應(yīng)用此過濾器的網(wǎng)絡(luò)掩碼。函數(shù)返回-1為失敗,其他的任何值都表明是成功的。
表達(dá)式被編譯之后就可以使用了。現(xiàn)在進(jìn)入pcap_setfilter()。仿照我們介紹pcap的格式,先來看一看pcap_setfilter()的原型:
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
這非常直觀,第一個(gè)參數(shù)是會(huì)話句柄,第二個(gè)參數(shù)是被編譯表達(dá)式版本的引用(可推測(cè)出它與pcap_compile()的第二個(gè)參數(shù)相同)。
下面的代碼示例可能能使你更好的理解:
#include <pcap.h>
pcap_t *handle; /* 會(huì)話的句柄 */
char dev[] = "rl0"; /* 執(zhí)行嗅探的設(shè)備 */
char errbuf[PCAP_ERRBUF_SIZE]; /* 存儲(chǔ)錯(cuò)誤 信息的字符串 */
struct bpf_program filter; /*已經(jīng)編譯好的過濾表達(dá)式*/
char filter_app[] = "port 23"; /* 過濾表達(dá)式*/
bpf_u_int32 mask; /* 執(zhí)行嗅探的設(shè)備的網(wǎng)絡(luò)掩碼 */
bpf_u_int32 net; /* 執(zhí)行嗅探的設(shè)備的IP地址 */
pcap_lookupnet(dev, &net, &mask, errbuf);
handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf);
pcap_compile(handle, &filter, filter_app, 0, net);
pcap_setfilter(handle, &filter);
這個(gè)程序使嗅探器嗅探經(jīng)由端口23的所有通信,使用混雜模式,設(shè)備是rl0。
你可能注重到前面的示例包含一個(gè)我們還沒提到的函數(shù):pcap_lookupnet(),向這個(gè)函數(shù)提供設(shè)備接口名,它將返回其IP和網(wǎng)絡(luò)掩碼,這是很基本的,因?yàn)槲覀冃枰谰W(wǎng)絡(luò)掩碼以便應(yīng)用過濾器。此函數(shù)在此文最后的miscellaneous一節(jié)里還有描述。
據(jù)我的經(jīng)驗(yàn),這個(gè)過濾器在所有的
操作系統(tǒng)下都不會(huì)工作。在我的測(cè)試環(huán)境里,我發(fā)現(xiàn)OpenBSD 2.9默認(rèn)內(nèi)核支持這種過濾器,但FreeBSD 4.3默認(rèn)內(nèi)核則不支持。你的情況可能會(huì)有變化。
實(shí)際的嗅探
到此為止,我們已經(jīng)學(xué)習(xí)了如何定義一個(gè)設(shè)備,讓它預(yù)備嗅探,還有應(yīng)用過濾器使我們嗅談到什么或者不嗅探到什么。現(xiàn)在到了真正去捕捉一些數(shù)據(jù)包的時(shí)候了。有兩種手段捕捉包。我們可以一次只捕捉一個(gè)包,也可以進(jìn)入一個(gè)循環(huán),等捕捉到多個(gè)包再進(jìn)行處理。我們將先看看怎樣去捕捉單個(gè)包,然后再看看使用循環(huán)的方法。為此,我們使用函數(shù)pcap_next()。
Pcap_next()的原型及其簡(jiǎn)單:
u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
第一個(gè)參數(shù)是會(huì)話句柄,第二個(gè)參數(shù)是指向一個(gè)包括了當(dāng)前數(shù)據(jù)包總體信息(被捕捉時(shí)的時(shí)間,包的長(zhǎng)度,其被指定的部分長(zhǎng)度)的結(jié)構(gòu)體的指針(在這里只有一個(gè)片斷,只作為一個(gè)示例)。Pcap_next()返回一個(gè)u_char指針給被這個(gè)結(jié)構(gòu)體描述的包。我們將稍后討論這種實(shí)際讀取包本身的手段。
這里有一個(gè)演示怎樣使用pcap_next()來嗅探一個(gè)包的例子:
#include <pcap.h>
#include <stdio.h>
int main()
{
pcap_t *handle; /* 會(huì)話句柄 */
char *dev; /* 執(zhí)行嗅探的設(shè)備 */
char errbuf[PCAP_ERRBUF_SIZE]; /* 存儲(chǔ)錯(cuò)誤信息的字符串 */

struct bpf_program filter; /* 已經(jīng)編譯好的過濾器 */
char filter_app[] = "port 23"; /* 過濾表達(dá)式 */
bpf_u_int32 mask; /* 所在網(wǎng)絡(luò)的掩碼 */
bpf_u_int32 net; /* 主機(jī)的IP地址 */
struct pcap_pkthdr header; /* 由pcap.h定義 */
const u_char *packet; /* 實(shí)際的包 */
/* Define the device */
dev = pcap_lookupdev(errbuf);
/* 探查設(shè)備屬性 */
pcap_lookupnet(dev, &net, &mask, errbuf);
/* 以混雜模式打開會(huì)話 */
handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf);
/* 編譯并應(yīng)用過濾器 */
pcap_compile(handle, &filter, filter_app, 0, net);
pcap_setfilter(handle, &filter);
/* 截獲一個(gè)包 */
packet = pcap_next(handle, &header);
/* 打印它的長(zhǎng)度 */
printf("Jacked a packet with length of [%d]
", header.len);
/* 關(guān)閉會(huì)話 */
pcap_close(handle);
return(0);
}
這個(gè)程序嗅探被pcap_lookupdev()返回的設(shè)備并將它置為混雜模式。它發(fā)現(xiàn)第一個(gè)包經(jīng)過端口23(telnet)并且告訴用戶此包的大小(以字 節(jié)為單位)。這個(gè)程序又包含了一個(gè)新的調(diào)用pcap_close(),我們將在后面討論(盡管它的名字就足夠證實(shí)它自己的作用)。
我們可以使用的另一種手段則要復(fù)雜的多,并且可能也更為有用。很少有(假如有的話)嗅探器真正的使用pcap_next()。通常,它們使用pcap_loop()或者 pcap_dispatch()(它就是用了pcap_loop())。為了理解這兩個(gè)函數(shù)的用法,你必須理解回調(diào)函數(shù)的思想。
回調(diào)函數(shù)并不是什么新東西,它在許多API里面非常普遍。回調(diào)函數(shù)的概念極其簡(jiǎn)單。設(shè)想我有一個(gè)程序正等待某種排序的事件。為了達(dá)到這個(gè)例子的目的,讓我們假象我的程序想讓用戶在鍵盤上按下一個(gè)鍵,每當(dāng)他們按下了一個(gè)鍵,我就想調(diào)用一個(gè)作相應(yīng)處理的函數(shù)。我所用的函數(shù)就是一個(gè)回調(diào)函數(shù)。用戶每按一個(gè)鍵一次,我的程序就調(diào)用回調(diào)函數(shù)一次。回調(diào)函數(shù)在應(yīng)用在pcap里,取代當(dāng)用戶按下鍵時(shí)被調(diào)用的函數(shù)的是當(dāng)pcap嗅探到一個(gè)數(shù)據(jù)包時(shí)所調(diào)用的函數(shù)。可以定義它們的回調(diào)函數(shù)的兩個(gè)函數(shù)就是pcap_loop()和pcap_dispatch()。此二者在它們的回調(diào)函數(shù)的使用上非常的相似。它們都是每當(dāng)捕捉到一個(gè)符合我們過濾器的包時(shí)調(diào)用器回調(diào)函數(shù)(當(dāng)然是存在一個(gè)過濾器時(shí),假如不存在則所有被嗅探到的包都被送到會(huì)調(diào)函數(shù)處理)。
Pcap_loop()的原型如下:
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
第一個(gè)參數(shù)是會(huì)話句柄,接下來是一個(gè)整型,它告訴pcap_loop()在返回前應(yīng)捕捉多少個(gè)數(shù)據(jù)包(若為負(fù)值則表示應(yīng)該一直工作直至錯(cuò)誤發(fā)生)。第三個(gè)參數(shù)是回調(diào)函數(shù)的名稱(正像其標(biāo)識(shí)符所指,無括號(hào))。最后一個(gè)參數(shù)在有些應(yīng)用里有用,但更多時(shí)候則置為NULL。假設(shè)我們有我們自己的想送往回調(diào)函數(shù)的參數(shù),另外還有pcap_loop()發(fā)送的參數(shù),這就需要用到它。很明顯,必須是一個(gè)u_char類型的指針以確保結(jié)果正確;正像我們稍后見到的, pcap使用了很有意思的方法以u(píng)_char指針的形勢(shì)傳遞信息。在我們展示了一個(gè)pcap是怎樣做的例子之后就很輕易去做了。若是還不行就參考你的本地的C引用文本,作為一個(gè)指針的解釋那就超出了本文的范圍。 Pcap_dispatch()的用法幾乎相同。唯一不同的是它們?nèi)绾翁幚沓瑫r(shí)(還記得在調(diào)用pcap_open_live()時(shí)怎樣設(shè)置超時(shí)嗎?這就是它起作用的地方)。Pcap_loop()忽略超時(shí)而pcap_dispatch()則不。關(guān)于它們之間區(qū)別的更深入的討論請(qǐng)參見pcap的手冊(cè)頁(yè)。
在提供使用pcap_loop()的示例之前,我們必須檢查我們的回調(diào)函數(shù)的格式。我們不能武斷的定義回調(diào)函數(shù)的原型,否則pcap_loop()將會(huì)不知道如何去使用它。因此我們使用這樣的格式作為我們的回調(diào)函數(shù)的原型:
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
讓我們更細(xì)致的考察它。首先,你會(huì)注重到該函數(shù)返回void類型,這是符合邏輯的,因?yàn)閜cap_loop()不知道如何去處理一個(gè)回調(diào)返回值。第一個(gè)參數(shù)相應(yīng)于pcap_loop()的最后一個(gè)參數(shù)。每當(dāng)回調(diào)函數(shù)被調(diào)用時(shí),無論最后一個(gè)參數(shù)傳給pcap_loop()什么值,這個(gè)值都會(huì)傳給我們回調(diào)函數(shù)的第一個(gè)參數(shù)。第二個(gè)參數(shù)是pcap頭文件定義的,它包括數(shù)據(jù)包被嗅探的時(shí)間、大小等信息。結(jié)構(gòu)體pcap_pkhdr在pcap.h中定義如下:
struct pcap_pkthdr {
struct timeval ts; /* 時(shí)間戳 */
bpf_u_int32 caplen; /* 已捕捉部分的長(zhǎng)度 */
bpf_u_int32 len; /* 該包的脫機(jī)長(zhǎng)度 */
};
這些量都相當(dāng)明了。最后一個(gè)參數(shù)在它們中是最有意思的,也最讓pcap程序新手感到迷惑。這又是一個(gè)u_char指針,它包含了被pcap_loop()嗅探到的所有包。
但是你怎樣使用這個(gè)我們?cè)谠屠锓Q為packet的變量呢?一個(gè)數(shù)據(jù)包包含許多屬性,因此你可以想象它不只是一個(gè)字符串,而實(shí)質(zhì)上是一個(gè)結(jié)構(gòu)體的集合(比如,一個(gè)TCP/IP包會(huì)有一個(gè)以太網(wǎng)的頭部,一個(gè)IP頭部,一個(gè)TCP頭部,還有此包的有效載荷)。這個(gè)u_char就是這些結(jié)構(gòu)體的串聯(lián)版本。為了使用它,我們必須作一些有趣的匹配工作。
首先,在匹配它們之前必須定義這些實(shí)際的結(jié)構(gòu)體。下面就是我用來描述一個(gè)通過以太網(wǎng)的TCP/IP包的結(jié)構(gòu)體的定義。我使用的所有這些定義都是直接從POSIX庫(kù)中提取的。通常,我只簡(jiǎn)單的使用那些庫(kù)中的定義即可,但據(jù)我的經(jīng)驗(yàn)不同平臺(tái)的庫(kù)之間有稍微的差別,這使得它實(shí)現(xiàn)起來變得混亂。因此,為達(dá)到示例的目的,我就避免那些混亂而簡(jiǎn)單的復(fù)制這些有關(guān)的結(jié)構(gòu)體。所有這些都能在你的本地unix系統(tǒng)中的include/netinet中找到。下面就是這些結(jié)構(gòu)體:

/* 以太網(wǎng)幀頭部 */
struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN]; /* 目的主機(jī)的地址 */
u_char ether_shost[ETHER_ADDR_LEN]; /* 源主機(jī)的地址 */
u_short ether_type; /* IP? ARP? RARP? etc */
};
/* IP數(shù)據(jù)包的頭部 */
struct sniff_ip {
#if BYTE_ORDER == LITTLE_ENDIAN
u_int ip_hl:4, /* 頭部長(zhǎng)度 */
ip_v:4; /* 版本號(hào) */
#if BYTE_ORDER == BIG_ENDIAN
u_int ip_v:4, /* 版本號(hào) */
ip_hl:4; /* 頭部長(zhǎng)度 */
#endif
#endif /* not _IP_VHL */
u_char ip_tos; /* 服務(wù)的類型 */
u_short ip_len; /* 總長(zhǎng)度 */
u_short ip_id; /*包標(biāo)志號(hào) */
u_short ip_off; /* 碎片偏移 */
#define IP_RF 0x8000 /* 保留的碎片標(biāo)志 */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* 多碎片標(biāo)志*/
#define IP_OFFMASK 0x1fff /*分段位 */
u_char ip_ttl; /* 數(shù)據(jù)包的生存時(shí)間 */
u_char ip_p; /* 所使用的協(xié)議 */
u_short ip_sum; /* 校驗(yàn)和 */
struct in_addr ip_src,ip_dst; /* 源地址、目的地址*/
};
/* TCP 數(shù)據(jù)包的頭部 */
struct sniff_tcp {
u_short th_sport; /* 源端口 */
u_short th_dport; /* 目的端口 */
tcp_seq th_seq; /* 包序號(hào) */
tcp_seq th_ack; /* 確認(rèn)序號(hào) */
#if BYTE_ORDER == LITTLE_ENDIAN
u_int th_x2:4, /* 還沒有用到 */
th_off:4; /* 數(shù)據(jù)偏移 */
#endif
#if BYTE_ORDER == BIG_ENDIAN
u_int th_off:4, /* 數(shù)據(jù)偏移*/
th_x2:4; /*還沒有用到 */
#endif
u_char th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_FLAGS (TH_FINTH_SYNTH_RSTTH_ACKTH_URGTH_ECETH_CWR)
u_short th_win; /* TCP滑動(dòng)窗口 */
u_short th_sum; /* 頭部校驗(yàn)和 */
u_short th_urp; /* 緊急服務(wù)位 */
};
注:在Slackware Linux 8(內(nèi)核版本2.2.19)上我發(fā)現(xiàn)使用以上結(jié)構(gòu)體的代碼將不能通過編譯。后來證實(shí)問題在于include/fearures.h,它只實(shí)現(xiàn)了一個(gè) POSIX接口,除非定義BSD_SOURCE。假如它沒有被定義,我就只能使用一個(gè)不同的結(jié)構(gòu)體去定義TCP頭部。使它們工作在FreeBSD或 OpenBSD系統(tǒng)上的更為通用的解決方法如下:
#define _BSD_SOURCE 1
事先要包含你自己的所有頭文件。這將確保正常使用BSD風(fēng)格的API。假如不想這樣做,那你可以改變TCP頭結(jié)構(gòu)(點(diǎn)此鏈接即可,內(nèi)含注釋)。
那么所有這些與pcap還有神秘的u_char是怎么關(guān)聯(lián)的呢?看,幸運(yùn)的是pcap嗅探數(shù)據(jù)包時(shí)正是使用的這些結(jié)構(gòu)。接下來,它簡(jiǎn)單的創(chuàng)建一個(gè) u_char字符串并且將這些結(jié)構(gòu)體填入。那么我們?cè)鯓硬拍軈^(qū)分它們呢?預(yù)備好見證指針最實(shí)用的好處之一吧(在此,我可要刺激刺激那些堅(jiān)持說指針無用的C 程序新手了)。
我們?cè)僖淮渭俣ㄒ獙?duì)以太網(wǎng)上的TCP/IP包進(jìn)行處理。同樣的手段可以應(yīng)用于任何數(shù)據(jù)包,唯一的區(qū)別是你實(shí)際所使用的結(jié)構(gòu)體的類型。讓我們從聲明分解u_char包的變量開始:
const struct sniff_ethernet *ethernet; /* 以太網(wǎng)幀頭部*/
const struct sniff_ip *ip; /* IP包頭部 */
const struct sniff_tcp *tcp; /* TCP包頭部 */
const char *payload; /* 數(shù)據(jù)包的有效載荷*/
/*為了讓它的可讀性好,我們計(jì)算每個(gè)結(jié)構(gòu)體中的變量大小*/
int size_ethernet = sizeof(struct sniff_ethernet);
int size_ip = sizeof(struct sniff_ip);
int size_tcp = sizeof(struct sniff_tcp);
現(xiàn)在我們開始讓人感到有些神秘的匹配:
ethernet = (struct sniff_ethernet*)(packet);
ip = (struct sniff_ip*)(packet + size_ethernet);
tcp = (struct sniff_tcp*)(packet + size_ethernet + size_ip);
payload = (u_char *)(packet + size_ethernet + size_ip + size_tcp);

此處如何工作?考慮u_char在內(nèi)存中的層次。基本的,當(dāng)pcap將這些結(jié)構(gòu)體填入u_char的時(shí)候是將這些數(shù)據(jù)存入一個(gè)字符串中,那個(gè)字符串將被送入我們的會(huì)調(diào)函數(shù)中。反向轉(zhuǎn)換是這樣的,不考慮這些結(jié)構(gòu)體制中的值,它們的大小將是一致的。例如在我的平臺(tái)上,一個(gè)sniff_ethernet結(jié)構(gòu)體的大小是14字節(jié)。一個(gè)sniff_ip結(jié)構(gòu)體是20字節(jié),一個(gè)sniff_tcp結(jié)構(gòu)體也是20字節(jié)。 u_char指針正是包含了內(nèi)存地址的一個(gè)變量,這也是指針的實(shí)質(zhì),它指向內(nèi)存的一個(gè)區(qū)域。簡(jiǎn)單而言,我們說指針指向的地址為x,假如三個(gè)結(jié)構(gòu)體恰好線性排列,第一個(gè)(sniff_ethernet)被裝載到內(nèi)存地址的x處則我們很輕易的發(fā)現(xiàn)其他結(jié)構(gòu)體的地址,讓我們以表格顯示之:
Variable Location (in bytes)
sniff_ethernet X
sniff_ip X + 14
sniff_tcp X + 14 + 20
payload X + 14 + 20 + 20
結(jié)構(gòu)體sniff_ethernet正好在x處,緊接著它的sniff_ip則位于x加上它本身占用的空間(此例為14字節(jié)),依此類推可得全部地址。
注重:你沒有假定你的變量也是同樣大小是很重要的。你應(yīng)該總是使用sizeof()來確保尺寸的正確。這是因?yàn)檫@些結(jié)構(gòu)體中的每個(gè)成員在不同平臺(tái)下可以有不同的尺寸。
到現(xiàn)在,我們已經(jīng)知道了怎樣設(shè)置回調(diào)函數(shù),調(diào)用它,弄清被嗅探到的數(shù)據(jù)包的屬性。你可能正期待著寫出一個(gè)可用的包嗅探器。因?yàn)榇a的長(zhǎng)度關(guān)系,我不想列在這篇文章里。你可以點(diǎn)擊這里下載并測(cè)試它。
結(jié)束語(yǔ)
到此為止,你應(yīng)該可以寫出一個(gè)基于pcap的包嗅探器了。你已經(jīng)學(xué)習(xí)了基本的概念:打開一個(gè)pcap會(huì)話,有關(guān)它的全體屬性,嗅探數(shù)據(jù)包,使用過濾器,使用回調(diào)函數(shù),等等。現(xiàn)在是進(jìn)行數(shù)據(jù)包嗅探的時(shí)候了。
作者Blog:http://blog.csdn.net/plowboy/
相關(guān)文章Programming with pcap

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 永丰县| 湘潭县| 托克托县| 团风县| 宁强县| 哈密市| 邓州市| 延庆县| 黔江区| 漠河县| 米易县| 通州区| 小金县| 普兰县| 盱眙县| 河北区| 宕昌县| 那曲县| 卓资县| 定远县| 顺昌县| 高雄市| 仁布县| 无棣县| 临猗县| 铜山县| 成武县| 滦南县| 专栏| 和平区| 安西县| 环江| 霍州市| 广宗县| 武定县| 会同县| 通化市| 永登县| 屏东市| 融水| 永顺县|