1. 程式人生 > >利用win_pcap實現解析經過本機的IP包

利用win_pcap實現解析經過本機的IP包

我們電腦上的網絡卡有一個重要功能就是判斷每一個到自己這的資料幀是不是發給自己的,如果不是的話會主動丟棄,從而保證了因特網的一定的安全性。而利用win_pcap將網絡卡設定為混雜模式可以捕獲網絡卡上的所有經過的資料包。

首先利用pcap_findalldevs()函式獲取已連線的網路介面卡列表。

int pcap_findalldevs(
        pcap_if_t  * * alldevsp,
        char   *errbuf
        )
**alldevsp是一個指向網絡卡連結串列頭一個節點的指標,errbuf返回的是錯誤資訊。

pcap_if_t結構體的定義如下:



這樣可以打印出網絡卡列表從而選擇一個網絡卡進行資料捕獲。

然後利用pcap_open_live()開啟網路裝置,這個函式定義入下:

pcap_t * pcap_open_live (
		char * device,   //網路裝置
		int snaplen,        //捕獲的資料包長度,最大65535
		int promisc,        //網路裝置工作模式,1為混雜模式
		int to_ms,           //捕獲的時間間隔
		char *ebuf        //錯誤緩衝區
)
返回值是winpcap控制代碼

再利用pcap_compile()和pcap_setfilter()設定過濾規則。

int pcap_compile(
pcap_t * p,                           //WinPcap控制代碼
struct bpf_program * fp,   //BPF規則
char * str,                              //過濾規則字串
int optimize,                         //優化
bpf_u_int32 netmask        //掩碼
)
如果我們只想捕獲IP資料包的話,就把過濾規則字串設定為“IP”,編譯過後的過濾規則將被返回至struct bpf_program * fp,這是一個編譯過的過濾規則結構體。
int pcap_setfilter(
	pcap_t * p,                          //WinPcap控制代碼
	struct bpf_program * fp  //BPF規則
)
給winpcap控制代碼設定過濾規則。

接下來就利用pcap_loop函式對資料包進行捕獲,並進行處理。

int pcap_loop(
	pcap_t *p,                //WinPcap控制代碼
	int cnt,                      // 捕獲的個數,負值表示無限捕獲
	pcap_handler call-back,    //回撥函式
	u_char* user                            //回撥函式的引數
)
回撥函式相當於是一個安卓中的事件監聽器,當事件發生後自動執行。

回撥函式call_back的原型:

typedef void( * ) pcap_handler (
u_char * user,                                //傳遞進來的使用者引數
const struct pcap_pkthdr * pkt_header,  //資料包的簡單資訊
const u_char *   pkt_data                             //資料包的原始資料
)
pcap_pkthdr結構體的定義:
struct pcap_pkthdr{
    struct timeval ts;                  //捕獲資料包的時間
    bpf_u_int32   caplen;        //捕獲的長度
    bpf_u_int32   len;                //資料包長度  
 }

利用上面這些函式,就可以實現IP包的捕獲與解析了。

完整原始碼如下:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pcap.h>

#define MAX_PRINT 80
#define MAX_LINE 16

struct ip_header
{
#if defined(WORDS_BIENDIAN)   
	u_int8_t   ip_version : 4,
	ip_header_length : 4;
#else   
	u_int8_t   ip_header_length : 4,
	ip_version : 4;
#endif   
	u_int8_t    ip_tos;
	u_int16_t   ip_length;
	u_int16_t   ip_id;
	u_int16_t   ip_off;
	u_int8_t    ip_ttl;
	u_int8_t    ip_protocol;
	u_int16_t   ip_checksum;
	struct in_addr ip_souce_address;
	struct in_addr ip_destination_address;
};

struct ether_header
{
	u_int8_t ether_dhost[6]; //目的Mac地址   
	u_int8_t ether_shost[6]; //源Mac地址   
	u_int16_t ether_type;    //協議型別   
};

void ip_protool_packet_callback(u_char*, const struct pcap_pkthdr*, const u_char*);

