1. 程式人生 > >winpcap程式設計抓包例項和windump使用

winpcap程式設計抓包例項和windump使用

http://www.winpcap.org/archive/
官方文件

http://www.ferrisxu.com/WinPcap/html/index.html

http://www.winpcap.org/
http://www.winpcap.org/windump/install/default.htm
http://www.360doc.com/content/11/0319/10/54470_102500630.shtml

WinDump的使用:

WinDump.exe version 3.9.5, based on tcpdump version 3.9.5
WinPcap version 4.1.3 (packet.dll version 4.1.0.2980), based on libpcap version 1.0 branch 1_0_rel0b (20091008)
Usage: WinDump.exe [-aAdDeflLnNOpqRStuUvxX] [ -B size ] [-c count] [ -C file_size ]
[ -E algo:secret ] [ -F file ] [ -i interface ] [ -M secret ]
[ -r file ] [ -s snaplen ] [ -T type ] [ -w file ]
[ -W filecount ] [ -y datalinktype ] [ -Z user ]
[ expression ]
windump -D 

列出所有的網絡卡。

windump -h 列出幫助。

WinDump.exe -h  >mylog.txt 2>&1
 
WinDump.exe -i 4

windump -i 2 port 80

通過埠80從介面#2記錄所有流量

windump -i 2 host im-chat.com

記錄所有從the host im-chat.com.或來或到介面#2的流量

windump -i 1 net 127

這些引數也可以自由組合。
 
 WinDump手冊
命令格式
windump [ -aBdDeflnNOpqRStvxX ] [ -c count ] [ -F file ] [ -i interface ] [ -m module ] [ -r file ] [ -s snaplen ] [ -T type ] [ -w file ] [ -E algo:secret ] [ expression ]
描述 Tcpdump 輸出網絡卡資料包中匹配布林表示式的資料包頭。
SunOS 系統下使用nit或bpf:要執行tcpdump,你必須有對dev/nit或/dev/bpf*的權利。Solaris系統下使用dlpi:你必須有對 網路假設定的權利。HP-UX系統下使用dlpi:你應該以超級使用者ROOT或安裝SETUID到ROOT下。IRIX下使用SNOOP:你應該以超級用 戶ROOT或安裝SETUID到ROOT下。
LINUX下:你應該以超級使用者ROOT或安裝SETUID到ROOT下。
在系統Ultrix和Digital UNIX:
命令引數
-a 將網路和廣播地址轉化為名稱
-c 接收指定資料包後退出
-d 接收人可讀的包匹配編譯程式碼到標準輸出,然後停止
-dd 以C程式分段方式捕獲包匹配程式碼
-ddd 以十進位制資料形式捕獲包匹配程式碼
-e 在每個捕獲行列印鏈路層頭標
-E algo:secret為解密IPSE ESP包使用演算法。
演算法可以是des-cbc, 3des-cbc, blowfish-cbc, rc3-cbc, cast128-cbc, 或none。
預設值是desc-cbc。只有當TCPDUMP編譯時使用啟用加密選項時,才可以解密資料包。
Secret是ESP密匙是ASCII碼。當前還不能認為一定是二進位制值。該選項是以RFC2406ESP為假設,而不是RFC1827 ESP。只用於除錯,不鼓勵用真正的密碼作為選項。當你在PS或其他場合,把IPSEC密碼寫在命令列上時,會被他人看到。
-f 不用符號而用數字方式輸出外部英特網地址 -F 使用檔案作為過濾表示式的輸入。命令列的其他部分會被忽略。
-i 在介面上監聽。如果沒有指定,TCPDUMP將搜尋系統介面列表中最小,被配置啟用的介面(LOOPBACK介面除外)。可用最先匹配替換這種關係。在 WINDOWS中介面可以是網絡卡的名稱,或是網絡卡的號碼(-D引數可顯示該號碼)。核心為2。2或其後的LINUX系統,引數“ANY”可以獲取所有介面 的資料。應注意的是在混亂模式下不能使用“ANY”引數。
-l 標準輸出行快取。如果你想在捕獲資料時檢視的話,這個引數很有用。
例如:“tcpdump -l │ tee dat” or “tcpdump -l > dat & tail -f dat”.” n 不要將地址(如主機地址,埠號)轉換為名稱 -N 不要列印主機名稱的域名限定。如:如果你使用該引數,TCPDUMP會輸出“NIC”而不是“NIC。DDN。MIL”。
-m 從檔案模組中載入SMI MIB 模組定義。這個選項可以為TCPDUMP載入多個MIB模組
-O 不要執行包匹配程式碼優化器。只有在你懷疑優化器有問題時可以使用這個引數。
-p 不要讓介面處於“混亂”模式。注意介面可能由於其他原因處於“混亂”模式;因此“-p”不能用作乙太網絡主機或廣播的縮寫。
-q 快速(安靜?)輸出。列印較少的協議資訊,因此輸出行更短。
-r 從檔案中讀取包(與引數據-W一起使用)。如果檔案是“-”就使用標準輸入。
-s 不使用預設的68個位元組,更改從每個包中獲取資料的位元組數量( SunOS系統實際最小為96)。對於IP,ICMP,TCP和UDP包68個位元組已足夠,但是對命名服務和NFS包,他們的協議會被截斷(見下面)。包 被截斷是因為在使用引數“[│proto]”輸出時指定受限制的快照,proto是被截斷協議層的名稱。注意如果使用大的快照會增加處理包的時間,並且明 顯地減少包的快取數量。也許會導致包的丟失。你應該將snaplen 設定成你感興趣協議的最小數。當snaplen 為0時接收整個包。
-T 根據表示式將選中的資料包表達成指定的型別。當前已有的型別有CNFP(Cisco的網路流量協議),rpc(遠端程式呼叫),rtp(實時程式協議), rtcp(實時程式控制協議),snmp(簡單網路管理協議),vat(可視單頻工具),和wb(分散式白板)。 -R 假設ESP/AH包遵守舊的說明(RFC1825到RFC1829)。如果該引數被指定,TCPDUMP不打輸出域。因為在ESP/AH說明中沒有協議版 本,TCPDUMP就無法推斷出其版本號。 -S 輸出絕對TCP序列號,而不是相對號。
-t 每個捕獲行不要顯示時間戳。 -tt 每個捕獲行顯示非格式化的時間時間戳。
-v 詳細輸出。例如,顯示生存時間TTL,識別符號,總長度和IP資料包的選項。也進行額外的包完整性較驗,如驗證IP和ICMP的頭標較驗值。
-vv 更為詳細的輸出。例如,顯示NFS中繼包中的其他域。
-vvv 很詳細的輸出。如,完全輸出TELNET SB… SE選項。帶-X引數的TELNET,列印並以十六進位制輸出。
-w 不對原始資料包解析列印而是轉到檔案中去。以後可用-r選項列印。當檔名為“-”表示標準輸出。 -x 以十六進位制(去除鏈路層頭標)輸出每個資料包。輸出整個包的小部分或snaplen 個位元組。
-X 輸出十六進位制同時,輸出ASCII碼。如果-x也被設定,資料包會以十六制/ASCII碼顯示。這對於分析新協議非常方便。如果-x也沒有設定,一些資料包的部分會以十六制/ASCII碼顯示。 Win32特殊擴充套件
-B 以千位元組為單位設定驅動快取。預設快取為1M(即1000)。如果在獲取資料包時有資料丟失,建議使用該引數增大核心快取大小,因為驅動快取大小對資料捕獲效能有很大影響。
-D 顯示系統上可用的網絡卡列表。該引數將返回每塊網絡卡的號碼,名稱和描述。


