winpcap编程中遇到的问题及解决方法

下面是从这次编程过程中总结的小收获,总结如下,可能不难,但借此机会整理如下,供以后查看作为参考:

1)首先是各个类型的定义,这是以前自己没有注意过的,现在总结如下:

u_intintu_charchar,这两组类型变量在内存里分别所占的长度是一样的

u_int/int4个字节,u_char/char1个字节。

这些类型的使用场合:

u_intunsigned int)表示无符号整形,意味着该变量的值不会出现负数;

int表示有符号整形,意味着该变量可以是正、负数;

u_char数组(或者指针),表示该变量是(或者指向)二进制数据,即里面可能有不可见字符;

char数组则一般是可见字符串了。

uint8_tuint16_tuint32_其实这些都不是新的数据类型,它们只是使用typedef给类型起的别名,这样其实是为了程序的规范,或者是为了以后修改代码比较方便。

查看定义可以发现如下:

typedef unsigned char u_int8_t;(u_char)

typedef signed char int8_t;

typedef unsigned int u_int16_t;(u_int)

typedef signed int int16_t;

2)winpcap网络编程中有其很多特殊性,因此说一下其中的某些函数。

首先这个程序中最重要的ntohs() 

解释:将一个无符号短整形数从网络字节顺序转换为主机字节顺序。

#include <winsock.h>  

u_short PASCAL FAR ntohs( u_short netshort); 

netshort:一个以网络字节顺序表达的16位数。

其实也就是将一个16位数由网络字节顺序转换为主机字节顺序。

好,那么问题来了,什么是网络字节顺序与主机字节顺序呢,这是自己在编程过程中一直存在的问题,后来终于看懂了:

首先介绍两种字节序类型

1. Little endian:将低序字节存储在起始地址

2. Big endian:将高序字节存储在起始地址

前者就是我们平时用的x86系列机子的顺序,及我们上学期在计算机组成原理中学的,地址低位存储值的低位  ;地址高位存储值的高位。

后者是比较直观的方式,只需要把内存地址从左到右按照由低到高的顺序写出。这就是网络字节顺序。网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。

为了更直观的了解,下面我从程序中截了一个屏

 

我把程序中nthos()这个注释掉的结果是如上所数据示的,为了方便看,我加了一个display()函数,这样对比看的比较清晰。

3)由于winpcap网络编程中IPMAC地址的特殊性,下面继续说另一个函数:

Inet_ntoa():

将网络地址转换成“.”点隔的字符串格式

这个函数使用的过程中需要注意一下问题,这也是在写程序中的问题:

首先:其定义为char *inet_ntoa (struct in_addr)因此参数必须是struct in_addr类型的,不然是用不了。对比本程序中的IP地址,必须都得是定义为struct in_addr

其次:由于in_addr是老函数,可能会报错

inet_ptoninet_ntop2个函数是IP地址转换函数是比较新的函数,微软希望我们用他们新的函数,因此我们这里要加个define定义就行。一开始在头部加

#define _WINSOCK_DEPRECATED_NO_WARNINGS仍然是报错的,后来在项目属性->c++->预处理器里面加上 _WINSOCK_DEPRECATED_NO_WARNINGS就可以运行了。

第三个问题:inet_nota()函数的数据覆盖问题,可参考下面的博客去看,(http://blog.sina.com.cn/s/blog_9f48885501018xm6.html

inet_ntoa函数的输入参数是u_int型的ip地址,返回的却是指向ip字符串的指针,很明显,ip字符串所占的内存是在函数内部分配的,而我们并不需要释放该内存,所以,它分配的内存是静态的,也就是说下一次调用该函数时会覆盖这个数组的内容。为了解决这个问题,我们只需备份数组里的内容即可。这也就是程序中ARP回环函数中为什么要用memcpy()函数拷贝一下数据内容了。

4)这个是比较简单的问题,但是当时也困惑了好久,后来在同学范鹏的帮助下才解决。

就是在输出结果的 比如结果为0001,但是其输出是1,前面的0就都省去了,这应该是程序语言设计成这样的,其实在前面加一句setw(2) << setfill(‘0’) 就可以了,这个和C语言中的%02x是类似的。这个地方还要在程序头部加一个 #include <iomanip>

setfill,setw,setbase,setprecision这些函数都需要包含这个头文件。


5)正对VS环境的配置,建议参照这个博客,很详细http://www.findspace.name/easycoding/871



最后附上自己编写的抓取三层协议的程序,抓取帧,ip,icmp,ARP的分析程序。

//#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include"stdafx.h"
#include <iostream>

#include <iomanip>

#include"pcap.h"
#include<string>
#include<cstring>
using namespace std;



