日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯網營銷解決方案
詳解Linuxtcpdump抓包的原理

tcpdump是一個用于截取網絡分組,并輸出分組內容的工具。憑借強大的功能和靈活的截取策略,使其成為類UNIX系統下用于網絡分析和問題排查的首選工具,本篇文章為大家詳細講解一下Linux tcpdump 抓包的原理。

1.1.1.1 如何實現

先來看看包傳遞過來的流程,如下圖。包從網卡到內存,到內核態(tài),最后給用戶程序使用。我們知道tcpdump程序運行在用戶態(tài),那如何實現從內核態(tài)的抓包呢?

img

這個就是通過libpcap庫來實現的,tcpdump調用libpcap的api函數,由libpcap進入到內核態(tài)到鏈路層來抓包,如下圖。圖中的BPF是過濾器,可以根據用戶設置用于數據包過濾減少應用程序的數據包的包數和字節(jié)數從而提高性能。BufferQ是緩存供應用程序讀取的數據包。我們可以說tcpdump底層原理其實就是libpcap的實現原理。

而libpcap在linux系統鏈路層中抓包是通過PF_PACKET套接字來實現的(不同的系統其實現機制是由差異的),該方法在創(chuàng)建的時候,可以指定第二參數為SOCK_DGRAM或者SOCK_RAW,影響是否扣除鏈路層的首部。

? libpcap在內核收發(fā)包的接口處將skb_clone()拿走的包.

關于內核中如何注冊網絡協議和鉤子函數的過程,此處先不展開,后續(xù)專門講解。我們接下去是看下libpcap的一些實現及其api.

1.1.1.2 libpcap

當在系統中輸入tcpdump –version的時候,輸出的其實還有l(wèi)ibpcap,足見其在tcpdump中的地位。

? 其實最早的編譯系統和過濾引擎是在tcpdump項目中的,后來為了編譯其他抓包的應用,將其獨立出來。現在libpcap提供獨立于平臺的庫和API,來滿足執(zhí)行網絡嗅探。

tcpdump.c正式使用libpcap里的函數完成兩個最關鍵的動作:獲取捕獲報文的接口,和捕獲報文并將報文交給callback。

libpcap支持“伯克利包過濾(BPF)”語法。BPF能夠通過比較第2、3、4層協議中各個數據字段值的方法對流量進行過濾。Libpcap的使用邏輯如下圖:

如果愿意,大家也可以基于libpcap開發(fā)一個類似tcpdump的抓包工具。需要注意的是如果使用分組捕獲設備,只能在單個接口上接收到達的分組。

1.1.1.3 核心函數

我們先來看下libpcap中的一些核心函數,根據函數的功能,可以分為如下幾類:

l? ? 為讀包打開句柄

l? ? 為抓包選擇鏈路層

l? ? 抓包函數

l? ? 過濾器

l? ? 選定抓包方向(進還是出)

l? ? 抓統計信息

l? ? 將包寫入文件打開句柄

l? ? 寫包

l? ? 注入包

l? ? 報告錯誤

l? ? 獲取庫版本信息

官方的介紹查看http://www.tcpdump.org/manpages/pcap.3pcap.html

常用的一些函數如下:

pcap_lookupdev,如果分組捕獲設備未曾指定(-i命令行選項),該函數選擇一個設備。

pcap_open_offine打開一個保存的文件。

pcap_setfilter設置過濾器

pcap_open_live打開選擇的設備。

pcap_next接收一個包

pcap_dump將包寫入到pcap_dump_t結構體

pcap_loopupnet返回分組捕獲設備的網絡地址和子網掩碼,然后在調用pcap_compile時必須指定這個子網掩碼。

pcap_compile把cmd字符數組中構造的過濾器字符串編譯成一個過濾器程序,存放在fcode中。

pcap_setfilter把編譯出來的過濾器程序裝載到分組捕獲設備,同時引發(fā)用該過濾器選取的分組的捕獲。

pcap_datalink返回分組捕獲設備的數據鏈路類型。

等等,那么如何去使用libpcap庫呢,一起來看下。

1.1.1.4 使用準備

先在系統中安裝pcap-dev包(apt-get install pcap-dev),然后創(chuàng)建一個test.c文件如下:

#include

#include

int

main (int argc, char *argv[])

{undefined

char *dev, errbuf[PCAP_ERRBUF_SIZE];

dev = pcap_lookupdev (errbuf);

if (dev == NULL)

{undefined

fprintf (stderr, “Couldn’t find default device: %s\n”, errbuf);

return (2);

}

printf (“Device: %s\n”, dev);

return (0);

}

然后編譯如下:

gcc test.c -lpcap -lpthread

? 就可以執(zhí)行了,在系統中尋找一個可以抓包的接口。