這個包裡面有很多例子,下面我們就可以程式設計實現了:

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
#define HAVE_REMOTE
#include <pcap.h>
#include <iomanip>
#include <string>
#include <stdio.h>
using namespace std;

#include <pcap.h>
#pragma comment(lib,"wpcap.lib")  
#pragma comment(lib,"ws2_32.lib")  

//https://github.com/yfnick/winpcap/blob/master/ref_http/main.cpp
//demo  https://github.com/yfnick/winpcap

/*Ethernet Heder*/
struct ether_header
{
	u_int8_t  ether_dhost[6];      /* destination eth addr */
	u_int8_t  ether_shost[6];      /* source ether addr    */
	u_int16_t ether_type;          /* packet type ID field */
};

/* 4 bytes IP address */
struct ip_address{
	u_char byte1;
	u_char byte2;
	u_char byte3;
	u_char byte4;
};

/* IPv4 header */
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
};

/* UDP header*/
struct udp_header{
	u_short sport;          // Source port
	u_short dport;          // Destination port
	u_short len;            // Datagram length
	u_short crc;            // Checksum
};

/*TCP Header*/
struct tcp_header
{
	u_int16_t th_sport;         /* source port */
	u_int16_t th_dport;         /* destination port */
	u_int32_t th_seq;             /* sequence number */
	u_int32_t th_ack;             /* acknowledgement number */
	u_int16_t th_len_resv_code; //   Datagram   length and reserved code
	u_int16_t th_win;           /* window */
	u_int16_t th_sum;           /* checksum */
	u_int16_t th_urp;           /* urgent pointer */
};