//ethernet 帧头
struct ethernet
{                   //u_char是无符号的char型 而char是有符号的char型,u_char和char都占一个字节,8个bit位
	u_int8_t ether_dst[6];   //6位目的地址字段
	u_int8_t ether_src[6];   //6位源地址字段
	u_short ether_type;   //协议类型

};

//ip头部
struct ip
{

#if defined(WORDS_BIENDIAN)   
	u_int8_t   ip_v : 4,
		ip_hl : 4;
#else   
	u_int8_t   ip_hl : 4,
		ip_v : 4;
#endif   

	u_int8_t ip_tos;  //服务类型  8
	u_int16_t ip_len; //总长度   16
	u_int16_t ip_id;  //标示,标识这个IP数据包.
	u_int16_t ip_off; //标志位碎片偏移

	u_int8_t ip_ttl;  //TTL
	u_int8_t ip_pro;    //协议类型
	u_int16_t ip_cks; //头部校验和
	struct in_addr ip_src; //ip源
	struct in_addr ip_dst; //目的地址
};


//ICMP header
struct 	icmp
{
	u_int8_t icmp_type;  //类型
	u_int8_t code;  //代码
	u_int16_t icmp_cks;  //校验和
	u_int16_t icmp_id;    //标示
	u_int16_t icmp_seq;    //序列号

};


//ARP header
struct arp
{
	u_short arp_hrd;    //硬件地址类型
	u_short arp_pro;    //协议地址类型
	u_char arp_hln;    //硬件地址长度
	u_char arp_pln;    //协议地址长度
	u_short arp_op;     // ARP/RARP 操作
	u_char arp_eth_src[6];    //发送站硬件地址MAC
	
	u_char arp_src[4];    //发送站协议地址IP
	u_char arp_eth_dst[6];    //目的站硬件地址MAC
	u_char  arp_dst[4];    //目的站协议地址IP
};

void ip_packet_loop(u_char *pram, const struct pcap_pkthdr* header, const u_char* data);
void arp_packet_loop(u_char *pram, const struct pcap_pkthdr* header, const u_char* data);
void icmp_packet_loop(u_char *pram, const struct pcap_pkthdr* header, const u_char* data);

void display_data(const u_char* data, int length);





void ethernet_packet_loop(u_char *pram, const struct pcap_pkthdr* header, const u_char* data)
{
	struct ethernet *eth_h;
	eth_h = (struct ethernet*)data;//获得数据包内容
	static int p_num = 1;  //从第一个数据包开始
	cout << endl;
	cout << "第" << p_num << "个数据包" << endl;

	u_short eth_type;
	eth_type = ntohs(eth_h->ether_type);//以太网类型
	u_char *macstring1;
	u_char *macstring2;
	macstring1 = eth_h->ether_src;
	macstring2 = eth_h->ether_dst;

	cout << "*************帧结构***************" << endl;
	cout << "  目标MAC地址:     ";
	cout << setw(2) << setfill('0') << hex << int(*macstring2) <<":"<< setw(2) << setfill('0') << hex << int(*(macstring2 + 1)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring2 + 2)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring2 + 3)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring2 + 4)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring2 + 5));
	cout << endl;
	cout << "  源MAC地址:       ";
	cout <<setw(2)<< setfill('0') << hex << int(*macstring1) << ":" << setw(2)<< setfill('0') << hex << int(*(macstring1+1)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring1+2)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring1 + 3)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring1 + 4)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring1 + 5));
	cout << endl;
	


	cout << "  以太网类型为:   " << setw(4) << setfill('0') << hex << eth_type << endl;
	cout << "  上层协议为:    ";
	switch (eth_type)
	{
	case 0x0800: cout << " IP协议 " << endl; break;
	case 0x0806: cout << " ARP协议 " << endl; break;
	case 0x86dd: cout << " ICMP协议" << endl; break;
	default:break;
	}

	cout << endl;

	if (eth_type == 0x0800)//继续分析IP协议  
	{

		ip_packet_loop(pram, header, data);
	}


	else if (eth_type == 0x0806)
	{
		arp_packet_loop(pram, header, data);
	}



	p_num++;

}