void ethernet_protocol_packet_callback(u_char *argument, const struct pcap_pkthdr* packet_header, const u_char* packet_content)
{
	u_short ethernet_type;
	struct ether_header *ethernet_protocol;
	u_char *mac_string;
	static int packet_number = 1;
	printf("----------------------------------------------\n");
	printf("捕獲第%d個網路資料包\n", packet_number);
	printf("資料包長度:\n");
	printf("%d\n", packet_header->len);
	printf("---------乙太網協議---------\n");
	ethernet_protocol = (struct ether_header*)packet_content;//獲得資料包內容   
	printf("乙太網型別:\n");
	ethernet_type = ntohs(ethernet_protocol->ether_type);//獲得乙太網型別   
	printf("%04x\n", ethernet_type);
	switch (ethernet_type)
	{
	case 0x0800: printf("上層協議是IP協議\n"); break;
	case 0x0806: printf("上層協議是ARP協議\n"); break;
	case 0x8035: printf("上層協議是RARP協議\n"); break;
	default:break;
	}
	printf("MAC幀源地址:\n");
	mac_string = ethernet_protocol->ether_shost;
	printf("%02x:%02x:%02x:%02x:%02x:%02x\n", *mac_string, *(mac_string + 1), *(mac_string + 2), *(mac_string + 3), *(mac_string + 4), *(mac_string + 5));
	printf("MAC幀目的地址:\n");
	mac_string = ethernet_protocol->ether_dhost;
	printf("%02x:%02x:%02x:%02x:%02x:%02x\n", *mac_string, *(mac_string + 1), *(mac_string + 2), *(mac_string + 3), *(mac_string + 4), *(mac_string + 5));
	if (ethernet_type == 0x0800)//繼續分析IP協議   
	{
		ip_protool_packet_callback(argument, packet_header, packet_content);
	}
	printf("----------------------------------------------\n");
	packet_number++;
}


void ip_protool_packet_callback(u_char *argument, const struct pcap_pkthdr* packet_header, const u_char* packet_content)
{
	struct ip_header *ip_protocol;
	u_int header_length;
	u_int offset;
	u_char tos;
	u_int16_t checksum;
	//MAC首部是14位的,加上14位得到IP協議首部   
	ip_protocol = (struct ip_header *) (packet_content + 14);
	checksum = ntohs(ip_protocol->ip_checksum);
	tos = ip_protocol->ip_tos;
	offset = ntohs(ip_protocol->ip_off);
	printf("---------IP協議---------\n");
	printf("版本號:%d\n", ip_protocol->ip_version);
	printf("首部長度:%d\n", ip_protocol->ip_header_length);
	printf("服務質量:%d\n", tos);
	printf("總長度:%d\n", ntohs(ip_protocol->ip_length));
	printf("標識:%d\n", ntohs(ip_protocol->ip_id));
	printf("偏移:%d\n", (offset & 0x1fff) * 8);
	printf("生存時間:%d\n", ip_protocol->ip_ttl);
	printf("協議型別:%d\n", ip_protocol->ip_protocol);
	switch (ip_protocol->ip_protocol)
	{
	case 1: printf("上層協議是ICMP協議\n"); break;
	case 2: printf("上層協議是IGMP協議\n"); break;
	case 6: printf("上層協議是TCP協議\n"); break;
	case 17: printf("上層協議是UDP協議\n"); break;
	default:break;
	}
	printf("檢驗和:%d\n", checksum);
	printf("源IP地址:%s\n", inet_ntoa(ip_protocol->ip_souce_address));
	printf("目的地址:%s\n", inet_ntoa(ip_protocol->ip_destination_address));
}


int main()
{
	pcap_t *fp;
	pcap_if_t *alldev, *d;
	char errbuf[30];
	u_char user[30];
	int retval, i, inum;
	bpf_u_int32 net_mask; //掩碼地址   
	bpf_u_int32 net_ip;  //網路地址   
	struct bpf_program bpf_filter;//BPF過濾規則   
	char bpf_filter_string[] = "ip";
	retval = pcap_findalldevs(&alldev, errbuf);
	if (retval == -1)
	{
		printf("find all devs failed\n");
	}
	i = 0;
	for (d = alldev; d != NULL; d = d->next)
	{
		printf("%d. %s", i, d->name);
		i++;
		if (d->description == NULL)
		{
			printf("description: none\n");
		}
		else
		{
			printf("description: %s\n", d->description);
		}
	}
	if (i == 0)
	{
		printf("no network dev avaliable\n");
	}
	printf("choose one	 interface number:");
	scanf("%d", &inum);
	for (d = alldev, i = 0; i < inum ; d = d->next, i++);
	memset(errbuf, 0, sizeof(errbuf));
	fp = pcap_open_live(d->name, 65535, 1, 1000, errbuf);
	if (fp == NULL)
	{
		printf("open failed\n");
	}	
	pcap_lookupnet(d->name, &net_ip, &net_mask, errbuf);
	pcap_compile(fp, &bpf_filter, bpf_filter_string, 0, net_mask);   
	pcap_setfilter(fp, &bpf_filter);
	retval = pcap_loop(fp, 10, ethernet_protocol_packet_callback, user);
	//printf("%d\n", retval);
	return 0;
}
部分程式碼轉自:http://www.cnblogs.com/luxiaoxun/archive/2012/08/05/2623641.html

win_pcap中文官網文件:http://www.ferrisxu.com/WinPcap/html/index.html