winpcap

winpcap

winpcap(windows packet capture)是windows平台下一個免費,公共的網路訪問系統。開發winpcap這個項目的目的在於為win32應用程式提供訪問網路底層的能力。它用於windows系統下的直接的網路編程。

基本介紹

  • 中文名:winpcap
  • 外文名:windows packet capture
  • 使用平台:windows
  • 目的:為win32提供訪問網路底層能力
  • 作用網路分析,故障排除等方面
特點,驅動功能,內部結構,NPF驅動,NPF和NDIS,NPF結構基礎,抓包,實例,獲得網卡接口,抓包,發包,卸載問題,

特點

Winpcap提供了一個強大的編程接口,它很容易地在各個作業系統之間進行移植,也很方便程式設計師進行開發。
什麼樣的程式需要使用Winpcap
很多不同的工具軟體使用Winpcap於網路分析,故障排除,網路安全監控等方面。Winpcap特別適用於下面這幾個經典領域:
1、網路及協定分析
2、網路監控
3、通信日誌記錄
4、traffic generators
5、用戶級別的橋路和路由
6、網路入侵檢測系統(NIDS)
7、網路掃描
8、安全工具
Winpcap有些方面不能做。它不依靠主機的諸如TCP/IP協定去收發數據包。這意味著它不能阻塞,不能處理同一台主機中各程式之間的通信數據。它只能“嗅探”到物理線路上的數據包。因此它不適用於traffic shapers,QoS調度,以及個人防火牆
Winpcap內部結構
Winpcap是一個Win32平台下用於抓包和分析的系統。包括一個核心級別的packet filter,一個底層的DLL(packet.dll)和一個高級的獨立於系統的DLL(Wpcap.dll)

驅動功能

  1. 捕獲原始數據包,包括在共享網路上各主機傳送/接收的以及相互之間交換的數據包;
  2. 在數據包發往應用程式之前,按照自定義的規則將某些特殊的數據包過濾掉;
  3. 在網路上傳送原始的數據包;
  4. 收集網路通信過程中的統計信息。
winpcap的主要功能在於獨立於主機協定(如TCP-IP)而傳送和接收原始數據包。也就是說,winpcap不能阻塞,過濾或控制其他應用程式數據包的發收,它僅僅只是監聽共享網路上傳送的數據包。因此,它不能用於QoS調度程式或個人防火牆。winpcap開發的主要對象是windows NT/2000/XP,這主要是因為在使用winpcap的用戶中只有一小部分是僅使用windows 95/98/Me,並且MS也已經放棄了對win9x的開發。因此本文相關的程式T-ARP也是面向NT/2000/XP用戶的。其實winpcap中的面向9x系統的概念和NT系統的非常相似,只是在某些實現上有點差異,比如說9x只支持ANSI編碼,而NT系統則提倡使用Unicode編碼。有個軟體叫snifferpro.可以作網管軟體用,有很多功能,可監視網路運行情況,每台網內機器的數據流量,實時反映每台機器所訪問IP以及它們之間的數據流通情況,可以抓包,可對過濾器進行設定,以便只抓取想要的包,比如POP3包,smtp包,ftp包等,並可從中找到信箱用戶名和密碼,還有ftp用戶名和密碼。它還可以在使用交換機的網路上監聽,不過要在交換機上裝它的一個軟體。還有一個簡單的監聽軟體叫Passwordsniffer,可截獲信箱用戶名和密碼,還有ftp用戶名和密碼,它只能用在HUB網路上。著名軟體tcpdumpidssnort都是基於libpcap編寫的,此外Nmap掃描器也是基於libpcap來捕獲目標主機返回的數據包的。
winpcap結構winpcap結構
winpcap提供給用戶兩個不同級別的編程接口:一個基於libpcap的wpcap.dll,另一個是較底層的packet.dll。對於一般的要與unix平台上libpcap兼容的開發來說,使用wpcap.dll是當然的選擇。

內部結構