int main()
{
	//retrieve the devices list
	pcap_if_t *all_devs;
	char err_buff[PCAP_ERRBUF_SIZE];
	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &all_devs, err_buff) == -1){
		cerr << "Error in pcap_findalldevs_ex " << err_buff << endl;
		return -1;
	}

	//get the device index,default is the first one
	int dev_idx = 2;
	pcap_if_t *dev = all_devs;
	for (int i = 0; i < dev_idx; ++i, dev = dev->next);//jump to the device of the specified index
	cout << "Listen on: " << dev->name << endl;
	cout << "****************************************" << endl;

	//get the netcard adapter
	pcap_t *adpt_hdl = pcap_open(dev->name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, err_buff);
	if (adpt_hdl == NULL){
		cerr << "Unable to open adapter " << dev->name << endl;
		pcap_freealldevs(all_devs);
		return -1;
	}

	/* At this point, we don't need any more the device list. Free it */
	pcap_freealldevs(all_devs);

	//analyze each packet
	struct pcap_pkthdr *header;
	const u_char *pkt_data;

	int rst = 0;
	char x;
	FILE *fp, *fq;
	fp = fopen("http.txt", "w+");
	fq = fopen("ac.txt", "w+");
	while ((rst = pcap_next_ex(adpt_hdl, &header, &pkt_data)) >= 0)
	{
		if (rst == 0){
			//time out and not packet captured
			continue;
		}

		ether_header *eh = (ether_header*)pkt_data;

		if (ntohs(eh->ether_type) == 0x0800){ // ip packet only

			ip_header *ih = (ip_header*)(pkt_data + 14);

			if (ntohs(ih->proto) == 0x0600){ // tcp packet only

				int ip_len = ntohs(ih->tlen);//ip_len = ip_body + ip_header

				bool find_http = false;

				string http_txt = "";
				//char* http;
				char* ip_pkt_data = (char*)ih;

				for (int i = 0; i < ip_len; ++i){

					//check the http request

					if (!find_http && ( 3 < ip_len && strncmp(ip_pkt_data + i, "GET", strlen("GET")) == 0)

						|| (i + 4 < ip_len && strncmp(ip_pkt_data + i, "POST", strlen("POST")) == 0)){

						find_http = true;

					}

					//check the http response

					if (!find_http && i + 8 < ip_len && strncmp(ip_pkt_data + i, "HTTP/1.1", strlen("HTTP/1.1")) == 0){

						find_http = true;
					}

					//collect the http text

					if (find_http){

						http_txt += ip_pkt_data[i];
						fputc(ip_pkt_data[i], fp);
						if ((ip_pkt_data[i] > 'A'&&ip_pkt_data[i]<'Z') || (ip_pkt_data[i]>'a'&&ip_pkt_data[i]<'z'))
						{
							if ((ip_pkt_data[i]>'A'&&ip_pkt_data[i] < 'Z'))
								x = ip_pkt_data[i] - 'A' + 'a';
							else x = ip_pkt_data[i];
							fputc(x, fq);
						}
					}
				}
				//print the http request or response
				if (http_txt != ""){
					cout << http_txt;
					cout << endl << "***********************************************************" << endl << endl;
				}
			}
		}
	}
	return 0;
}

#define HAVE_REMOTE
#define _CRT_SECURE_NO_WARNINGS 1

#include <pcap.h>

#pragma comment(lib,"wpcap.lib")   

/* 回撥函式原型 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

int main(int argc, char **argv)
{
	pcap_if_t *alldevs;
	pcap_if_t *d;
	int inum;
	int i = 0;
	pcap_t *adhandle;
	char errbuf[PCAP_ERRBUF_SIZE];
	pcap_dumper_t *dumpfile;



	/* 檢查程式輸入引數 */
	if (argc != 2)
	{
		printf("usage: %s filename", argv[0]);
		return -1;
	}

	/* 獲取本機裝置列表 */
	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,            // 要捕捉的資料包的部分
		// 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;
	}

	/* 開啟堆檔案 */
	dumpfile = pcap_dump_open(adhandle, argv[1]);

	if (dumpfile == NULL)
	{
		fprintf(stderr, "\nError opening output file\n");
		return -1;
	}

	printf("\nlistening on %s... Press Ctrl+C to stop...\n", d->description);

	/* 釋放裝置列表 */
	pcap_freealldevs(alldevs);

	/* 開始捕獲 */
	pcap_loop(adhandle, 0, packet_handler, (unsigned char *)dumpfile);

	return 0;
}

/* 回撥函式,用來處理資料包 */
void packet_handler(u_char *dumpfile, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	/* 儲存資料包到堆檔案 */
	pcap_dump(dumpfile, header, pkt_data);
}