複製鏈接
請複製以下鏈接發送給好友

winpcap

鎖定
winpcap(windows packet capture)是windows平台下一個免費,公共的網絡訪問系統。開發winpcap這個項目的目的在於為win32應用程序提供訪問網絡底層的能力。它用於windows系統下的直接的網絡編程
中文名
winpcap
外文名
windows packet capture
使用平台
windows
目    的
為win32提供訪問網絡底層能力
作    用
網絡分析,故障排除等方面

winpcap特點介紹

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

winpcap驅動功能

  1. 捕獲原始數據包,包括在共享網絡上各主機發送/接收的以及相互之間交換的數據包;
  2. 在數據包發往應用程序之前,按照自定義的規則將某些特殊的數據包過濾掉;
  3. 在網絡上發送原始的數據包;
  4. 收集網絡通信過程中的統計信息。
winpcap結構 winpcap結構
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網絡上。著名軟件tcpdump及idssnort都是基於libpcap編寫的,此外Nmap掃描器也是基於libpcap來捕獲目標主機返回的數據包的。
winpcap提供給用户兩個不同級別的編程接口:一個基於libpcap的wpcap.dll,另一個是較底層的packet.dll。對於一般的要與unix平台上libpcap兼容的開發來説,使用wpcap.dll是當然的選擇。

winpcap內部結構

圖1 Winpcap的各個組成部分 圖1 Winpcap的各個組成部分
Winpcap是針對Win32平台上的抓包和網絡分析的一個架構。它包括一個核心態包過濾器,一個底層的動態鏈接庫(packet.dll)和一個高層的不依賴於系統的庫(wpcap.dll)。
為什麼使用“architecture”而不是“library”呢?因為抓包是一個要求與網絡適配器(網卡)和操作系統交互的底層機制,而且與網絡的實施也有密切關係,所以僅用“library”不能充分表達Winpcap的作用。
圖1表明了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最主要的目的是作為一個允許協議驅動發送和接收網絡(LANWAN)上的數據包而不必關心特定的適配器或特定的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驅動連接。
圖2 NPF在NDIS棧中的位置 圖2 NPF在NDIS棧中的位置
NPF是一個協議驅動。從性能方面來看,這不是最好的選擇,但是它合理地獨立於MAC層並且有權使用原始通信(raw traffic)。
圖2表現了NPF在NDIS棧中的位置:

winpcap結構基礎

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

winpcap抓包

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

winpcap實例

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

winpcap獲得網卡接口

#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);
}
--------------------------------------------------------------------------------

winpcap抓包

本程序俘獲局域網內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 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 header*/
typedef struct udp_header{
u_short sport; // Source port
u_short dport; // Destination port
u_short len; // Datagram length
u_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);
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");
/* 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 device
65536, // portion of the packet to capture.
// 65536 grants that the whole packet will be captured on all the MACs.
1, // promiscuous mode
1000, // read timeout
errbuf // 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 filter
if(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 filter
if(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
);
}

--------------------------------------------------------------------------------

winpcap發包

要在命令行下運行,給與參數:網卡描述符。或者添加代碼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卸載不乾淨的解決方法
winpcap卸載不乾淨的的時候,在windows下刪除一些文件即可,刪除下面的文件即可: [3-4] 
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
參考資料