WinPcap编程
// tt.cpp : 定義控制臺應用程序的入口點。
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
//包含頭文件,(可以在工具-->選項-->項目和解決方案-->VC++目錄 里面添加,也可以直接把頭文件和庫文件拷到“C:\Program Files\Microsoft Visual Studio 8\VC”目錄下)
#include <pcap.h>
//也可以在項目屬性里面添加“連接器-->輸入”
#pragma comment(lib,"wpcap.lib")
#pragma comment(lib,"ws2_32.lib")
//采用頭文件包含是不行的,必須在項目的預處理器定義字段那,添加“;WPCAP;HAVE_REMOTE”。
//#define HAVE_REMOTE
//#define WPCAP
#define IPTOSBUFFERS??? 12
//4字節的IP地址
typedef struct ip_address
{?? ?
?? ?u_char byte1;?? ?
?? ?u_char byte2;?? ?
?? ?u_char byte3;?? ?
?? ?u_char byte4;
} ip_address;
//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;
//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)
} udp_header;
void ifprint(pcap_if_t *d);
char *iptos(u_long in);
char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen);
//每次捕獲到數據包時,libpcap都會自動調用這個回調函數
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
//回調函數原型,過濾數據包
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
//打印所有可用信息
void ifprint(pcap_if_t *d)
{
?? ?pcap_addr_t *a; ?
?? ?char ip6str[128];
?? ?//設備名(Name)
?? ?printf("網卡名稱:%s\n",d->name); ?
?? ?//設備描述(Description)
?? ?if (d->description)?? ?
?? ??? ?printf("\t設備描述: %s\n",d->description);? /* Loopback Address*/ ?
?? ?printf("\tLoopback: %s\n",(d->flags & PCAP_IF_LOOPBACK)?"yes":"no");? /* IP addresses */ ?
?? ?for(a=d->addresses;a;a=a->next)
?? ?{?? ?
?? ??? ?printf("\tAddress Family: #%d\n",a->addr->sa_family);?? ?
?? ??? ?switch(a->addr->sa_family)?? ?
?? ??? ?{???? ?
?? ??? ?case AF_INET:?????? ?
?? ??? ??? ?printf("\tAddress Family Name: AF_INET\n");?????? ?
?? ??? ??? ?if (a->addr)???????? ?
?? ??? ??? ??? ?printf("\tAddress: %s\n",iptos(((struct sockaddr_in *)a->addr)->sin_addr.s_addr));?????? ?
?? ??? ??? ?if (a->netmask)???????? ?
?? ??? ??? ??? ?printf("\tNetmask: %s\n",iptos(((struct sockaddr_in *)a->netmask)->sin_addr.s_addr));?????? ?
?? ??? ??? ?if (a->broadaddr)???????? ?
?? ??? ??? ??? ?printf("\tBroadcast Address: %s\n",iptos(((struct sockaddr_in *)a->broadaddr)->sin_addr.s_addr));?????? ?
?? ??? ??? ?if (a->dstaddr)???????? ?
?? ??? ??? ??? ?printf("\tDestination Address: %s\n",iptos(((struct sockaddr_in *)a->dstaddr)->sin_addr.s_addr));?????? ?
?? ??? ??? ?break;???? ?
?? ??? ?case AF_INET6:?????? ?
?? ??? ??? ?printf("\tAddress Family Name: AF_INET6\n");?????? ?
?? ??? ??? ?if (a->addr)???????? ?
?? ??? ??? ??? ?printf("\tAddress: %s\n", ip6tos(a->addr, ip6str, sizeof(ip6str)));????? ?
?? ??? ??? ?break;???? ?
?? ??? ?default:?????? ?
?? ??? ??? ?printf("\tAddress Family Name: Unknown\n");?????? ?
?? ??? ??? ?break;?? ?
?? ??? ?} ?
?? ?} ?
?? ?printf("\n");
}
//將數字類型的IP地址轉換成字符串類型的
char *iptos(u_long in)
{?? ?
?? ?static char output[IPTOSBUFFERS][3*4+3+1];?? ?
?? ?static short which;?? ?
?? ?u_char *p;?? ?
?? ?p = (u_char *)∈?? ?
?? ?which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1);?? ?
?? ?sprintf(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);?? ?
?? ?return output[which];
}
//將數字類型的IP地址轉換成字符串類型的
char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen)
{?? ?
?? ?socklen_t sockaddrlen;?? ?
#ifdef WIN32?? ?
?? ?sockaddrlen = sizeof(struct sockaddr_in6);?? ?
#else?? ?
?? ?sockaddrlen = sizeof(struct sockaddr_storage);?? ?
#endif?? ?
?? ?if(getnameinfo(sockaddr,sockaddrlen,address,addrlen,NULL,0,NI_NUMERICHOST) != 0)
?? ??? ?address = NULL;?? ?
?? ?return address;
}
//每次捕獲到數據包時,libpcap都會自動調用這個回調函數
//這個程序當中我們只用到了第二個參數,將每一個數據包的時間戳和長度從它的首部當中解析出來,并打印在屏幕上。
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{?? ?
?? ?struct tm *ltime;
?? ?char timestr[16];
?? ?time_t local_tv_sec;
?? ?//將時間戳轉換成可識別的格式
?? ?local_tv_sec = header->ts.tv_sec;?? ?
?? ?ltime=localtime(&local_tv_sec);
?? ?strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);?? ?
?? ?printf("%s,%.6ld len:%d\n",timestr,header->ts.tv_usec,header->len);
}
/* 回調函數,當收到每一個數據包時會被libpcap所調用 */
void packet_handler2(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{?? ?
?? ?struct tm *ltime;?? ?
?? ?char timestr[16];?? ?
?? ?ip_header *ih;?? ?
?? ?udp_header *uh;?? ?
?? ?u_int ip_len;?? ?
?? ?u_short sport,dport;?? ?
?? ?time_t local_tv_sec;?? ?
?? ?/* 將時間戳轉換成可識別的格式 */?? ?
?? ?local_tv_sec = header->ts.tv_sec;?? ?
?? ?ltime=localtime(&local_tv_sec);?? ?
?? ?strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);?? ?
?? ?/* 打印數據包的時間戳和長度 */?? ?
?? ?printf("%s.%.6ld len:%d ", timestr, header->ts.tv_usec, header->len);?? ?
?? ?/* 獲得IP數據包頭部的位置 */?? ?
?? ?ih = (ip_header *) (pkt_data +14); //以太網頭部長度?? ?
?? ?/* 獲得UDP首部的位置 */?? ?
?? ?ip_len = (ih->ver_ihl & 0xf) * 4;?? ?
?? ?uh = (udp_header *) ((u_char*)ih + ip_len);?? ?
?? ?/* 將網絡字節序列轉換成主機字節序列 */?? ?
?? ?sport = ntohs( uh->sport );?? ?
?? ?dport = ntohs( uh->dport );?? ?
?? ?/* 打印IP地址和UDP端口 */?? ?
?? ?printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
?? ??? ?ih->saddr.byte1,ih->saddr.byte2,ih->saddr.byte3,ih->saddr.byte4,sport,ih->daddr.byte1,
?? ??? ?ih->daddr.byte2,ih->daddr.byte3,ih->daddr.byte4,dport);
}
int main()
{
?? ?獲取本地適配器信息//
?? ?
?? ?//pcap_if_t結構體它包含了一個適配器的詳細信息,其中的數據域name和description表示一個適配器名稱和一個可以讓人們理解的描述
// ?? ?pcap_if_t *alldevs;
// ?? ?pcap_if_t *d;
// ?? ?int i = 0;
// ?? ?char errbuf[PCAP_ERRBUF_SIZE];
//
// ?? ?//獲取本地適配器
// ?? ?/*
// ?? ?int pcap_findalldevs_ex(char *? source,struct pcap_rmtauth *? auth,pcap_if_t **? alldevs,char *? errbuf);
// ?? ?第一個參數是一個字符指針,保存的是source的地址,事實上它決定了source的類型(file, remote/local interface),而PCAP_SRC_IF_STRING表示用戶希望從一個網絡接口卡打開捕獲(open a capture from a network interface)。
// ?? ?第二個參數是一個pcap_rmtauth的結構體指針,它保存了RPCAP連接遠程用戶的驗證信息,因為是要獲取本地的設備列表,這個參數就沒有意義,置為NULL。
// ?? ?第三個參數剛才已經說了,這個函數的功能就是返回一個pcap_if結構的鏈表,給的是鏈表的地址,所以在源碼中看到寫的是“&alldevs”。
// ?? ?第四個參數是錯誤信息緩沖,就是說如果這個函數調用出錯則把錯誤信息保存到這個buff里面。
// ?? ?返回值:pcap_findalldevs_ex函數的返回值為0,返回-1則表示出錯。
// ?? ?*/
// ?? ?if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
// ?? ?{
// ?? ??? ?fprintf(stderr, "Error in pcap_findalldevs_ex: %s\n", errbuf);
// ?? ??? ?exit(1);
// ?? ?}
//
// ?? ?//打印列表
// ?? ?//設備的名稱都是以rpcap://打頭的,其實就是源碼中用到的PCAP_SRC_IF_STRING,這應該是RPCAP協議所定義的格式
// ?? ?for(d = alldevs; d != NULL; d = d->next)
// ?? ?{
// ?? ??? ?printf("網卡%d-->網卡名字:%s", ++i, d->name);
// ?? ??? ?if (d->description)
// ?? ??? ??? ?printf("網卡描述:%s\n", d->description);
// ?? ??? ?else
// ?? ??? ??? ?printf("該網卡沒有描述\n");
// ?? ?}
//
// ?? ?if(i == 0)
// ?? ?{
// ?? ??? ?printf("沒有發現網卡信息,請確認安裝了Winpcap\n");
// ?? ??? ?return -1;
// ?? ?}
//
// ?? ?//釋放鏈表空間
// ?? ?pcap_freealldevs(alldevs);
?? ?獲取已安裝設備的高級信息//
// ?? ?/*
// ?? ?每一個pcap_if結構體,都包含一個pcap_addr結構體,這個結構體如下元素組成:一個地址列表、一個掩碼列表、一個廣播地址列表、一個目的地址列表
// ?? ?另外,函數pcap_findalldevs_ex()還能返回遠程適配器信息和一個位于所給的本地文件夾的pcap文件列表,跟函數第一個參數有關。
// ?? ?*/
// ?? ?pcap_if_t *alldevs;
// ?? ?char errbuf[PCAP_ERRBUF_SIZE+1];
// ?? ?char source[PCAP_ERRBUF_SIZE+1];
//
// ?? ?strcpy(source,"rpcap://");
// ?? ?//fgets(source, PCAP_ERRBUF_SIZE, stdin); ?
// ?? ?source[PCAP_ERRBUF_SIZE] = '\0'; ?
// ?? ?//獲得接口列表
// ?? ?if (pcap_findalldevs_ex(source, NULL, &alldevs, errbuf) == -1) ?
// ?? ?{?? ?
// ?? ??? ?fprintf(stderr,"Error in pcap_findalldevs: %s\n",errbuf);?? ?
// ?? ??? ?exit(1); ?
// ?? ?}
//
// ?? ?//掃描列表并打印每一項
// ?? ?int i=1;
// ?? ?for(pcap_if_t *d=alldevs;d;d=d->next)
// ?? ?{
// ?? ??? ?printf("發現第%d個網卡\n",i);
// ?? ??? ?i++;
// ?? ??? ?ifprint(d);
// ?? ?}
// ?? ?pcap_freealldevs(alldevs);
?? ?打開適配器并捕獲數據包//
//???? pcap_if_t *alldevs,*d;
// ?? ?int CapNum;
// ?? ?int i=1;
// ?? ?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\n\t網卡名字:%s",i++,d->name);?????? ?
// ?? ??? ?if (d->description)?????????? ?
// ?? ??? ??? ?printf("\n\t網卡描述\n",d->description);?????? ?
// ?? ??? ?else?????????? ?
// ?? ??? ??? ?printf("\n\t無網卡描述\n");?? ?
// ?? ?}?? ?
// ?? ?if(i==0)?? ?
// ?? ?{?????? ?
// ?? ??? ?printf("\n沒有發現網卡信息,請確認安裝了Winpcap\n");?????? ?
// ?? ??? ?return -1;?? ?
// ?? ?}?? ?
// ?? ?printf("請輸入需要監聽的網卡,范圍(1-%d):",i);?? ?
// ?? ?scanf("%d", &CapNum);
// ?? ?if(CapNum < 1 || CapNum > i)?? ?
// ?? ?{?????? ?
// ?? ??? ?printf("\n輸入范圍無效\n");?????? ?
// ?? ??? ?//釋放設備列表????? ?
// ?? ??? ?pcap_freealldevs(alldevs);?????? ?
// ?? ??? ?return -1;?? ?
// ?? ?}?? ?
// ?? ?//跳轉到選中的適配器
// ?? ?for(d=alldevs,i=0;i<CapNum-1;d=d->next,i++);
//
// ?? ?//打開設備
// ?? ?/*
// ?? ?pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf);
// ?? ?第一個參數,它表示的是設備的名稱。在獲取適配器鏈表后,通過返回數據域name即可知道設備的名稱。
// ?? ?第二個參數制定要捕獲數據包中的哪些部分。我們知道對于使用以太網的局域網來說,最大傳輸單元為1500字節,那么設為65535則能保證收到完整的數據包。
// ?? ?第三個參數是最重要的一個值,它用來指示適配器是否需要設置成混雜模式,它的意思就是說設置混雜模式可以捕獲到所有經過適配器的數據包,不論是不是發給機器本身的。PCAP_OPENFLAG_PROMISCUOUS這個值就是設置成混雜模式的意思。
// ?? ?第四個參數表示的是讀取數據的超時時間,單位是毫秒。意思就是說會在read_timeout時間內對適配器的讀取操作進行響應,不管有沒有讀到數據。這里有兩個特殊的值需要說明一下,如果將時間設置為0意味著沒有超時,那么如果沒有數據到達的話,讀操作就永遠不會返回;如果設置為-1則恰恰相反,不論有沒有讀到數據都會立即返回。
// ?? ?第五個參數之前提到過,它表示的是連接遠程用戶的驗證信息,由于我們不需要連接到遠程用戶,這里置為NULL。
// ?? ?第六個參數是錯誤信息緩沖,如果該函數在調用過程中出錯則會將出錯信息保存在緩沖中。
// ?? ?返回值:pcap_t的指針類型,查看聲明處我們可以發現pcap_t實際上是pcap結構體,而文檔上說明它是一個已打開的捕捉實例的描述符。
// ?? ?*/
// ?? ?if ((adhandle= pcap_open(d->name,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("\n正在監聽網卡%s...\n",d->description);?? ?
// ?? ?//釋放設備列表? ?
// ?? ?pcap_freealldevs(alldevs);
//
// ?? ?//開始捕獲
// ?? ?/*
// ?? ?int pcap_loop(pcap_t *p,int cnt,pcap_handler callback,u_char *user)
// ?? ?pcap_loop()函數是用來捕獲一組數據分組的。pcap_loop()函數跟pcap_dispath()函數很類似,唯一不同之處就是pcap_loop()會一直讀數據直到cnt數據被處理或者出現錯誤。
// ?? ?不論讀取時間是否超時它都不返回。當然有一種特殊情況,就是在調用pcap_open_live()函數時指定非0的讀取超時時間,調用pcap_dispath()函數可以在超時發生時對讀到的數據進行接收和處理。
// ?? ?將cnt設為正數可以使pcap_loop()一直循環(或至少到錯誤發生之前)。返回-1則出錯;返回0則說明cnt被耗盡;返回-2則是由于在數據被處理之前調用pcap_breakloop()來中斷循環。
// ?? ?如果你的應用程序使用了pcap_breakloop()函數,你需要對返回值-1和-2進行詳細的檢查,而不是只檢查<0的情況。
// ?? ?
// ?? ?pcap_loop()只有當cnt數據包被捕獲時才會返回,即它會在一小段時間內阻塞網絡的利用。pcap_loop()函數的第三個參數很重要,它是一個回調函數的函數指針,即pcap_loop()函數返回時會自動調用這個回調函數。
// ?? ?*/
// ?? ?pcap_loop(adhandle, 0, packet_handler, NULL);
?? ?
?? ?不用回調方法捕獲數據包//
// ?? ?/*
// ?? ?int pcap_next_ex(pcap_t *p,struct pcap_pkthdr **pkt_header,const u_char **pkt_data)
// ?? ?第一個參數是網絡適配器的描述符
// ?? ?第二個參數是一個指向pcap_pkthdr結構體的指針
// ?? ?第三個參數是指向數據報數據的緩沖的指針
// ?? ?返回值:
// ?? ??1 if the packet has been read without problems (數據讀取無誤)
// ?? ??0 if the timeout set with pcap_open_live() has elapsed. In this case pkt_header and pkt_data don't point to a valid packet
// ?? ??(pcap_open_live()設置的超時時間超時,在這種情況下pkt_header和pkt_data指向一個非法的數據)
// ?? ??-1 if an error occurred (出錯)
// ?? ??-2 if EOF was reached reading from an offline capture(讀取到EOF,應該是文件)
//
// ?? ?我們知道pcap_loop()函數是基于回調的原理來進行數據捕獲的,如技術文檔所說,這是一種精妙的方法,并且在某些場合下,它是一種很好的選擇。
// ?? ?但是在處理回調有時候會并不實用,它會增加程序的復雜度,特別是在多線程的C++程序中。而對于pcap_next_ex()函數而言,可以通過直接調用它來
// ?? ?獲得一個數據包,也只有在調用了這個函數才能收到數據包。pcap_next_ex()函數跟pcap_loop()的回調函數參數是相同的。
//
// ?? ?pcap_next_ex()函數和pcap_next()函數的區別,通過函數名我們知道pcap_next_ex()函數是在pcap_next()基礎上擴展得到的。pcap_next()函數有一些缺陷。
// ?? ?比如它效率很低,盡管隱藏了回調的方式,但它仍然依賴于函數pcap_dispatch();另外,它不能檢測到EOF這個狀態,那么如果數據包是從文件中讀取過來的,那么它就不那么好用了。顯然,pcap_next_ex()函數在此基礎上做出了一些改進。
// ?? ?*/
// ?? ?pcap_if_t *alldevs;?? ?
// ?? ?pcap_if_t *d;?? ?
// ?? ?int CapNum;?? ?
// ?? ?int i=0;?? ?
// ?? ?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\n\t網卡名字:%s",i++,d->name);?????? ?
// ?? ??? ?if (d->description)?????????? ?
// ?? ??? ??? ?printf("\n\t網卡描述\n",d->description);?????? ?
// ?? ??? ?else?????????? ?
// ?? ??? ??? ?printf("\n\t無網卡描述\n");?? ?
// ?? ?}?? ?
// ?? ?if(i==0)?? ?
// ?? ?{?????? ?
// ?? ??? ?printf("\n沒有發現網卡信息,請確認安裝了Winpcap\n");?????? ?
// ?? ??? ?return -1;?? ?
// ?? ?}?? ?
// ?? ?printf("請輸入需要監聽的網卡,范圍(1-%d):",i);?? ?
// ?? ?scanf("%d", &CapNum);
// ?? ?if(CapNum < 1 || CapNum > i)?? ?
// ?? ?{?????? ?
// ?? ??? ?printf("\n輸入范圍無效\n");?????? ?
// ?? ??? ?//釋放設備列表????? ?
// ?? ??? ?pcap_freealldevs(alldevs);?????? ?
// ?? ??? ?return -1;?? ?
// ?? ?}?? ?
// ?? ?//跳轉到選中的適配器
// ?? ?for(d=alldevs,i=0;i<CapNum-1;d=d->next,i++);
//
// ?? ?//打開設備
// ?? ?pcap_t *adhandle;
// ?? ?if ((adhandle= pcap_open(d->name,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("\n正在監聽網卡%s...\n",d->description);?? ?
// ?? ?//釋放設備列表? ?
// ?? ?pcap_freealldevs(alldevs);
//
//
// ?? ?/* 獲取數據包 */ ?
// ?? ?int res;
// ?? ?struct pcap_pkthdr *header;
// ?? ?const u_char *pkt_data;
// ?? ?time_t local_tv_sec;
// ?? ?struct tm *ltime;?? ?
// ?? ?char timestr[16];
// ?? ?while((res = pcap_next_ex(adhandle,&header, &pkt_data)) >= 0)?? ?
// ?? ?{?????? ?
// ?? ??? ?if(res == 0)??????????? /* 超時時間到 */?????????? ?
// ?? ??? ??? ?continue;??????? /* 將時間戳轉換成可識別的格式 */?????? ?
// ?? ??? ?local_tv_sec = header->ts.tv_sec;?????? ?
// ?? ??? ?ltime=localtime(&local_tv_sec);?????? ?
// ?? ??? ?strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);?????? ?
// ?? ??? ?printf("%s,%.6ld 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;?? ?
// ?? ?}
?? ?過濾及分析數據包//
?? ?/*
?? ?在WinPcap中用來過濾數據包的函數有兩個,pcap_compile()和pcap_setfilter()。
?? ?pcap_compile()的原理是將高層的布爾過濾表達式編譯成能夠被過濾引擎所解釋的低層的字節碼,關于布爾過濾表達式的語法會在后續的章節里進行說明。
?? ?pcap_setfilter()的原理是將一個過濾器與內核捕獲會話相關聯,當pcap_setfilter()被調用時,過濾器會應用到來自網絡的所有數據包,符合要求的所有數據包都將會被立刻復制給應用程序。
?? ?*/
?? ?pcap_if_t *alldevs;?? ?
?? ?pcap_if_t *d;?? ?
?? ?int inum;?? ?
?? ?int i=0;?? ?
?? ?pcap_t *adhandle;?? ?
?? ?char errbuf[PCAP_ERRBUF_SIZE];?? ?
?? ?u_int netmask;?? ?
?? ?char packet_filter[] = "ip and udp";?? ?
?? ?struct bpf_program fcode;?? ?
?? ?/* 獲得設備列表 */?? ?
?? ?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("%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,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;?? ?
?? ?}?? ?
?? ?/* 檢查數據鏈路層,為了簡單,我們只考慮以太網 */?? ?
?? ?if(pcap_datalink(adhandle) != DLT_EN10MB)?? ?
?? ?{?????? ?
?? ??? ?fprintf(stderr,"\nThis program works only on Ethernet networks.\n");?????? ?
?? ??? ?/* 釋放設備列表 */?????? ?
?? ??? ?pcap_freealldevs(alldevs);?????? ?
?? ??? ?return -1;?? ?
?? ?}?? ?
?? ?if(d->addresses != NULL)?????? ?
?? ??? ?/* 獲得接口第一個地址的掩碼 */????? ?
?? ??? ?netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;?? ?
?? ?else??????? /* 如果接口沒有地址,那么我們假設一個C類的掩碼 */?????? ?
?? ??? ?netmask=0xffffff;??? //編譯過濾器?? ?
?? ?if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )?? ?
?? ?{?????? ?
?? ??? ?fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");?????? ?
?? ??? ?/* 釋放設備列表 */?????? ?
?? ??? ?pcap_freealldevs(alldevs);?????? ?
?? ??? ?return -1;?? ?
?? ?}?? ?
?? ?//設置過濾器?? ?
?? ?if (pcap_setfilter(adhandle, &fcode)<0)?? ?
?? ?{?????? ?
?? ??? ?fprintf(stderr,"\nError setting the filter.\n");?????? ?
?? ??? ?/* 釋放設備列表 */?????? ?
?? ??? ?pcap_freealldevs(alldevs);?????? ?
?? ??? ?return -1;?? ?
?? ?}?? ?
?? ?printf("\nlistening on %s...\n", d->description);?? ?
?? ?/* 釋放設備列表 */?? ?
?? ?pcap_freealldevs(alldevs);?? ?
?? ?/* 開始捕捉 */?? ?
?? ?pcap_loop(adhandle, 0, packet_handler2, NULL);
?? ?return 0;
}
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
//包含頭文件,(可以在工具-->選項-->項目和解決方案-->VC++目錄 里面添加,也可以直接把頭文件和庫文件拷到“C:\Program Files\Microsoft Visual Studio 8\VC”目錄下)
#include <pcap.h>
//也可以在項目屬性里面添加“連接器-->輸入”
#pragma comment(lib,"wpcap.lib")
#pragma comment(lib,"ws2_32.lib")
//采用頭文件包含是不行的,必須在項目的預處理器定義字段那,添加“;WPCAP;HAVE_REMOTE”。
//#define HAVE_REMOTE
//#define WPCAP
#define IPTOSBUFFERS??? 12
//4字節的IP地址
typedef struct ip_address
{?? ?
?? ?u_char byte1;?? ?
?? ?u_char byte2;?? ?
?? ?u_char byte3;?? ?
?? ?u_char byte4;
} ip_address;
//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;
//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)
} udp_header;
void ifprint(pcap_if_t *d);
char *iptos(u_long in);
char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen);
//每次捕獲到數據包時,libpcap都會自動調用這個回調函數
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
//回調函數原型,過濾數據包
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
//打印所有可用信息
void ifprint(pcap_if_t *d)
{
?? ?pcap_addr_t *a; ?
?? ?char ip6str[128];
?? ?//設備名(Name)
?? ?printf("網卡名稱:%s\n",d->name); ?
?? ?//設備描述(Description)
?? ?if (d->description)?? ?
?? ??? ?printf("\t設備描述: %s\n",d->description);? /* Loopback Address*/ ?
?? ?printf("\tLoopback: %s\n",(d->flags & PCAP_IF_LOOPBACK)?"yes":"no");? /* IP addresses */ ?
?? ?for(a=d->addresses;a;a=a->next)
?? ?{?? ?
?? ??? ?printf("\tAddress Family: #%d\n",a->addr->sa_family);?? ?
?? ??? ?switch(a->addr->sa_family)?? ?
?? ??? ?{???? ?
?? ??? ?case AF_INET:?????? ?
?? ??? ??? ?printf("\tAddress Family Name: AF_INET\n");?????? ?
?? ??? ??? ?if (a->addr)???????? ?
?? ??? ??? ??? ?printf("\tAddress: %s\n",iptos(((struct sockaddr_in *)a->addr)->sin_addr.s_addr));?????? ?
?? ??? ??? ?if (a->netmask)???????? ?
?? ??? ??? ??? ?printf("\tNetmask: %s\n",iptos(((struct sockaddr_in *)a->netmask)->sin_addr.s_addr));?????? ?
?? ??? ??? ?if (a->broadaddr)???????? ?
?? ??? ??? ??? ?printf("\tBroadcast Address: %s\n",iptos(((struct sockaddr_in *)a->broadaddr)->sin_addr.s_addr));?????? ?
?? ??? ??? ?if (a->dstaddr)???????? ?
?? ??? ??? ??? ?printf("\tDestination Address: %s\n",iptos(((struct sockaddr_in *)a->dstaddr)->sin_addr.s_addr));?????? ?
?? ??? ??? ?break;???? ?
?? ??? ?case AF_INET6:?????? ?
?? ??? ??? ?printf("\tAddress Family Name: AF_INET6\n");?????? ?
?? ??? ??? ?if (a->addr)???????? ?
?? ??? ??? ??? ?printf("\tAddress: %s\n", ip6tos(a->addr, ip6str, sizeof(ip6str)));????? ?
?? ??? ??? ?break;???? ?
?? ??? ?default:?????? ?
?? ??? ??? ?printf("\tAddress Family Name: Unknown\n");?????? ?
?? ??? ??? ?break;?? ?
?? ??? ?} ?
?? ?} ?
?? ?printf("\n");
}
//將數字類型的IP地址轉換成字符串類型的
char *iptos(u_long in)
{?? ?
?? ?static char output[IPTOSBUFFERS][3*4+3+1];?? ?
?? ?static short which;?? ?
?? ?u_char *p;?? ?
?? ?p = (u_char *)∈?? ?
?? ?which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1);?? ?
?? ?sprintf(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);?? ?
?? ?return output[which];
}
//將數字類型的IP地址轉換成字符串類型的
char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen)
{?? ?
?? ?socklen_t sockaddrlen;?? ?
#ifdef WIN32?? ?
?? ?sockaddrlen = sizeof(struct sockaddr_in6);?? ?
#else?? ?
?? ?sockaddrlen = sizeof(struct sockaddr_storage);?? ?
#endif?? ?
?? ?if(getnameinfo(sockaddr,sockaddrlen,address,addrlen,NULL,0,NI_NUMERICHOST) != 0)
?? ??? ?address = NULL;?? ?
?? ?return address;
}
//每次捕獲到數據包時,libpcap都會自動調用這個回調函數
//這個程序當中我們只用到了第二個參數,將每一個數據包的時間戳和長度從它的首部當中解析出來,并打印在屏幕上。
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{?? ?
?? ?struct tm *ltime;
?? ?char timestr[16];
?? ?time_t local_tv_sec;
?? ?//將時間戳轉換成可識別的格式
?? ?local_tv_sec = header->ts.tv_sec;?? ?
?? ?ltime=localtime(&local_tv_sec);
?? ?strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);?? ?
?? ?printf("%s,%.6ld len:%d\n",timestr,header->ts.tv_usec,header->len);
}
/* 回調函數,當收到每一個數據包時會被libpcap所調用 */
void packet_handler2(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{?? ?
?? ?struct tm *ltime;?? ?
?? ?char timestr[16];?? ?
?? ?ip_header *ih;?? ?
?? ?udp_header *uh;?? ?
?? ?u_int ip_len;?? ?
?? ?u_short sport,dport;?? ?
?? ?time_t local_tv_sec;?? ?
?? ?/* 將時間戳轉換成可識別的格式 */?? ?
?? ?local_tv_sec = header->ts.tv_sec;?? ?
?? ?ltime=localtime(&local_tv_sec);?? ?
?? ?strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);?? ?
?? ?/* 打印數據包的時間戳和長度 */?? ?
?? ?printf("%s.%.6ld len:%d ", timestr, header->ts.tv_usec, header->len);?? ?
?? ?/* 獲得IP數據包頭部的位置 */?? ?
?? ?ih = (ip_header *) (pkt_data +14); //以太網頭部長度?? ?
?? ?/* 獲得UDP首部的位置 */?? ?
?? ?ip_len = (ih->ver_ihl & 0xf) * 4;?? ?
?? ?uh = (udp_header *) ((u_char*)ih + ip_len);?? ?
?? ?/* 將網絡字節序列轉換成主機字節序列 */?? ?
?? ?sport = ntohs( uh->sport );?? ?
?? ?dport = ntohs( uh->dport );?? ?
?? ?/* 打印IP地址和UDP端口 */?? ?
?? ?printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
?? ??? ?ih->saddr.byte1,ih->saddr.byte2,ih->saddr.byte3,ih->saddr.byte4,sport,ih->daddr.byte1,
?? ??? ?ih->daddr.byte2,ih->daddr.byte3,ih->daddr.byte4,dport);
}
int main()
{
?? ?獲取本地適配器信息//
?? ?
?? ?//pcap_if_t結構體它包含了一個適配器的詳細信息,其中的數據域name和description表示一個適配器名稱和一個可以讓人們理解的描述
// ?? ?pcap_if_t *alldevs;
// ?? ?pcap_if_t *d;
// ?? ?int i = 0;
// ?? ?char errbuf[PCAP_ERRBUF_SIZE];
//
// ?? ?//獲取本地適配器
// ?? ?/*
// ?? ?int pcap_findalldevs_ex(char *? source,struct pcap_rmtauth *? auth,pcap_if_t **? alldevs,char *? errbuf);
// ?? ?第一個參數是一個字符指針,保存的是source的地址,事實上它決定了source的類型(file, remote/local interface),而PCAP_SRC_IF_STRING表示用戶希望從一個網絡接口卡打開捕獲(open a capture from a network interface)。
// ?? ?第二個參數是一個pcap_rmtauth的結構體指針,它保存了RPCAP連接遠程用戶的驗證信息,因為是要獲取本地的設備列表,這個參數就沒有意義,置為NULL。
// ?? ?第三個參數剛才已經說了,這個函數的功能就是返回一個pcap_if結構的鏈表,給的是鏈表的地址,所以在源碼中看到寫的是“&alldevs”。
// ?? ?第四個參數是錯誤信息緩沖,就是說如果這個函數調用出錯則把錯誤信息保存到這個buff里面。
// ?? ?返回值:pcap_findalldevs_ex函數的返回值為0,返回-1則表示出錯。
// ?? ?*/
// ?? ?if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
// ?? ?{
// ?? ??? ?fprintf(stderr, "Error in pcap_findalldevs_ex: %s\n", errbuf);
// ?? ??? ?exit(1);
// ?? ?}
//
// ?? ?//打印列表
// ?? ?//設備的名稱都是以rpcap://打頭的,其實就是源碼中用到的PCAP_SRC_IF_STRING,這應該是RPCAP協議所定義的格式
// ?? ?for(d = alldevs; d != NULL; d = d->next)
// ?? ?{
// ?? ??? ?printf("網卡%d-->網卡名字:%s", ++i, d->name);
// ?? ??? ?if (d->description)
// ?? ??? ??? ?printf("網卡描述:%s\n", d->description);
// ?? ??? ?else
// ?? ??? ??? ?printf("該網卡沒有描述\n");
// ?? ?}
//
// ?? ?if(i == 0)
// ?? ?{
// ?? ??? ?printf("沒有發現網卡信息,請確認安裝了Winpcap\n");
// ?? ??? ?return -1;
// ?? ?}
//
// ?? ?//釋放鏈表空間
// ?? ?pcap_freealldevs(alldevs);
?? ?獲取已安裝設備的高級信息//
// ?? ?/*
// ?? ?每一個pcap_if結構體,都包含一個pcap_addr結構體,這個結構體如下元素組成:一個地址列表、一個掩碼列表、一個廣播地址列表、一個目的地址列表
// ?? ?另外,函數pcap_findalldevs_ex()還能返回遠程適配器信息和一個位于所給的本地文件夾的pcap文件列表,跟函數第一個參數有關。
// ?? ?*/
// ?? ?pcap_if_t *alldevs;
// ?? ?char errbuf[PCAP_ERRBUF_SIZE+1];
// ?? ?char source[PCAP_ERRBUF_SIZE+1];
//
// ?? ?strcpy(source,"rpcap://");
// ?? ?//fgets(source, PCAP_ERRBUF_SIZE, stdin); ?
// ?? ?source[PCAP_ERRBUF_SIZE] = '\0'; ?
// ?? ?//獲得接口列表
// ?? ?if (pcap_findalldevs_ex(source, NULL, &alldevs, errbuf) == -1) ?
// ?? ?{?? ?
// ?? ??? ?fprintf(stderr,"Error in pcap_findalldevs: %s\n",errbuf);?? ?
// ?? ??? ?exit(1); ?
// ?? ?}
//
// ?? ?//掃描列表并打印每一項
// ?? ?int i=1;
// ?? ?for(pcap_if_t *d=alldevs;d;d=d->next)
// ?? ?{
// ?? ??? ?printf("發現第%d個網卡\n",i);
// ?? ??? ?i++;
// ?? ??? ?ifprint(d);
// ?? ?}
// ?? ?pcap_freealldevs(alldevs);
?? ?打開適配器并捕獲數據包//
//???? pcap_if_t *alldevs,*d;
// ?? ?int CapNum;
// ?? ?int i=1;
// ?? ?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\n\t網卡名字:%s",i++,d->name);?????? ?
// ?? ??? ?if (d->description)?????????? ?
// ?? ??? ??? ?printf("\n\t網卡描述\n",d->description);?????? ?
// ?? ??? ?else?????????? ?
// ?? ??? ??? ?printf("\n\t無網卡描述\n");?? ?
// ?? ?}?? ?
// ?? ?if(i==0)?? ?
// ?? ?{?????? ?
// ?? ??? ?printf("\n沒有發現網卡信息,請確認安裝了Winpcap\n");?????? ?
// ?? ??? ?return -1;?? ?
// ?? ?}?? ?
// ?? ?printf("請輸入需要監聽的網卡,范圍(1-%d):",i);?? ?
// ?? ?scanf("%d", &CapNum);
// ?? ?if(CapNum < 1 || CapNum > i)?? ?
// ?? ?{?????? ?
// ?? ??? ?printf("\n輸入范圍無效\n");?????? ?
// ?? ??? ?//釋放設備列表????? ?
// ?? ??? ?pcap_freealldevs(alldevs);?????? ?
// ?? ??? ?return -1;?? ?
// ?? ?}?? ?
// ?? ?//跳轉到選中的適配器
// ?? ?for(d=alldevs,i=0;i<CapNum-1;d=d->next,i++);
//
// ?? ?//打開設備
// ?? ?/*
// ?? ?pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf);
// ?? ?第一個參數,它表示的是設備的名稱。在獲取適配器鏈表后,通過返回數據域name即可知道設備的名稱。
// ?? ?第二個參數制定要捕獲數據包中的哪些部分。我們知道對于使用以太網的局域網來說,最大傳輸單元為1500字節,那么設為65535則能保證收到完整的數據包。
// ?? ?第三個參數是最重要的一個值,它用來指示適配器是否需要設置成混雜模式,它的意思就是說設置混雜模式可以捕獲到所有經過適配器的數據包,不論是不是發給機器本身的。PCAP_OPENFLAG_PROMISCUOUS這個值就是設置成混雜模式的意思。
// ?? ?第四個參數表示的是讀取數據的超時時間,單位是毫秒。意思就是說會在read_timeout時間內對適配器的讀取操作進行響應,不管有沒有讀到數據。這里有兩個特殊的值需要說明一下,如果將時間設置為0意味著沒有超時,那么如果沒有數據到達的話,讀操作就永遠不會返回;如果設置為-1則恰恰相反,不論有沒有讀到數據都會立即返回。
// ?? ?第五個參數之前提到過,它表示的是連接遠程用戶的驗證信息,由于我們不需要連接到遠程用戶,這里置為NULL。
// ?? ?第六個參數是錯誤信息緩沖,如果該函數在調用過程中出錯則會將出錯信息保存在緩沖中。
// ?? ?返回值:pcap_t的指針類型,查看聲明處我們可以發現pcap_t實際上是pcap結構體,而文檔上說明它是一個已打開的捕捉實例的描述符。
// ?? ?*/
// ?? ?if ((adhandle= pcap_open(d->name,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("\n正在監聽網卡%s...\n",d->description);?? ?
// ?? ?//釋放設備列表? ?
// ?? ?pcap_freealldevs(alldevs);
//
// ?? ?//開始捕獲
// ?? ?/*
// ?? ?int pcap_loop(pcap_t *p,int cnt,pcap_handler callback,u_char *user)
// ?? ?pcap_loop()函數是用來捕獲一組數據分組的。pcap_loop()函數跟pcap_dispath()函數很類似,唯一不同之處就是pcap_loop()會一直讀數據直到cnt數據被處理或者出現錯誤。
// ?? ?不論讀取時間是否超時它都不返回。當然有一種特殊情況,就是在調用pcap_open_live()函數時指定非0的讀取超時時間,調用pcap_dispath()函數可以在超時發生時對讀到的數據進行接收和處理。
// ?? ?將cnt設為正數可以使pcap_loop()一直循環(或至少到錯誤發生之前)。返回-1則出錯;返回0則說明cnt被耗盡;返回-2則是由于在數據被處理之前調用pcap_breakloop()來中斷循環。
// ?? ?如果你的應用程序使用了pcap_breakloop()函數,你需要對返回值-1和-2進行詳細的檢查,而不是只檢查<0的情況。
// ?? ?
// ?? ?pcap_loop()只有當cnt數據包被捕獲時才會返回,即它會在一小段時間內阻塞網絡的利用。pcap_loop()函數的第三個參數很重要,它是一個回調函數的函數指針,即pcap_loop()函數返回時會自動調用這個回調函數。
// ?? ?*/
// ?? ?pcap_loop(adhandle, 0, packet_handler, NULL);
?? ?
?? ?不用回調方法捕獲數據包//
// ?? ?/*
// ?? ?int pcap_next_ex(pcap_t *p,struct pcap_pkthdr **pkt_header,const u_char **pkt_data)
// ?? ?第一個參數是網絡適配器的描述符
// ?? ?第二個參數是一個指向pcap_pkthdr結構體的指針
// ?? ?第三個參數是指向數據報數據的緩沖的指針
// ?? ?返回值:
// ?? ??1 if the packet has been read without problems (數據讀取無誤)
// ?? ??0 if the timeout set with pcap_open_live() has elapsed. In this case pkt_header and pkt_data don't point to a valid packet
// ?? ??(pcap_open_live()設置的超時時間超時,在這種情況下pkt_header和pkt_data指向一個非法的數據)
// ?? ??-1 if an error occurred (出錯)
// ?? ??-2 if EOF was reached reading from an offline capture(讀取到EOF,應該是文件)
//
// ?? ?我們知道pcap_loop()函數是基于回調的原理來進行數據捕獲的,如技術文檔所說,這是一種精妙的方法,并且在某些場合下,它是一種很好的選擇。
// ?? ?但是在處理回調有時候會并不實用,它會增加程序的復雜度,特別是在多線程的C++程序中。而對于pcap_next_ex()函數而言,可以通過直接調用它來
// ?? ?獲得一個數據包,也只有在調用了這個函數才能收到數據包。pcap_next_ex()函數跟pcap_loop()的回調函數參數是相同的。
//
// ?? ?pcap_next_ex()函數和pcap_next()函數的區別,通過函數名我們知道pcap_next_ex()函數是在pcap_next()基礎上擴展得到的。pcap_next()函數有一些缺陷。
// ?? ?比如它效率很低,盡管隱藏了回調的方式,但它仍然依賴于函數pcap_dispatch();另外,它不能檢測到EOF這個狀態,那么如果數據包是從文件中讀取過來的,那么它就不那么好用了。顯然,pcap_next_ex()函數在此基礎上做出了一些改進。
// ?? ?*/
// ?? ?pcap_if_t *alldevs;?? ?
// ?? ?pcap_if_t *d;?? ?
// ?? ?int CapNum;?? ?
// ?? ?int i=0;?? ?
// ?? ?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\n\t網卡名字:%s",i++,d->name);?????? ?
// ?? ??? ?if (d->description)?????????? ?
// ?? ??? ??? ?printf("\n\t網卡描述\n",d->description);?????? ?
// ?? ??? ?else?????????? ?
// ?? ??? ??? ?printf("\n\t無網卡描述\n");?? ?
// ?? ?}?? ?
// ?? ?if(i==0)?? ?
// ?? ?{?????? ?
// ?? ??? ?printf("\n沒有發現網卡信息,請確認安裝了Winpcap\n");?????? ?
// ?? ??? ?return -1;?? ?
// ?? ?}?? ?
// ?? ?printf("請輸入需要監聽的網卡,范圍(1-%d):",i);?? ?
// ?? ?scanf("%d", &CapNum);
// ?? ?if(CapNum < 1 || CapNum > i)?? ?
// ?? ?{?????? ?
// ?? ??? ?printf("\n輸入范圍無效\n");?????? ?
// ?? ??? ?//釋放設備列表????? ?
// ?? ??? ?pcap_freealldevs(alldevs);?????? ?
// ?? ??? ?return -1;?? ?
// ?? ?}?? ?
// ?? ?//跳轉到選中的適配器
// ?? ?for(d=alldevs,i=0;i<CapNum-1;d=d->next,i++);
//
// ?? ?//打開設備
// ?? ?pcap_t *adhandle;
// ?? ?if ((adhandle= pcap_open(d->name,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("\n正在監聽網卡%s...\n",d->description);?? ?
// ?? ?//釋放設備列表? ?
// ?? ?pcap_freealldevs(alldevs);
//
//
// ?? ?/* 獲取數據包 */ ?
// ?? ?int res;
// ?? ?struct pcap_pkthdr *header;
// ?? ?const u_char *pkt_data;
// ?? ?time_t local_tv_sec;
// ?? ?struct tm *ltime;?? ?
// ?? ?char timestr[16];
// ?? ?while((res = pcap_next_ex(adhandle,&header, &pkt_data)) >= 0)?? ?
// ?? ?{?????? ?
// ?? ??? ?if(res == 0)??????????? /* 超時時間到 */?????????? ?
// ?? ??? ??? ?continue;??????? /* 將時間戳轉換成可識別的格式 */?????? ?
// ?? ??? ?local_tv_sec = header->ts.tv_sec;?????? ?
// ?? ??? ?ltime=localtime(&local_tv_sec);?????? ?
// ?? ??? ?strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);?????? ?
// ?? ??? ?printf("%s,%.6ld 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;?? ?
// ?? ?}
?? ?過濾及分析數據包//
?? ?/*
?? ?在WinPcap中用來過濾數據包的函數有兩個,pcap_compile()和pcap_setfilter()。
?? ?pcap_compile()的原理是將高層的布爾過濾表達式編譯成能夠被過濾引擎所解釋的低層的字節碼,關于布爾過濾表達式的語法會在后續的章節里進行說明。
?? ?pcap_setfilter()的原理是將一個過濾器與內核捕獲會話相關聯,當pcap_setfilter()被調用時,過濾器會應用到來自網絡的所有數據包,符合要求的所有數據包都將會被立刻復制給應用程序。
?? ?*/
?? ?pcap_if_t *alldevs;?? ?
?? ?pcap_if_t *d;?? ?
?? ?int inum;?? ?
?? ?int i=0;?? ?
?? ?pcap_t *adhandle;?? ?
?? ?char errbuf[PCAP_ERRBUF_SIZE];?? ?
?? ?u_int netmask;?? ?
?? ?char packet_filter[] = "ip and udp";?? ?
?? ?struct bpf_program fcode;?? ?
?? ?/* 獲得設備列表 */?? ?
?? ?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("%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,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;?? ?
?? ?}?? ?
?? ?/* 檢查數據鏈路層,為了簡單,我們只考慮以太網 */?? ?
?? ?if(pcap_datalink(adhandle) != DLT_EN10MB)?? ?
?? ?{?????? ?
?? ??? ?fprintf(stderr,"\nThis program works only on Ethernet networks.\n");?????? ?
?? ??? ?/* 釋放設備列表 */?????? ?
?? ??? ?pcap_freealldevs(alldevs);?????? ?
?? ??? ?return -1;?? ?
?? ?}?? ?
?? ?if(d->addresses != NULL)?????? ?
?? ??? ?/* 獲得接口第一個地址的掩碼 */????? ?
?? ??? ?netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;?? ?
?? ?else??????? /* 如果接口沒有地址,那么我們假設一個C類的掩碼 */?????? ?
?? ??? ?netmask=0xffffff;??? //編譯過濾器?? ?
?? ?if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )?? ?
?? ?{?????? ?
?? ??? ?fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");?????? ?
?? ??? ?/* 釋放設備列表 */?????? ?
?? ??? ?pcap_freealldevs(alldevs);?????? ?
?? ??? ?return -1;?? ?
?? ?}?? ?
?? ?//設置過濾器?? ?
?? ?if (pcap_setfilter(adhandle, &fcode)<0)?? ?
?? ?{?????? ?
?? ??? ?fprintf(stderr,"\nError setting the filter.\n");?????? ?
?? ??? ?/* 釋放設備列表 */?????? ?
?? ??? ?pcap_freealldevs(alldevs);?????? ?
?? ??? ?return -1;?? ?
?? ?}?? ?
?? ?printf("\nlistening on %s...\n", d->description);?? ?
?? ?/* 釋放設備列表 */?? ?
?? ?pcap_freealldevs(alldevs);?? ?
?? ?/* 開始捕捉 */?? ?
?? ?pcap_loop(adhandle, 0, packet_handler2, NULL);
?? ?return 0;
}
總結
- 上一篇: 两个大文件找出相同的一条记录
- 下一篇: MFC拖拽文件到任意EDIT控件