MFC WinPcap編寫乙個嗅探器之七(協議)

2022-05-02 18:00:09 字數 4117 閱讀 5374

這一節是本系列教程的結尾了,內容也比較簡單,主要是對網路協議進行分析,其實學過計算機網路的同學完全可以略過

在整個專案中需要有乙個標頭檔案存放各層協議的頭部定義,我把它們放在了head.h中,這個標頭檔案都有什麼呢,首先放幾個關於協議的巨集定義,這樣可以讓整個程式顯得更加清晰:

1

/*網路層協議型別 */2

#define ip 0x0800

3#define arp 0x0806

4#define rarp 0x8035 56

/*傳輸層型別 */7

#define icmp 0x01

8#define igmp 0x02

9#define tcp 0x06

10#define egp 0x08

11#define udp 0x11

12#define ipv6 0x29

13#define ospf 0x59

之後就是存放協議的頭部定義了,對照著頭部定義,用合適的型別定義各個字段,比如ip協議頭:

1 typedef struct

ip_address

2ip_address;89

/*ipv4 首部

*/10 typedef struct

ip_header

11ip_header;

當然有些位置的意義是小於乙個位元組大小的,比如tcp協議中的標誌位欄位,有urg,ack,psh,rst,syn,fin,這些在之後用&&運算可以方便求得

當資料報被pcap_next_ex()函式捕獲後,我們需要其中的兩個引數,pcap_next_ex()函式原型如下:

1

int pcap_next_ex(pcap_t *, struct pcap_pkthdr **, const u_char **);

第乙個引數是乙個已開啟的捕捉例項,也就是之前我們選擇的介面卡,第二個引數是乙個結構體,其中內容有:

1

struct

pcap_pkthdr

2

因為在某些情況下你不能保證捕獲的包是完整的,例如乙個包長1480,但是你捕獲到1000的時候,可能因為某些原因就中止捕獲了,所以caplen是記錄實際捕獲的包長,也就是1000,而len就是1480。第三個引數就是資料報的內容後兩個引數是對我們有用的,將其傳遞到顯示資料報概略的函式showpacketlist中

每當捕獲以個資料報,需要進行儲存,在mfc中可以用carray,carray是乙個動態陣列,它的用法是:carrayvar1;前乙個引數是指定儲存在陣列中的物件的型別,後乙個引數是指定用於訪問儲存在陣列中物件的引數型別。其實這兩個引數一般是一樣的型別,程式中儲存每次抓包的pkt_header和pkt_data就用如下carray表示:

carray

struct pcap_pkthdr *,const

struct pcap_pkthdr *>m_pktheaders;

carray

m_pktdatas;

carray類的一些常用成員函式有:getat返回在給定索引上的值;add增加乙個元素;getsize獲得此陣列中的元素數 

儲存完捕獲的資料報,就可以利用pkt_data的資訊來分析資料報,在分析資料報中的資料時注意,資料報的資料是按照小端模式儲存的,對於大於1位元組的內容需要用noths或是nothl進行轉化(前者對應short,後者對應long)。舉乙個例子,比如數字0x12 34 56 78在記憶體中的表示形式為:0x78 | 0x56 | 0x34 | 0x12 ,這時就需要noths來轉化為正確的順序。顯示資料報概要的函式部分**如下:

1 ethernet_header *eh;

2 eh = (ethernet_header *)pkt_data;

3 str.format(_t("

%x:%x:%x:%x:%x:%x

"),eh->saddr.byte1,eh->saddr.byte2,eh->saddr.byte3,eh->saddr.byte4,eh->saddr.byte5,eh->saddr.byte6);

4 m_list1.setitemtext(ncount,2

,str);

5 str.format(_t("

%x:%x:%x:%x:%x:%x

"),eh->daddr.byte1,eh->daddr.byte2,eh->daddr.byte3,eh->daddr.byte4,eh->daddr.byte5,eh->daddr.byte6);

6 m_list1.setitemtext(ncount,3

,str);

7 str.format(_t("

%ld"),pheader->len);

8 m_list1.setitemtext(ncount,4

,str);9/*

處理網路層

*/10

switch(ntohs(eh->type))11{

12case

ip:13

{14 ip_header *ih;

15const u_char *ip_data;

16 ip_data=pkt_data+14

;17 ih = (ip_header *)ip_data;

18 u_int ip_len;//

ip首部長度

19 ip_len = (ih->ver_ihl & 0xf) * 4

;20 str.format(_t("

%d.%d.%d.%d

"),ih->saddr.byte1,ih->saddr.byte2,ih->saddr.byte3,ih->saddr.byte4);

21 m_list1.setitemtext(ncount,6

,str);

22 str.format(_t("

%d.%d.%d.%d

"),ih->saddr.byte1,ih->saddr.byte2,ih->saddr.byte3,ih->saddr.byte4);

23 m_list1.setitemtext(ncount,7

,str);

24/*

處理傳輸層

*/25

switch(ih->type)26{

27case tcp:

就這樣陷入一層一層的分析中,對於資料報的詳細分析,也就是在樹形結構中顯示的**也與次類似,不做分析。

在統計部分,除了有統計各種資料報的數目外,還有一部分是流量分析:

這一部分的實現也比較簡單,對於某些常用的軟體,都是通過固定埠進行資料傳輸的,比如qq 預設採用 udp 通訊方式,埠 8000,8001。如果 udp 的兩個埠不通,會自動轉換到 tcp 80 埠或者 tcp 443 埠進行通訊。 qq 同時也支援 http **模式及 sock5**模式。阿里旺旺採用 tcp 通訊方式,預設登入埠為 16000,當 16000 埠不通時,則跳轉到 443 埠進行通訊。所以通過對固定埠的監聽,可以知道哪些軟體產生了流量(這樣做並不完美,有興趣的可以分析應用層協議)

1

if(ntohs( th->sport ) == 0x3e80 || ntohs( th->dport ) == 0x3e80) //

阿里旺旺流量為tcp埠16000

2 m_wangcount++;

3if(ntohs( th->sport ) == 0x747 || ntohs( th->dport ) == 0x747) //

msn流量為tcp埠1863

4 m_msncount++;

寫在最後:

編寫乙個makefile

什麼是makefile?對於大多數的windows程式設計師來講,makefile可能不是那麼重要,因為windows的ide都為程式設計師做好了這個工作。但是在linux下程式設計,會不會寫makefile,從側面上說明乙個人是否具備完成大型工程的能力。makefile的作用 makefile是用...

編寫乙個webpack loader

loader是一種打包的方案,webpack預設只識別js結尾的檔案,當遇到其他格式的檔案後,webpack並不知道如何去處理。此時,我們可以定義一種規則,告訴webpack當他遇到某種格式的檔案後,去求助於相應的loader。新建loaders資料夾並建立三個loaders檔案 remove co...

如何編寫乙個 XML Schema

看看這個名為 shiporder.xml 的 xml 文件 george bush john adams oxford street london uk empire burlesque special edition 110.90 hide your heart 19.90 說明 上面的xml文件...