? 有了接口設備,可以繼續(xù)創(chuàng)建嗅探會話了,使用函數

pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)

其中snaplen是pcap抓包的字節(jié)數, promisc 是否啟用混雜模式(不是混雜模式的話就只抓給本機的包。),to_ms是否超時,ebuf存放錯誤信息。

? 創(chuàng)建了嗅探會話之后,就要一個過濾器??梢灾惶崛∥覀兿胍臄祿?。過濾器在應用之前必須要先編譯,調用函數如下:

int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)

第一個參數就是pcap_open_live返回的值,fp 存儲的過濾器的版本,optimize是表示是否需要優(yōu)化,最后netmask是過濾器使用的所在子網掩碼。

? 有了過濾器之后就是要使用編譯器,調用函數:

int pcap_setfilter(pcap_t *p, struct bpf_program *fp)

? 到此整個代碼流程參考如下代碼段:

#include

pcap_t handle; / Session handle */

char dev[] = “rl0”; /* Device to sniff on */

char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */

struct bpf_program fp; /* The compiled filter expression */

char filter_exp[] = “port 23”; /* The filter expression */

bpf_u_int32 mask; /* The netmask of our sniffing device */

bpf_u_int32 net; /* The IP of our sniffing device */

if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {undefined

? fprintf(stderr, “Can’t get netmask for device %s\n”, dev);

? net = 0;

? mask = 0;

}

handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);

if (handle == NULL) {undefined

? fprintf(stderr, “Couldn’t open device %s: %s\n”, dev, errbuf);

? return(2);

}

if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {undefined

? fprintf(stderr, “Couldn’t parse filter %s: %s\n”, filter_exp, pcap_geterr(handle));

? return(2);

}

if (pcap_setfilter(handle, &fp) == -1) {undefined

? fprintf(stderr, “Couldn’t install filter %s: %s\n”, filter_exp, pcap_geterr(handle));

? return(2);

}

1.1.1.5 開始抓包

已經準備好監(jiān)聽抓包,并設置了過濾器,下面就是啟動抓包了。

抓包技術有兩種,一種是一次抓一個包;另一種是等待有n個包的時候在一起抓。

? 先看抓一次抓一個包,使用函數如下:

u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

? 第一個參數就是創(chuàng)建的會話句柄,第二個參數是存放包信息的。

? 這個函數是比較少用的,現在大多數抓包工具都是使用第二種技術抓包的,其用到的函數就是:

int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)

? 第一個參數是創(chuàng)建的會話句柄,第二個參數是數量(抓幾個包),就是這個參數制定抓多少包,抓完就結束了,第三個函數是抓到足夠數量后的回調函數,每次抓到都會調用回調函數,第四個參數經常設置為NULL,在一些應用中會有用。

? 和pcap_loop函數類似的是pcap_dispatch,兩者用法基本一致,主要差異是pcap_dispatch只會執(zhí)行一次回調函數,而pcap_loop會一直調用回調函數處理包。

? 其回調函數的定義如下:

void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);

? 是void型的,第一個參數args是pcap_loop函數的最后一個參數,第二個參數是pcap的頭其包含了抓住的包的信息,第三個就是包本身了。

struct pcap_pkthdr {undefined

? struct timeval ts; /* time stamp */

? bpf_u_int32 caplen; /* length of portion present */

? bpf_u_int32 len; /* length this packet (off wire) */

};

? 關于包本身其實是一個字符串指針,怎么去尋找我的ip頭,tcp頭,以及頭中的內容呢?這就需要是使用C語言中異常強大的指針了,定義一個宏如下:

/* Ethernet addresses are 6 bytes */

#define ETHER_ADDR_LEN 6

/* Ethernet header */

struct sniff_ethernet {undefined

? u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */

? u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */

? u_short ether_type; /* IP? ARP? RARP? etc */

};

/* IP header */

struct sniff_ip {undefined

? u_char ip_vhl; /* version > 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)

/* TCP header */

typedef u_int tcp_seq;

struct sniff_tcp {undefined

? 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 */

};

/* ethernet headers are always exactly 14 bytes */

#define SIZE_ETHERNET 14

const struct sniff_ethernet ethernet; / The ethernet header */

const struct sniff_ip ip; / The IP header */

const struct sniff_tcp tcp; / The TCP header */

const char payload; / Packet payload */

u_int size_ip;

u_int size_tcp;

? 通過以上結構體定義,可以從回調函數的包指針地址出發(fā),逐個找到鏈路幀頭、IP幀頭、TCP幀頭、數據負載了。

? 附上一個實例DEMO鏈接。

實例DEMO

                

網站題目:詳解Linuxtcpdump抓包的原理
URL網址:http://www.dlmjj.cn/article/cdsicig.html