Winpcap是針對Win32平台上的抓包網路分析的一個架構。它包括一個核心態的包過濾器,一個底層的動態程式庫(packet.dll)和一個高層的不依賴於系統的庫(wpcap.dll)。
Winpcap的各個組成部分Winpcap的各個組成部分
為什麼使用“architecture”而不是“library”呢?因為抓包是一個要求與網路適配器(網卡)和作業系統互動的底層機制,而且與網路的實施也有密切關係,所以僅用“library”不能充分表達Winpcap的作用。
下圖表明了Winpcap的各個組成部分:
首先,抓包系統必須繞過作業系統的協定棧來訪問在網路上傳輸的原始數據包(raw packet),這就要求一部分運行在作業系統核心內部,直接與網路接口驅動互動。這個部分是系統依賴(system dependent)的,在Winpcap的解決方案里它被認為是一個設備驅動,稱作NPF(Netgroup Packet Filter)。Winpcap開發小組針對Windows95,Windows98,WindowsME,Windows NT 4,Windows2000和WindowsXP提供了不同版本的驅動。這些驅動不僅提供了基本的特性(例如抓包和injection),還有更高級的特性(例如可程式的過濾器系統和監視引擎)。前者可以被用來約束一個抓包會話只針對網路通信中的一個子集(例如,僅僅捕獲特殊主機產生的ftp通信的數據包),後者提供了一個強大而簡單的統計網路通信量的機制(例如,獲得網路負載或兩個主機間的數據交換量)。
其次,抓包系統必須有用戶級的程式接口,通過這些接口,用戶程式可以利用核心驅動提供的高級特性。Winpcap提供了兩個不同的庫:packet.dll和wpcap.dll。前者提供了一個底層API,伴隨著一個獨立於Microsoft作業系統的編程接口,這些API可以直接用來訪問驅動的函式;後者導出了一組更強大的與libpcap一致的高層抓包函式館(capture primitives)。這些函式使得數據包的捕獲以一種與網路硬體和作業系統無關的方式進行。

NPF驅動

網路數據包過濾器(Netgroup Packet Filter,NPF)是Winpcap的核心部分,它是Winpcap完成困難工作的組件。它處理網路上傳輸的數據包,並且對用戶級提供可捕獲(capture)、傳送(injection)和分析性能(analysis capabilities)。

NPF和NDIS

NDIS(Network Driver Interface Specification)是一個定義網路適配器(或者說成是管理網路適配器的驅動程式)與協定驅動(例如TCP/IP的實現)之間通信的規範。NDIS最主要的目的是作為一個允許協定驅動傳送和接收網路(LAN或WAN)上的數據包而不必關心特定的適配器或特定的Win32作業系統的封裝。
NDIS支持三種類型的網路驅動:
(1)網路接口卡NIC驅動(Network interface card or NIC drivers)。NIC驅動直接管理著網路接口卡(NIC)。NIC驅動接下邊與硬體連線,從上邊表現為一個接口,該接口允許高層傳送數據包到網路上,處理中斷,重置NIC,停止NIC,查詢和設定驅動的運行特徵。NIC驅動可以是小連線埠(miniport)或完全的NIC驅動(full NIC driver)。
Miniport驅動僅僅實現了管理NIC的必要操作,包括在NIC上傳送和接收數據。對於所有最底層的NIC驅動的操作由NDIS提供,例如同步(synchronization)。小連線埠(miniport)不直接調用作業系統函式,它們對於作業系統的接口是NDIS。
小連線埠僅僅是向上傳遞數據包給NDIS並且NDIS確保這些數據包被傳遞給正確的協定。
完全NIC驅動(Full NIC driver)完成硬體細節的操作和所有由NDIS完成的同步和查詢操作。例如,完全NIC驅動維持接收到的數據的綁定信息。
(2) 中間層驅動(Intermediate drivers)中間層驅動位於高層驅動(例如協定驅動)和小連線埠之間。對於高層驅動,中間層驅動看起來像是小連線埠;對於小連線埠,中間層驅動看起來像協定驅動。一個中間層協定驅動可以位於另一個中間層驅動之上,儘管這種分層可能對系統性能帶來負面影響。開發中間層驅動的一個關鍵原因是在現存的遺留協定驅動(legacy protocol driver)和小連線埠之間形成媒體的轉化。例如,中間層驅動可以將LAN協定轉換成ATM協定。中間層驅動不能與用戶模式的應用程式通信,但可以與其他的NDIS驅動通信。
(3) 傳輸驅動或協定驅動(Transport drivers or protocol drivers)協定驅動實現了網路協定棧,例如IPX/SPX或TCP/IP,在一個或多個網路接口卡上提供它的服務。在協定驅動的上面,它為套用層客戶程式服務;在它的下面,它與一個或多個NIC驅動或中間層NDIS驅動連線。
NPF是一個協定驅動。從性能方面來看,這不是最好的選擇,但是它合理地獨立於MAC層並且有權使用原始通信(raw traffic)。
NPF在NDIS棧中的位置NPF在NDIS棧中的位置
下圖表現了NPF在NDIS棧中的位置:

