« | September 2025 | » | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | | | | | |
| 登录 |
| 联系我 email: binaryluo(at)gmail.com
Blog信息 |
blog名称:二进制-虚心使人进步,骄傲使人落后。 日志总数:42 评论数量:370 留言数量:88 访问次数:640674 建立时间:2005年2月19日 |

| |
[网络编程技术]【原创】Winpcap学习:第四天(20060203)【上】 原创空间, 随笔, 读书笔记, 心得体会, 软件技术, 电脑与网络
binaryluo 发表于 2006/2/3 21:18:58 |
Winpcap提供(libpcap也提供)的一个强大特性是过滤引擎(filtering engine)。它提供了一个非常有效的接收网络流量的方法,并且它通常与Winpcap提供的抓包机制集成在一起。用于过滤数据包的函数是pcap_complie()和pcap_setfilter()。
pcap_complie()使用一个包含高级布尔表达式的字符串并且产生一个能被过滤引擎集成到数据包驱动中的低级字节码。
pcap_setfilter()把一个过滤器与核心驱动抓包会话关联起来。一旦pcap_setfilter()被调用,相关的过滤器将被应用到所有的来自网络的数据包上,并且所有的一致的数据包将被复制给应用程序。
抓包和过滤
经过前面几天的知识准备,现在我们将把前面的知识综合后应用于一个简单的实际应用程序。下面试验的目的是如何解析和解释被捕获的数据包的协议头。应用程序运行的结果是打印出一组我们的网络上的UDP通信数据。我们之所以选择解析和显示UDP协议是因为它比起其他协议(例如:TCP)更易于理解,对于初学者更适合。
试验代码:
#include <pcap.h>#include <remote-ext.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 pachet handler */void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data);
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[] = "ip and udp"; struct bpf_program fcode;
/* Retrieve the device list */ if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &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; d; d = d->next);
/* Open the adapter */ if ((adhandle = pcap_open(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 */ PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */ 1000, /* read timeout */ NULL, /* remote authentication */ errbuf /* error buffer */ )) == NULL) { fprintf(stderr, "\nUnable to open the adapter. %s is not supported by Winpcap\n"); /* Free the devices 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 devices 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 = 0xffffffff; }
/* complie 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 devices list */ pcap_freealldevs(alldevs); return -1; }
/* set the filter */ if (pcap_setfilter(adhandle, &fcode) < 0) { fprintf(stderr, "\nError setting the filter.\n"); /* Free the devices 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 1;}
/* Callback function invoked by libpcap for 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; u_short sport, dport;
/* convert the timestamp to readable format */ ltime = localtime(&header->ts.tv_sec); strftime(timestr, sizeof(timestr), "%H:%M:%S", ltime);
/* print timestamp and length of the packet */ printf("%s.%.6d len: %d ", timestr, header->ts.tv_usec, header->len);
/* retrieve the position of the ip header */ ih = (ip_header*)(pkt_data + 14); /* length of ethernet header */
/* retrieve 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 */ /*sport = ntohs(uh->sport); dport = ntohs(uh->dport);*/
/* print ip addresses and udp ports */ printf("%d.%d.%d.%d -> %d.%d.%d.%d\n", ih->saddr.byte1, ih->saddr.byte2, ih->saddr.byte3, ih->saddr.byte4, /*sport,*/ ih->daddr.byte1, ih->daddr.byte2, ih->daddr.byte3, ih->daddr.byte4 /*dport*/);}
函数1:
int pcap_datalink(pcap_t* p)
返回链路层上的一个适配器。返回链路层的类型,链路层的类型包括:
l DLT_NULL: BSD回路封装;链路层协议头是一个4字节的域,以主机字节顺序(host byte order),包含一个从socket.h来的PF_value。主机字节顺序(host byte order)是捕获数据包的机器的字节顺序,而PF_value是捕获数据包的机器的OS。如果一个读取一个文件,字节顺序和PF_value不一定是抓取文件的那些机器。
l DLT_EN10MB: 以太网(10Mb, 100Mb, 1000Mb, 或者更高)。
l DLT_IEEE802: IEEE802.5令牌环网。
l DLT_ARCNET:ARCNET。
l DLT_SLIP:SLIP。
l DLT_PPP:PPP;如果第一个字节是0xff或0x03,它是类HDLC帧上的PPP。
l DLT_FDDI:FDDI
l DLT_ATM_RFC1483:RFC1483LLC/SNAP ATM;数据包以IEEE802.2 LLC头开始。
l DLT_RAW:原始IP(raw IP);数据包以IP头开始。
l DLT_PPP_SERIAL:按照RFC1662,基于类HDLC帧的PPP,或者按照RFC1547的4.3.1,基于HDLC帧的Cisco PPP;前者的第一个字节是0xFF,后者的第一个字节是0x0F或0x8F。
l DLT_PPP_ETHER:按照RFC2516,PPPoE;数据包以PPPoE头开始。
l DLT_C_HDLC:按照RFC1547的4.3.1,基于HDLC帧的Cisco PPP。
l DLT_IEEE802_11:IEEE 802.11无线局域网。
l DLT_FRELAY:帧中继(Frame Relay)。
l DLT_LOOP:OpenBSD回路封装。
l DLT_LINUX_SLL:Linux抓包封装。
l DLT_LTALK:苹果的LocalTalk,数据包以AppleTalk LLAP头开始。
l DLT_PFLOG:OpenBSD pflog。
l DLT_PRISM_HEADER:后接802.11头的棱镜监视器模式(Prism monitor mode)信息。
l DLT_IP_OVER_FC:RFC2625 IP-over-Fiber 频道,以RFC2625中定义的Network_Header开始。
l DLT_SUNATM:SunATM设备。
l DLT_IEEE802_11_RADIO:后接802.11头的链路层信息。
l DLT_ARCNET_LINUX:没有异常帧的ARCNET。
l DLT_LINUX_IRDA:Linux-IrDA数据包,DLT_LINUX_SLL头后接IrLAP头。
|
回复:Winpcap学习:第四天(20060203)【上】 原创空间, 随笔, 读书笔记, 心得体会, 软件技术, 电脑与网络
buaa副班长(游客)发表评论于2006/5/7 14:27:13 |
以下引用icefire(游客)在2006-4-12 20:58:40的评论:大侠我用你的代码编译连接都成功不过执行EXE文件的时候,当选择了要监听的网卡时,就出现错误直接弹出WINDOWS报错了
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/* Jump to the selected adpater */ for (d = alldevs, i = 0; i < inum - 1; d = d->next, ++ i);
把楼主的那句换成这句,就没问题了.
|
回复:Winpcap学习:第四天(20060203)【上】 原创空间, 随笔, 读书笔记, 心得体会, 软件技术, 电脑与网络
feiyu_lili发表评论于2006/4/22 12:54:26 |
以下引用binaryluo在2006-4-22 12:12:33的评论:
以下引用feiyu_lili在2006-4-18 21:44:11的评论:第二个问题还是不明白"netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;"d是一个pcap_if指针;d->addresses是引用pcap_if结构体中的addresses成员;d->addresses->netmask是引用pcap_addr结构体中的netmask成员;(struct sockaddr_in *)(d->addresses->netmask)是将netmask强制转化为sockaddr_in结构体;((struct sockaddr_in)*)(d->addresses->netmask)->sin_addr是引用sockaddr_in结构体中的成员sin_addr,sin_addr是一个struct in_addr结构体变量;((struct sockaddr_in)*)(d->addresses->netmask)->sin_addr.S_un是引用in_addr结构体中的S_un成员;((struct sockaddr_in)*)(d->addresses->netmask)->sin_addr.S_un.S_addr是应用S_un中的成员S_addr,S_addr是u_long类型的变量。有点明白了,谢谢
|
回复:Winpcap学习:第四天(20060203)【上】 原创空间, 随笔, 读书笔记, 心得体会, 软件技术, 电脑与网络
binaryluo发表评论于2006/4/22 12:12:33 |
以下引用feiyu_lili在2006-4-18 21:44:11的评论:第二个问题还是不明白"netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;"
d是一个pcap_if指针;
d->addresses是引用pcap_if结构体中的addresses成员;
d->addresses->netmask是引用pcap_addr结构体中的netmask成员;
(struct sockaddr_in *)(d->addresses->netmask)是将netmask强制转化为sockaddr_in结构体;
((struct sockaddr_in)*)(d->addresses->netmask)->sin_addr是引用sockaddr_in结构体中的成员sin_addr,sin_addr是一个struct in_addr结构体变量;
((struct sockaddr_in)*)(d->addresses->netmask)->sin_addr.S_un是引用in_addr结构体中的S_un成员;
((struct sockaddr_in)*)(d->addresses->netmask)->sin_addr.S_un.S_addr是应用S_un中的成员S_addr,S_addr是u_long类型的变量。
|
回复:Winpcap学习:第四天(20060203)【上】 原创空间, 随笔, 读书笔记, 心得体会, 软件技术, 电脑与网络
binaryluo发表评论于2006/4/18 9:06:42 |
以下引用feiyu_lili在2006-4-13 15:51:52的评论:一:在这段代码
if (pcap_datalink(adhandle) != DLT_EN10MB) { fprintf(stderr, "\nThis program works only on Ethernet networks.\n"); /* Free the devices list */ pcap_freealldevs(alldevs); return -1; }
中“!= DLT_EN10MB”怎么又显示“This program works only on Ethernet networks.”是不是写错了呢?
二:在
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 = 0xffffffff; }
中① netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
我不知道为什么要这么做,而且也不知道明白这段的语法。
② 我也不知道为什么d->addresses = NULL就是c类网络?
三: uh = (udp_header*)((u_char*)ih + ip_len);不知道为什么要这样
四:pcap_setfilter被用来指定一个过滤器程序,首先我们设置过滤器为“ip and udp”没有看出哪段代码实现了这个功能 A1:DLT_EN10MB代表的是以太网,意思就是如果你选择的网卡不是以太网类型的网卡(或者说网络不是以太网),那么就提示错误,错误信息就是“This program works only on Ethernet networks.”。
A2:看下pcap_if的定义也许对你有帮助:
nstruct pcap_if {
struct pcap_if *next;
char *name;
char *description;
struct pcap_addr *addresses;
u_int flags;
};
A3:在c里面,对于一段连续的缓冲区,可以用强制转化的方式将其转化成一定的结构体类型。不过你要保证这段缓冲区中保存的数据确实跟你的结构体中的各个字段是对应的,不然访问结构体的时候可能会出问题。
A4:pcap_setfilter我也没研究过,你可以参考下winpcap的文档看看。
|
|