下面是从这次编程过程中总结的小收获,总结如下,可能不难,但借此机会整理如下,供以后查看作为参考:
1)首先是各个类型的定义,这是以前自己没有注意过的,现在总结如下:
u_int与int,u_char与char,这两组类型变量在内存里分别所占的长度是一样的
u_int/int占4个字节,u_char/char占1个字节。
这些类型的使用场合:
u_int(unsigned int)表示无符号整形,意味着该变量的值不会出现负数;
int表示有符号整形,意味着该变量可以是正、负数;
u_char数组(或者指针),表示该变量是(或者指向)二进制数据,即里面可能有不可见字符;
而char数组则一般是可见字符串了。
uint8_t,uint16_t,uint32_其实这些都不是新的数据类型,它们只是使用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网络编程中IP和MAC地址的特殊性,下面继续说另一个函数:
Inet_ntoa():
将网络地址转换成“.”点隔的字符串格式
这个函数使用的过程中需要注意一下问题,这也是在写程序中的问题:
首先:其定义为char *inet_ntoa (struct in_addr)因此参数必须是struct in_addr类型的,不然是用不了。对比本程序中的IP地址,必须都得是定义为struct in_addr。
其次:由于in_addr是老函数,可能会报错
inet_pton和inet_ntop这2个函数是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; }