NPF結構基礎

下圖表現了伴隨著NPF驅動細節的Winpcap的結構。
伴隨著NPF驅動細節的Winpcap的結構伴隨著NPF驅動細節的Winpcap的結構

抓包

抓包是NPF最重要的操作。在抓包的時候,驅動使用一個網路接口監視著數據包,並將這些數據包完整無缺地投遞給用戶級應用程式。
抓包過程依賴於兩個主要組件:
一個數據包過濾器,它決定著是否接收進來的數據包並把數據包拷貝給監聽程式。數據包過濾器是一個有布爾輸出的函式。如果函式值是true,抓包驅動拷貝數據包給應用程式;如果是false,數據包將被丟棄。NPF數據包過濾器更複雜一些,因為它不僅決定數據包是否應該被保存,而且還得決定要保存的位元組數。被NPF驅動採用的過濾系統來源於BSD Packet Filter(BPF),一個虛擬處理器可以執行偽彙編書寫的用戶級過濾程式。應用程式採用用戶定義的過濾器並使用wpcap.dll將它們編譯進BPF程式。然後,應用程式使用BIOCSETF IOCTL寫入核心態的過濾器。這樣,對於每一個到來的數據包該程式都將被執行,而滿足條件的數據包將被接收。與傳統解決方案不同,NPF不解釋(interpret)過濾器,而是執行(execute)它。由於性能的原因,在使用過濾器前,NPF提供一個JIT編譯器將它轉化成本地的80x86函式。當一個數據包被捕獲,NPF調用這個本地函式而不是調用過濾器解釋器,這使得處理過程相當快。
一個循環緩衝區,用來保存數據包並且避免丟失。一個保存在緩衝區中的數據包有一個頭,它包含了一些主要的信息,例如時間戳數據包的大小,但它不是協定頭。此外,以佇列插入的方式來保存數據包可以提高數據的存儲效率。可以以組的方式將數據包從NPF緩衝區拷貝到應用程式。這樣就提高了性能,因為它降低了讀的次數。如果一個數據包到來的時候緩衝區已經滿了,那么該數據包將被丟棄,因此就發生了丟包

實例

獲得網卡接口。在普通的SOCKET編程中,對雙網卡編程是不行的。當主機為雙網卡時,本程式可分別獲得兩張網卡各自的描述結構及地址,然後可以對它們分別進行操作。返回的alldevs佇列首部為邏輯網卡,一般不對它進行什麼操作。

獲得網卡接口

#include "pcap.h"void main(){    pcap_if_t *alldevs;    /*struct pcap_if_t{    pcap_if_t *next;    char *name;    char *description;    pcap_addr *addresses;    U_int falgs;    }    */    pcap_if_t *d;    int i=0;    char errbuf[PCAP_ERRBUF_SIZE];    /* Retrieve the device list */    if (pcap_findalldevs(&alldevs, errbuf) == -1)//返回網卡列表,alldevs指向表頭    {        fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);        exit(1);    }    /* Print the list */    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;    }    /* We don't need any more the device list. Free it */    pcap_freealldevs(alldevs);}
--------------------------------------------------------------------------------

抓包

