« | 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 访问次数:640808 建立时间: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)【上】 原创空间, 随笔, 读书笔记, 心得体会, 软件技术, 电脑与网络
feiyu_lili发表评论于2006/3/2 15:39:13 |
终于发现运行出错的原因了!
/* Jump to the selected adapter */ for (d = alldevs; d; d = d->next);
的 for (d = alldevs; d; d = d->next);这句话的指针出错了,指向了下一个了。我把它改成 for (d = alldevs, i = 0; i < inum - 1; d = d->next, ++ i);就对了。我就不知道你怎么不会出错呢
|
第四天(20060203)【上】再问 原创空间, 随笔, 读书笔记, 心得体会, 软件技术, 电脑与网络
feiyu_lili发表评论于2006/3/2 13:20:32 |
stderr是系统自动定义的标准出错输出(也就是从终端输出)。那就是说把Error in pcap_findalldevs加errbuf的内容输出到终端上,而fprintf是要放入文件的阿?那为什么这里不用printf而是要用fprintf,难道说fprintf也能用来只输出到屏幕上吗?
|
回复:Winpcap学习:第四天(20060203)【上】 原创空间, 随笔, 读书笔记, 心得体会, 软件技术, 电脑与网络
binaryluo发表评论于2006/3/1 21:52:17 |
以下引用feiyu_lili在2006-3-1 15:00:59的评论:fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
我搞晕了,int fprintf(FILE *stream, char *format, <variable-list>); 中的不是要把内容写进FILE *stream里面吗?这个FILE *stream是一个文件还是一个缓冲区。如果是文件的话是已经存在的文件还是要创建的文件,还是都可以?
我不知道这里的stderr是一个文件,还是一个一缓冲区。是系统自带的还是用户自行定义的啊?
我第四天的代码编译能通过当他运行到if ((adhandle = pcap_open(d->name, /*name of the device */ 65536, PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */ 1000, /* read timeout */ NULL, /* remote authentication */ errbuf /* error buffer */ )) == NULL)就会出错,我不知道是不是和stderr有关。如果stderr是文件它有没有固定放置的位置阿?fprintf是C中的一个输入输出函数。它是把<variable-list>的值以format指定的格式输出到stream指定的文件中。stream所指的文件是否需要存在你可以自己写一段代码试验下。
stderr是系统自动定义的标准出错输出(也就是从终端输出),另外还有stdin,stdout也是系统定义的,分别指向终端输入、终端输出。
这段代码我是编译过的,应该没有问题。可能是你编译器的问题。
|
回复:Winpcap学习:第四天(20060203)【上】 原创空间, 随笔, 读书笔记, 心得体会, 软件技术, 电脑与网络
feiyu_lili发表评论于2006/3/1 15:00:59 |
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
我搞晕了,int fprintf(FILE *stream, char *format, <variable-list>); 中的不是要把内容写进FILE *stream里面吗?这个FILE *stream是一个文件还是一个缓冲区。如果是文件的话是已经存在的文件还是要创建的文件,还是都可以?
我不知道这里的stderr是一个文件,还是一个一缓冲区。是系统自带的还是用户自行定义的啊?
我第四天的代码编译能通过当他运行到if ((adhandle = pcap_open(d->name, /*name of the device */ 65536, PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */ 1000, /* read timeout */ NULL, /* remote authentication */ errbuf /* error buffer */ )) == NULL)就会出错,我不知道是不是和stderr有关。如果stderr是文件它有没有固定放置的位置阿?
|
回复:Winpcap学习:第四天(20060203)【上】 原创空间, 随笔, 读书笔记, 心得体会, 软件技术, 电脑与网络
binaryluo发表评论于2006/2/28 23:51:58 |
以下引用feiyu_lili在2006-2-27 14:23:17的评论:我不太明白这段代码的目的:
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;
}
这段代码的作用是什么,放在这里的目的我不太清楚,你能给我解释一下吗?就
是将pcap_addr结构体中的netmask字段赋值给netmask变量。if判断表示如果网卡d的addresses不为NULL,那么就将其中
的netmask转化成相应格式的数据并赋值给netmask;否则,就将netmask变量赋值为ffffffff。
|
能不能帮我解释一下,这一下段代码的作用啊? 原创空间, 随笔, 读书笔记, 心得体会, 软件技术, 电脑与网络
feiyu_lili发表评论于2006/2/27 14:23:17 |
我不太明白这段代码的目的:
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;
}
这段代码的作用是什么,放在这里的目的我不太清楚,你能给我解释一下吗?
|
|