WinPcap編程(二),winpcap編程
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;
}
非回調函數