void ip_packet_loop(u_char *pram, const struct pcap_pkthdr* header, const u_char* data)
{

	struct ip *ip_h;
	ip_h = (struct ip *) (data + 14);

	u_int offset;
	offset = ntohs(ip_h->ip_off);

	cout << "*************IP协议*************" << endl;
	cout << "  版本号          " << int(ip_h->ip_v) << endl;
	cout << "  首部长度        " << int(ip_h->ip_hl) << endl;
	cout << "  服务质量        " << ntohs(ip_h->ip_tos) << endl;
	cout << "  总长度          " <<  ntohs(ip_h->ip_len) << endl;
	cout << "  标识            " << ntohs(ip_h->ip_id) << endl;
	cout << "  偏移            " << setw(4) << setfill('0') << ntohs(ip_h->ip_off)<< endl;
	cout << "  生存时间        " << int(ip_h->ip_ttl) << endl;
	cout << "  协议类型        " << setw(2) << setfill('0') << int(ip_h->ip_pro) << endl;
	cout << "  校验和          " << setw(4) << setfill('0') << ntohs(ip_h->ip_cks) << endl;

	cout << "  源IP地址:      " << inet_ntoa(ip_h->ip_src) << endl;
	cout << "  目的IP地址:    " << inet_ntoa(ip_h->ip_dst);

	cout << endl;


	cout  << endl;
	

	if (ip_h->ip_pro == 1)   //转到分析Icmp
	{
		cout << "上层协议是ICMP协议" << endl;
		icmp_packet_loop(pram, header, data);
	}


}




void arp_packet_loop(u_char *pram, const struct pcap_pkthdr* header, const u_char* data)
{

	struct arp * arp_h;
	arp_h = (struct arp*)(data + 14);

	u_char *macstring1;
	u_char *macstring2;
	macstring1 = arp_h->arp_eth_src;
	macstring2 = arp_h->arp_eth_dst;

	switch (ntohs(arp_h->arp_op))
	{
	case 1:cout << "ARP请求协议" << endl; break;
	case 2:cout << "ARP应答协议" << endl; break;
	case 3:cout << "RARP请求协议" << endl; break;
	case 4:cout << "RARP应答协议" << endl; break;
	default:break;
	}
	cout << "*************ARP协议*************" << endl;

	cout << "  硬件类型:        " <<  ntohs(arp_h->arp_hrd) << endl;;
	cout << "  协议类型:        " << setw(4) << setfill('0') << ntohs(arp_h->arp_pro) << endl;
	cout << "  硬件地址长度:    " <<int(arp_h->arp_hln) << endl;
	cout << "  协议地址长度:    " <<  int(arp_h->arp_pln) << endl;
	cout << "  操作码:         " << ntohs(arp_h->arp_op) << endl;


	cout << "  发送方MAC地址:   ";
	cout << setw(2) << setfill('0') << hex << int(*macstring1) << ":" << setw(2) << setfill('0') << hex << int(*(macstring1 + 1)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring1 + 2)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring1 + 3)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring1 + 4)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring1 + 5));
	cout << endl;

	
	struct in_addr arp_ip_dst;
	struct in_addr arp_ip_src;

	memcpy((void*)&arp_ip_src, (void*)&arp_h->arp_dst, sizeof(struct in_addr));

	cout << "  发送方IP:        " << inet_ntoa(arp_ip_src) << endl;

	cout << "  接收方MAC地址:   ";
	cout << setw(2) << setfill('0') << hex << int(*macstring2) << ":" << setw(2) << setfill('0') << hex << int(*(macstring2 + 1)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring2 + 2)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring2 + 3)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring2 + 4)) << ":" << setw(2) << setfill('0') << hex << int(*(macstring2 + 5));
	cout << endl;
	memcpy((void*)&arp_ip_dst, (void*)&arp_h->arp_dst, sizeof(struct in_addr));
	cout << "  接收方IP地址:    " << inet_ntoa(arp_ip_dst) << endl;

	cout << endl;
	
}


