0.
這一次具體講抓包的兩種方法。
(建議)清除ARP表,最好自己寫個批處理命令。快一點。
1.0 抓包步驟
步驟很簡單:先打開適配器列表 --> 選擇適配器 --> 通過遍歷鏈表的方式到達你選擇的適配器位置 --> 打開設備 --> 開始抓包。
每一個步驟都是一個函數。了解步驟,後面就好辦。
首先,了解一個數據類型pcap/pcap_t。它代表一個打開的設備,理解成它就是適配器就行(實際上是這個適配器的描述符)。這個結構體對用戶來說是不透明的,它通過wpcap.dll提供的函數,維護了它的內容。
然後,跳轉至你的設備在WinPcap編程(一)中存在,就不多說。
之後,打開設備的函數:
pcap_t* pcap_open ( const char * source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth * auth, char * errbuf ) View Code備注:
第一個參數:source是我們所要打開的設備。當我們獲取所有的設備之後,這個source就是d->name。不能為NULL。
第二個參數:snaplen是我們抓取的數據包的大小,100就是抓取整個數據包的前100B,65536最大,把整個包都包括了。
第三個參數:flags設置為混雜模式(PCAP_OPENFLAG_PROMISCUOUS ),即不管數據包是否給我,我都去捕獲。這樣可以捕捉局域網內所有的包。
第四個參數: read_timeout設置超時時間,以毫秒計(1s=1000ms)。在適配器上進行讀取數據操作的時候,不管網絡上有沒有包,都會在 read_timeout這個時間內響應。
設置為0意味著沒有超時。沒有數據包到達,讀操作將永遠不會返回。
設置成-1,無論有沒有數據包到達,讀操作都會立即返回。
第五個參數:auth遠程機登錄信息,若本地則為NULL;
第六個參數:errbuf 出錯信息。
1.1抓包的兩種方式:
1.1.1 回調函數的方法進行抓包
每次抓到包就用回調函數進行處理。處理完接著抓包(如果設置了num的話)。
抓包函數:
int pcap_loop ( pcap_t * p, int cnt, pcap_handler callback, u_char * user ) View Code備注0:
第一個參數:P即是打開的設備。
第二個參數:cnt表示捕捉個數。
第三個參數:回調函數指針。對捕捉的數據包進行操作 。
第四個參數:用戶信息,一般為NULL。
函數返回值:
返回值為0,捕捉了cnt次,正常返回。
返回值為-1,發生錯誤。
返回值為-2,使用pcap_breakloop()結束循環。
回調函數抓包還有一種方法, pcap_dispatch() 。
區別是: pcap_ dispatch() 當超時時間到了(timeout expires)就返回 (盡管不能保證) ,而 pcap_loop() 不會因此而返回,只有當 cnt 數據包被捕獲。
所以,pcap_loop()會在一小段時間內,阻塞網絡的利用。pcap_dispatch() 函數一般用於比較復雜的程序中。
1.1.2 不利用回調函數
回調函數比較好,但是有時候需要不用回調函數的方法,特別在多線程裡面。
我用C# + WPF寫的,用回調函數的話一直出現錯誤。仔細考慮,應該是托管與非托管的問題。看了SharpPcap的源碼,貌似也沒有好的解決方案。
【有好的方案的看見了希望能夠提供一下,感謝。】
不利用回調函數抓包的話,方法比較簡單,每次只能抓一個包,加個while()循環就OK了。
int pcap_next_ex ( pcap_t * p, struct pcap_pkthdr ** pkt_header, const u_char ** pkt_data ) View Code
備注:
第一個參數:打開的設備。
第二個參數:WinPcap添加的一些信息。(時間戳,捕捉到包的長度,實際包的長度)。
第三個參數:實際數據包。
函數返回值:
返回值為1,正常返回;
返回值為0,超時後返回一個無效的包;
返回值為-1,發生錯誤;
返回值為-2,讀到EOF。
源碼:
#define WIN32 #include "pcap.h" #include "winsock.h" #include "time.h" /* packet handler 函數原型 */ void packet_handler( u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data ); int main() { pcap_if_t *alldevs; pcap_if_t *d; int inum; int i = 0; pcap_t *adhandle; char errbuf[PCAP_ERRBUF_SIZE]; /* 獲取本機設備列表 */ if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) { fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf); exit(1); } /* 打印列表 */ for (d = alldevs; d; d = d->next) { printf("%d. %s", ++i, d->name); if (d->description) printf(" (%s)\n", d->description); else printf(" (No description available)\n"); } if (i == 0) { printf("\nNo interfaces found! Make sure WinPcap is installed.\n"); return -1; } printf("Enter the interface number (1-%d):", i); scanf_s("%d", &inum); if (inum < 1 || inum > i) { printf("\nInterface number out of range.\n"); /* 釋放設備列表 */ pcap_freealldevs(alldevs); return -1; } /* 跳轉到選中的適配器 */ for (d = alldevs, i = 0; i< inum - 1; d = d->next, i++); /* 打開設備 */ if ((adhandle = pcap_open(d->name, // 設備名 65536, // 65535保證能捕獲到不同數據鏈路層上的每個數據包的全部內容 PCAP_OPENFLAG_PROMISCUOUS, // 混雜模式 0, // 讀取超時時間 NULL, // 遠程機器驗證 errbuf // 錯誤緩沖池 )) == NULL) { fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name); /* 釋放設備列表 */ pcap_freealldevs(alldevs); return -1; } printf("\nlistening on %s...\n", d->description); /* 釋放設備列表 */ pcap_freealldevs(alldevs); /* 開始捕獲 */ pcap_loop(adhandle, 30, packet_handler, NULL); system("pause"); return 0; } /* 每次捕獲到數據包時,libpcap都會自動調用這個回調函數 */ void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) { struct tm ltime = {0}; char timestr[16]; time_t local_tv_sec; /* 將時間戳轉換成可識別的格式 */ local_tv_sec = header->ts.tv_sec; localtime_s(<ime, &local_tv_sec); strftime(timestr, sizeof timestr, "%H:%M:%S", <ime); printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len); } 回調函數 #define WIN32 #include "pcap.h" int main() { pcap_if_t *alldevs; pcap_if_t *d; int inum; int i = 0; pcap_t *adhandle; int res; char errbuf[PCAP_ERRBUF_SIZE]; struct tm ltime; char timestr[16]; struct pcap_pkthdr *header; const u_char *pkt_data; time_t local_tv_sec; int j = 10;//限制只抓10個包 /* 獲取本機設備列表 */ if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) { fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf); exit(1); } /* 打印列表 */ for (d = alldevs; d; d = d->next) { printf("%d. %s", ++i, d->name); if (d->description) printf(" (%s)\n", d->description); else printf(" (No description available)\n"); } if (i == 0) { printf("\nNo interfaces found! Make sure WinPcap is installed.\n"); return -1; } printf("Enter the interface number (1-%d):", i); scanf_s("%d", &inum); if (inum < 1 || inum > i) { printf("\nInterface number out of range.\n"); /* 釋放設備列表 */ pcap_freealldevs(alldevs); return -1; } /* 跳轉到已選中的適配器 */ for (d = alldevs, i = 0; i< inum - 1; d = d->next, i++); /* 打開設備 */ if ((adhandle = pcap_open(d->name, // 設備名 65536, // 要捕捉的數據包的部分 // 65535保證能捕獲到不同數據鏈路層上的每個數據包的全部內容 PCAP_OPENFLAG_PROMISCUOUS, // 混雜模式 1000, // 讀取超時時間 NULL, // 遠程機器驗證 errbuf // 錯誤緩沖池 )) == NULL) { fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name); /* 釋放設列表 */ pcap_freealldevs(alldevs); return -1; } printf("\nlistening on %s...\n", d->description); /* 釋放設備列表 */ pcap_freealldevs(alldevs); /* 獲取數據包 */ while ((res = pcap_next_ex(adhandle, &header, &pkt_data)) >= 0&&j>0){ if (res == 0) /* 超時時間到 */ continue; /* 將時間戳轉換成可識別的格式 */ local_tv_sec = header->ts.tv_sec; localtime_s(<ime,&local_tv_sec); strftime(timestr, sizeof timestr, "%H:%M:%S", <ime); j--; printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len); } if (res == -1){ printf("Error reading the packets: %s\n", pcap_geterr(adhandle)); return -1; } system("pause"); return 0; } 非回調函數