本程式俘獲區域網路UDP報文。
#include "pcap.h"/* 4 bytes IP address */typedef struct ip_address{u_char byte1;u_char byte2;u_char byte3;u_char byte4;}ip_address;/* IPv4 header */typedef struct ip_header{u_char ver_ihl; // Version (4 bits) + Internet header length (4 bits)u_char tos; // Type of serviceu_short tlen; // Total lengthu_short identification; // Identificationu_short flags_fo; // Flags (3 bits) + Fragment offset (13 bits)u_char ttl; // Time to liveu_char proto; // Protocolu_short crc; // Header checksumip_address saddr; // Source addressip_address daddr; // Destination addressu_int op_pad; // Option + Padding}ip_header;/* UDP header*/typedef struct udp_header{u_short sport; // Source portu_short dport; // Destination portu_short len; // Datagram lengthu_short crc; // Checksum}udp_header;/* prototype of the packet handler */void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);main(){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;/* Retrieve the device list */if (pcap_findalldevs(&alldevs, errbuf) == -1){fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);exit(1);}/* Print the list */for(d=alldevs; d; d=d->next){printf("%d. %s", ++i, d->name);if (d->description)printf(" (%s)\n", d->description);elseprintf(" (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");/* Free the device list */pcap_freealldevs(alldevs);return -1;}/* Jump to the selected adapter */for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);/* Open the adapter */if ( (adhandle= pcap_open_live(d->name, // name of the device65536, // portion of the packet to capture.// 65536 grants that the whole packet will be captured on all the MACs.1, // promiscuous mode1000, // read timeouterrbuf // error buffer) ) == NULL){fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n");/* Free the device list */pcap_freealldevs(alldevs);return -1;}/* Check the link layer. We support only Ethernet for simplicity. */if(pcap_datalink(adhandle) != DLT_EN10MB){fprintf(stderr,"\nThis program works only on Ethernet networks.\n");/* Free the device list */pcap_freealldevs(alldevs);return -1;}if(d->addresses != NULL)/* Retrieve the mask of the first address of the interface */netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;else/* If the interface is without addresses we suppose to be in a C class network */netmask=0xffffff;//compile the filterif(pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 ){fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");/* Free the device list */pcap_freealldevs(alldevs);return -1;}//set the filterif(pcap_setfilter(adhandle, &fcode)<0){fprintf(stderr,"\nError setting the filter.\n");/* Free the device list */pcap_freealldevs(alldevs);return -1;}printf("\nlistening on %s...\n", d->description);/* At this point, we don't need any more the device list. Free it */pcap_freealldevs(alldevs);/* start the capture */pcap_loop(adhandle, 0, packet_handler, NULL);return 0;}/* Callback function invoked bylibpcapfor every incoming packet */void packet_handler(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;/* convert the timestamp to readable format */ltime=localtime(&header->v_sec);strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);/* print timestamp and length of the packet *//* retireve the position of the ip header */ih = (ip_header *) (pkt_data +; //length of ethernet header/* retireve the position of the udp header */ip_len = (ih->ver_ihl & 0xf) * 4;uh = (udp_header *) ((u_char*)ih + ip_len);/* convert from network byte order to host byte order */printf("%s.%.6d len:%d ", timestr, header->_usec, header->len);/* print ip addresses */printf("%d.%d.%d.%d -> %d.%d.%d.%d\n",ih->saddr.byte1,ih->saddr.byte2,ih->saddr.byte3,ih->saddr.byte4,ih->daddr.byte1,ih->daddr.byte2,ih->daddr.byte3,ih->daddr.byte4);}
--------------------------------------------------------------------------------

發包

要在命令行下運行,給與參數:網卡描述符。或者添加代碼findalldevs(),那樣應很方便。
#include <stdlib.h>#include <stdio.h>#include "pcap.h"void usage();void main(int argc, char **argv) {pcap_t *fp;char error[PCAP_ERRBUF_SIZE];u_char packet[100];int i;/* Check the validity of the command line */if (argc != 2){printf("usage: %s inerface", argv[0]);return;}/* Open the output adapter */if((fp =pcap_open_live(argv[1], 100, 1, 1000, error) ) == NULL){fprintf(stderr,"\nError opening adapter: %s\n", error);return;}/* Supposing to be on ethernet, set mac destination to 1:1:1:1:1:1 */packet[0]=1;packet[1]=1;packet[2]=1;packet[3]=1;packet[4]=1;packet[5]=1;/* set mac source to 2:2:2:2:2:2 */packet[6]=2;packet[7]=2;packet[8]=2;packet[9]=2;packet[10]=2;packet[11]=2;/* Fill the rest of the packet */for(i=12;i<100;i++){packet=i%256;}/* Send down the packet */pcap_sendpacket(fp,packet,;return;}

卸載問題

winpcap卸載不乾淨的解決方法
winpcap卸載不乾淨的的時候,在windows下刪除一些檔案即可,刪除下面的檔案即可:
c:\windows\system32\Packet.dll
c:\windows\system32\drivers/npf.sys
c:\windows\system32\WanPacket.dll
c:\windows\system32\wpcap.dll
c:\windows\system32\pthreadVC.dll

相關詞條

熱門詞條

聯絡我們