void icmp_packet_loop(u_char *pram, const struct pcap_pkthdr* header, const u_char* data)
{
	struct icmp *icmp_h;
	icmp_h = (struct icmp*)(data + 14 + 20);

	cout << "*************ICMP协议*************" << endl;
	cout << "  ICMP类型:    "  << int(icmp_h->icmp_type)<<endl;//获得ICMP类型
	cout << "  code:        " << ntohs(icmp_h->code) << " ";
	switch (icmp_h->icmp_type)
	{
		cout << "  ";
	case 0:cout << "回应应答" << endl; break;
	case 3:
		switch (icmp_h->code)
		{
		case 0:cout << "网络不可达" << endl; break;
		case 1:cout << "主机不可达" << endl; break;
		case 2:cout << "协议不可达" << endl; break;
		case 3:cout << "端口不可达" << endl; break;
		case 4:cout << "需要分段但设置为不允许分段" << endl; break;
		case 5:cout << "源路由失败" << endl; break;
		case 6:cout << "目的站点网络未知" << endl; break;
		case 7:cout << "目的主机网络未知" << endl; break;
		case 8:cout << "原主机被隔离" << endl; break;
		case 9:cout << "与目的站点网络的通信被禁止" << endl; break;
		case 10:cout << "与目的站点主机的通信被禁止" << endl; break;
		case 11:cout << "对请求的服务类型,网络不可达" << endl; break;
		case 12:cout << "对请求的服务类型,主机不可达" << endl; break;
		default:cout << "其他问题" << endl; break;
		}
		break;
	case 4:cout << "源抑制" << endl; break;
	case 5:switch (icmp_h->code)
	{
	case 0:cout << "网络重定向" << endl; break;
	case 1:cout << "主机重定向" << endl; break;
	case 2:cout << "服务类型和网络重定向" << endl; break;
	case 3:cout << "服务类型和主机重定向" << endl; break;
	}
	case 8:cout << "回应请求" << endl; break;
	case 9:cout << "路由器公告" << endl; break;
	case 10:cout << "路由器请求" << endl; break;
	case 11:switch (icmp_h->code)
	{
	case 0:cout << "传输期间TTL超时" << endl;
	case 1:cout << "数据段组装期间TTL超时" << endl;
	}break;
	case 12:switch (icmp_h->code)
	{
	case 0:cout << "损坏的IP头部" << endl;
	case 1:cout << "缺少选型" << endl;
	}break;
	case 13:cout << "时标请求" << endl; break;
	case 14:cout << "时标应答" << endl; break;
	case 15:cout << "信息请求" << endl; break;
	case 16:cout << "信息应答" << endl; break;
	case 17:cout << "地址掩码请求" << endl; break;
	case 18:cout << "地址掩码应答" << endl; break;

	default:break;
	}
	
	cout << "  ICMP校验和   " << setw(4) << setfill('0')<<ntohs(icmp_h->icmp_cks) << endl;//获得ICMP校验和 
	cout << "  ID           " << setw(4) << setfill('0') << ntohs(icmp_h->icmp_id)<<endl;
	cout << "  序列号       " << setw(4) << setfill('0') << ntohs(icmp_h->icmp_seq);
	cout << endl;
	
}




int 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[] = "";
	struct bpf_program fcode;

	/*
	int pcap_findalldebs_ex(
	char *source,
	struct pcap_rmtauth *auth,
	pcap_if_t **alldevs,
	char *errbuf);

	PCAP_SRC_IF_STRING代表用户想从一个本地文件开始捕获内容
	*/


	//获得设备列表

	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
	{
		//结果为-1表示获取适配器表失败

		cout << "获取设备列表失败";
		exit(1);//exit(0)代表正常退出
	}

	//打印列表

	for (d = alldevs; d; d = d->next)  //从第一各设备开始,切换到另一个设备
	{
		cout << ++i << d->name;
		if (d->description)
			cout << d->description << endl;
		else
			cout << "没有获得详细信息描述" << endl;

	}
	if (i == 0)
	{
		cout << "没有找到接口,请确保您安装了Winpcap程序" << endl;
		return -1;
	}

	cout << "请您输入设备号:  ";
	cin >> inum;
	if (inum<1 || inum>i)
	{
		cout << "您输入的设备号超出了显示的范围" << endl;

		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)
	{
		cout << "不能打开适配器,设备不被winpcap支持" << endl;

		pcap_freealldevs(alldevs);//释放设备列表
		return -1;
	}




	// 检查数据链路层,k看是否是以太网 
	if (pcap_datalink(adhandle) != DLT_EN10MB)
	{
		cout << "这个不工作在以太网范围" << endl;

		pcap_freealldevs(alldevs);  // 释放设备列表 
		return -1;
	}

	if (d->addresses != NULL) //获得接口第一个地址的掩码

		netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
	else

		netmask = 0xffffff;  //如果接口没有地址,那么我们假设一个C类的掩码 


	cout << "请输入要过滤的包: " ;
	cin >> packet_filter;


							 //  编译过滤规则
	if (pcap_compile(adhandle, //适配器处理对象
		&fcode,
		packet_filter,  //过滤
		1,               //优化标志
		netmask        //子网掩码
		) < 0)
	{
		cout << "不能编译这个包" << endl;

		pcap_freealldevs(alldevs);
		return -1;
	}

	//设置过滤规则
	if (pcap_setfilter(adhandle, &fcode) < 0)
	{
		cout << "设置过滤规则错误" << endl;

		pcap_freealldevs(alldevs);
		return -1;
	}

	int packet_num;//设置抓的包的个数

	cout << "请输入抓包个数:   " ;
	cin >> packet_num;
	
	pcap_loop(adhandle, packet_num, ethernet_packet_loop, NULL); //回调函数

	system("pause");
	return 0;
}






猜你喜欢

转载自blog.csdn.net/ax_hacker/article/details/50115537
今日推荐