本文介紹數據包的捕獲與按照協議格式進行數據包分析。數據包捕獲採用了libpcap抓包框架,能夠分析IP、TCP、UDP、ARP、RARP等類型的數據包。
一、數據包捕獲
數據包的捕獲採用了libpcap函數庫,下面介紹捕獲過程中各函數的使用步驟。1. 獲取網絡接口
char *pcap_lookupdev(char *errbuf)
用於返回可被pcap_open_live()或pcap_lookupnet()函數調用的網絡設備名指針。如果函數出錯,則返回NULL,同時errbuf中存放相關的錯誤消息。
2. 打開網絡接口
pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
上面這個函數會返回指定接口的pcap_t類型指針,後面的所有操作都要使用這個指針。
第二個參數是對於每個數據包,從開頭要抓多少個字節。
第三個參數指定是否打開混雜模式(Promiscuous Mode),0表示非混雜模式,任何其他值表示混合模式。如果要打開混雜模式,那麼網卡必須也要打開混雜模式。
第四個參數指定需要等待的毫秒數,0表示一直等待直到有數據包到來。
第五個參數是存放出錯信息的數組。
3. 設置過濾器
雖然不設置過濾器本實驗也能分析各種協議的數據包,但是設置過濾器能獲取特定的包,增強實驗的目的性。實現這一過程由pcap_compile()與pcap_setfilter()這兩個函數完成。int pcap_compile(pcap_t *p, strUCt bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
第一個參數是會話句柄。接下來的是我們存儲被編譯的過濾器版本的地址的引用。再接下來的則是表達式本身,存儲在規定的字符串格式裏。再下邊是一個定義表達式是否被優化的整形量(0爲false,1爲true,標準規定)。最後,我們必須指定應用此過濾器的網絡掩碼。函數返回-1爲失敗,其他的任何值都表明是成功的。表達式被編譯之後就可以使用了。
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
第一個參數是會話句柄,第二個參數是被編譯表達式版本的引用。
4. 獲取數據包
Libpcap中獲取數據包最常用的函數是int pcap_loop()。int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
此函數提供了抓包工具的核心功能。第一個參數是第2步返回的pcap_t類型的指針。第二個參數是需要抓的數據包的個數,一旦抓到了cnt個數據包,pcap_loop立即返回。負數的cnt表示pcap_loop永遠循環抓包,直到出現錯誤。第三個參數是一個回調函數指針,它必須是如下的形式:void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
其中:第一個參數是pcap_loop的最後一個參數,當收到足夠數量的包後pcap_loop會調用callback回調函數,同時將pcap_loop()的user參數傳遞給它。第二個參數是收到的數據包的pcap_pkthdr類型的指針。第三個參數是收到的數據包數據。後面,我們將調用回調函數callback() 來進行數據包的分析,回調函數提供了數據包分析的核心功能。
二、數據包分析
1. 數據包分析原理
在libpcap定義的回調函數中,有一個參數const u_char *packet,它是一個u_char類型的指針,包含了所有被pcap_loop()所嗅探到的包。每一個包其實是一塊連續的內存,packet指針指向這塊內存的起始位置。一個數據包包含許多屬性,因此可以看做一個包含許多成員變量的結構體。事先按照協議的格式定義好結構體,然後用這塊內存裝載,則結構體的成員變量將會與數據包信息一一對應。這樣,就拿到了數據包的信息。下面是一些數據包的結構體定義。各協議頭部可以自己定義,也可以查看Linux /usr/include/netinet 下的各種協議頭部的結構體定義。
/* IP header*/
struct ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
#define IP_HL(ip) (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip) (((ip)->ip_vhl) >> 4)
};
/* ARP Header*/
struct arp {
u_int16_t htype; /* Hardware Type */
u_int16_t ptype; /* Protocol Type */
u_char hlen; /* Hardware Address Length */
u_char plen; /* Protocol Address Length */
u_int16_t oper; /* Operation Code */
u_char sha[6]; /* Sender hardware address */
u_char spa[4]; /* Sender IP address */
u_char tha[6]; /* Target hardware address */
u_char tpa[4]; /* Target IP address */
#define ARP_REQUEST 1
#define ARP_REPLY 2
};
/* TCP header */
typedef u_int tcp_seq;
struct tcp {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
u_char th_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
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_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
u_short th_win; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
};