————————————————
版權聲明:本文爲CSDN博主「Ezioooooo」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/u012877472/article/details/49817875網絡
1、配置(VS2015)tcp
項目右鍵屬性,在C/C++目錄預處理器添加WPCAP和HAVE_REMOTE,預編譯頭改成不使用預編譯頭,連接器輸入wpcap.lib和ws2_32.lib,VC++目錄包含目錄添加下載的include文件,庫目錄添加下載的lib文件。ide
2、獲取設備列表函數
(編寫winpcap程序的第一件事) oop
pcap_findalldevs_ex()函數來實現這個功能:建立一個能夠被函數pcap_open()打開的網絡設備鏈表優化
pcap_findalldevs_ex(
char * source, //字符指針,保存來源的位置,設爲PCAP_SRC_IF_STRING;
struct pcap_rmthauth* auth, //指向pcap_rmtauth結構體的指針,保存須要遠程設備捕獲協議認證的信息。捕獲本地設備設爲NULL;
pcap_if_t** alldevs, //一個pcap_if_t結構體指針,函數返回時用來保存找到的適配器的信息;
char* errbuf //用來保存錯誤信息;
); //返回值:若是返回0,表示函數運行成功,說明找到合適的適配器,經過alldevs參數返回;若是返回-1,發生錯誤,或者沒有在本地找到合適的適配器;spa
pcap_if_t結構體定義以下:.net
pcap_if* next; //指向鏈表的下一個節點,若是不爲空指向一個pcap_if元素;指針
char* name; //字符串指針,存儲傳向pcap_open_live函數的設備名稱;blog
char* description; //設備的描述;
pcap_addr* addresses;//這項這個設備接口地址鏈表的第一個元素;
u_int flags; //當前惟一的值是PCAP_IF_LOOPBACK,噹噹前接口是迴路接口的時候設置;
當咱們完成設備列表的使用後,應當調用pcap_freealldevs()函數釋放內存資源。
3、獲取已安裝設備的詳細信息
函數pcap_findalldevs_ex()返回的pcap_if結構體中,都有一個pcap_addr的結構體,這個結構體用來保存地址信息。
pcap_addr* next;//指向下一個節點
sockaddr* addr;//一個地址列表
sockaddr* netmask;//一個掩碼列表
sockaddr* broadaddr;//一個廣播地址列表
sockaddr* dstaddr;//一個目的地址列表
sockaddr結構用來存儲與Windows套接字通信的計算機上的一個IP地址。
4、打開適配器並捕獲數據包
①打開適配器:函數pcap_open():
pcap_t* pcap_open (
const char * source, //設備名稱
int snaplen, //制定要捕獲數據包中的哪些部分,將值定爲65535確信總能收到完整數據包(比最大MTU大
int flags, //*最重要,用來指示適配器是否要被設置成混雜模式(無論這個數據包是否是發給個人,我都會去捕獲。WinPcap能捕獲其餘主機的全部的數據包)
int read_timeout, //讀取超時時間
struct pcap_rmtauth * auth, //遠程機器驗證
char * errbuf //錯誤緩衝池,存儲錯誤信息
);
函數返回:一個能夠做爲調用參數的指向pcap_t結構的指針,而且指定一個WinPcap會話。若是出現錯誤,返回NULL,errbuf中存儲錯誤信息。
typedef struct pcap pcap_t //一個已打開的捕捉實例的描述符
②捕獲工做:
pcap_dispatch() 或 pcap_loop()函數:
int pcap_loop (
pcap_t * p, //pcap的句柄
int cnt, //捕獲包的數量,若是是負數表示永不中止直到出現錯誤
pcap_handler callback, //回調函數,當捕獲到數據包時調用此函數
u_char * user //留給用戶使用的
);
其中回調函數:
void(*) pcap_handler(
u_char *user, //函數pcap_loop()傳遞過來的,就是pcap_loop()函數中的user參數
const struct pcap_pkthdr *pkt_header, //表示捕獲到的數據包的基本信息
const u_char *pkt_data //表示捕獲到的數據包的內容
);
結構體:
struct pcap_pkthdr {
struct timeval ts; /* 時間戳 */
bpf_u_int32 caplen; /* 已捕獲部分的長度 */
bpf_u_int32 len; /* 該包的脫機長度 */
}; //保存捕獲到的包的基本信息,由pcap_loop函數自動填充
③不用回調方法捕獲:
pcap_next_ex()函數代替pcap_loop()函數來實現捕獲數據包,當只有顯示調用時才能捕獲數據包。
int pcap_next_ex (
pcap_t * p, //句柄
struct pcap_pkthdr ** pkt_header, //指向一個pcap_pkthdr結構的指針,用於保存捕獲數據包的基本信息
const u_char ** pkt_data //保存捕獲的數據
);
//捕獲一個數據包,而後用捕獲的數據包填充pkt_header和pkt_data參數,根據結果不一樣,返回不一樣的數字(1成功)。
5、過濾數據包
pcap_compile() 和 pcap_setfilter() 函數:
int pcap_compile(
pcap_t *p, //返回數據包捕獲的指針
struct bpf_program *fp, //一個bpf_program結構的指針,用於過濾,在pcap_compile()函數中被賦值。
char *str, //規定過濾規則
int optimize, //控制結果代碼的優化。規定了在結果代碼上的選擇是否被執行
bpf_u_int32 netmask); //指定本地網絡的網絡掩碼(該網卡的子網掩碼),經過pcap_lookupnet()獲取
將str指定的規則整合到分fp過濾程序中,並生成過濾程序入口地址,用於過濾選擇指望的數據包。成功返回0,不然返回-1。
過濾規則str由一個或多個原語組成,若是爲""表示不進行過濾。原語一般由一個標識id(名字或序列)和限定詞(輸入(type,指明哪些東西是id所表明的,可能的輸入是host,net和port,默認host),方向(dir,id指明瞭一個特定的傳輸方向,可能的方向是src,dst,src or dst,默認src/dst),協議(proto,所匹配的協議ether,fddi,tr,ip,ip6,arp,rarp,decnet,tcp和udp,默認全部均可))組成。很複雜,記得查詢。
6、分析數據包
此時已經能夠能夠捕獲並過濾網絡流量,以TCP/UDP爲例分析。
網絡中的數據包每通過一個層次都會加上那個層的報頭來標註一些重要的信息。
捕獲到的數據包首先有個mac報頭,14字節,包含6字節目的mac地址、6字節源mac地址,和2字節上一層協議(不關注mac),接下來是IP數據包有20字節的報頭。
IP報頭定義:
/* IPv4 首部 */
typedef struct ip_header {
u_char ver_ihl; // 版本 (4 bits) + 首部長度 (4 bits)
u_char tos; // 服務類型(Type of service)
u_short tlen; // 總長(Total length)
u_short identification; // 標識(Identification)
u_short flags_fo; // 標誌位(Flags) (3 bits) + 段偏移量(Fragment offset) (13 bits)
u_char ttl; // 存活時間(Time to live)
u_char proto; // 協議(Protocol)
u_short crc; // 首部校驗和(Header checksum)
ip_address saddr; // 源地址(Source address)
ip_address daddr; // 目的地址(Destination address)
u_int op_pad; // 選項與填充(Option + Padding)
}ip_header;
經過首部長度(ip_len = (ih->ver_ihl & 0xf) * 4;)獲得UDP數據包的位置。
UDP報頭定義:
/* UDP 首部*/
typedef struct udp_header {
u_short sport; // 源端口(Source port)
u_short dport; // 目的端口(Destination port)
u_short len; // UDP數據包長度(Datagram length)
u_short crc; // 校驗和(Checksum)
TCP報頭定義:
/* tcp 首部 */
typedef struct tcp_header {
u_short sport; //源端口
u_short dport; //目的端口
u_int th_seq; //序列號
u_int th_ack; //確認號
u_short doff : 4, hlen : 4, fin : 1, syn : 1, rst : 1, psh : 1, ack : 1, urg : 1, ece : 1, cwr : 1; //4 bits 首部長度,6 bits 保留位,6 bits 標誌位
u_short th_window; //窗口大小
u_short th_sum; //校驗和
u_short th_urp; //緊急指針
}tcp_header;
}udp_header;
UDP報頭:
TCP報頭:
7、保存數據包到堆文件
①pcap_dump_open()函數打開一個能夠保存數據的文件。
pcap_dumper_t* pcap_dump_open (
pcap_t * p, //一個libpcap存儲文件的描述符,對用戶不可見
const char * fname //要寫入的文件名
);//只有當接口打開時,調用 pcap_dump_open() 纔是有效的。 這個調用將打開一個堆文件,並將它關聯到特定的接口上。
②pcap_dump()函數將數據包寫入用戶指定的文件中。
void pcap_dump (
u_char * user, //文件描述符dumpfp
const struct pcap_pkthdr * h, //pkt_header
const u_char * sp //要用pkt_data
);
8、從堆文件讀取數據包
pcap_open_offline()函數將堆文件打開。
pcap_t* pcap_open_offline (
const char * fname, //要打開的文件名
char * errbuf //保存錯誤信息
);
經過pcap_createsrcstr()函數根據新WinPcap語法建立一個源字符串。
int pcap_createsrcstr (
char * source, //要存入的源字符串
int type, //要建立的源字符串類型
const char * host, //遠程主機
const char * port, //遠程端口
const char * name, //咱們要打開的文件名
char * errbuf //存儲錯誤信息
); //返回值:0:沒有錯誤;-1:建立出錯,錯誤寫入errbuf中