大家好,欢迎来到IT知识分享网。
介绍:
libpcap是一个C语言库,libpcap的英文意思是 Packet Capture Library,即数据包捕获函数库,其功能是通过网卡抓取网络以太网中的数据包。这个库为不同的平台提供了一致的c函数编程接口,在安装了libpcap 的平台上,以 libpcap 为接口写的程序、应用,能够自由地跨平台使用。它支持多种操作系统。libpcap源代码由20多个C文件构成,但在Linux系统下并不是所有文件都用到。可以通过查看命令make的输出了解实际所用的文件。
主要作用:
- 捕获各种数据包,例如:网络流量统计
- 过滤网络数据包,例如:过滤掉本地上的一些数据,类似防火墙
- 分析网络数据包,例如:分析网络协议,数据的采集
- 存储网络数据包,例如:保存捕获的数据以为将来进行分析
libpcap中使用的函数
- 获取主机网卡信息
函数名称:char *pcap_lookupdev(char *errbuf) 函数功能:用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名指针。 参数说明:如果函数出错,则返回NULL,同时errbuf中存放相关的错误消息。
- 获取一个包捕捉句柄
函数名称:pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) 函数功能:获得用于捕获网络数据包的数据包捕获描述字, 类似文件操作函数使用的文件句柄。 参数说明:device 参数为指定打开的网络设备名。snaplen参数定义捕获数据的最大字节数。promisc指定是否将网络接口置于混杂模式。to_ms参数指*定超时时 间(毫秒)。ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消息。
- 打开已保存的数据包文件
函数名称:pcap_t *pcap_open_offline(char *fname, char *ebuf) 函数功能:打开一个 tcpdump/libpcap 格式的存储文件,来读取数据包。 参数说明:fname参数指定打开的文件名。该文件中的数据格式与tcpdump和tcpslice兼容。”-“为标准输入。ebuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传递错误消息。
- 打开用于数据包写入的文件
函数名称:pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname) 函数功能:打开用于保存捕获数据包的文件,用于写入。 参数说明:fname 参数为”-“时表示标准输出。出错时返回NULL。p参数为调用pcap_open_offline()或pcap_open_live()函数后返回的 pcap结构指针。fname参数指定打开的文件名。如果返回NULL,则可调用pcap_geterr()函数获取错误消息。
- 获取网卡信息
函数名称:int pcap_lookupnet(char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf) 函数功能:获得指定网络设备的网络号和掩码。 参数说明:netp参数和maskp参数都是bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相关的错误消息。
- 捕获并处理数据包
函数名称:int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 函数功能: 捕捉报文以及分发报文到预先指定好的处理函数(回调函数)。 参数说明:cnt 参数指定函数返回前所处理数据包的最大值。cnt=-1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有数据包,直到产生以下错误之一:读取 到EOF;超时读取。callback参数指定一个带有三个参数的回调函数,这三个参数为:一个从pcap_dispatch()函数传递过来的 u_char指针,一个pcap_pkthdr结构的指针,和一个数据包大小的u_char指针。如果成功则返回读取到的字节数。读取到EOF时则返回零 值。出错时则返回-1,此时可调用pcap_perror()或pcap_geterr()函数获取错误消息。
- 循环捕获并处理数据包
函数名称:int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 函数功能: 功能基本与pcap_dispatch()函数相同,只不过此函数在cnt个数据包被处理或出现错误时才返回,但读取超时不会返回。而如果为 pcap_open_live()函数指定了一个非零值的超时设置,然后调用pcap_dispatch()函数,则当超时发生时 pcap_dispatch()函数会返回。cnt参数为负值时pcap_loop()函数将始终循环运行,除非出现错误。
- 处理已打开数据包文件
函数名称:void pcap_dump(u_char *user, struct pcap_pkthdr *h,u_char *sp) 函数功能:向调用pcap_dump_open()函数打开的文件输出一个数据包。该函数可作为pcap_dispatch()函数的回调函数。
- 编译过滤程序
函数名称:int pcap_compile(pcap_t *p, struct bpf_program *fp,char *str, int optimize, bpf_u_int32 netmask) 函数功能:将str参数指定的字符串编译到过滤程序中。 参数说明:fp是一个bpf_program结构的指针,在pcap_compile()函数中被赋值。optimize参数控制结果代码的优化。netmask参数指定本地网络的网络掩码。
- 设置过滤程序
函数名称:int pcap_setfilter(pcap_t *p, struct bpf_program *fp) 函数功能:指定一个过滤程序。 参数说明:fp参数是bpf_program结构指针,通常取自pcap_compile()函数调用。出错时返回-1;成功时返回0。
- 捕获并处理数据包
函数名称:u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h) 函数功能:返回指向下一个数据包的u_char指针。
- 返回数据链路层类型
函数名称:int pcap_datalink(pcap_t *p) 函数功能:返回数据链路层类型,例如DLT_EN10MB。
- snapshot参数值
函数名称:int pcap_snapshot(pcap_t *p) 函数功能:返回pcap_open_live被调用后的snapshot参数值。
- 字节顺序
函数名称:int pcap_is_swapped(pcap_t *p) 函数功能:返回当前系统主机字节与被打开文件的字节顺序是否不同。
- 主版本号
函数名称:int pcap_major_version(pcap_t *p) 函数功能:返回写入被打开文件所使用的pcap函数的主版本号。
- 辅版本号
函数名称:int pcap_minor_version(pcap_t *p) 函数功能:返回写入被打开文件所使用的pcap函数的辅版本号。
- 数据包统计
函数名称:int pcap_stats(pcap_t *p, struct pcap_stat *ps) 函数功能:向pcap_stat结构赋值。成功时返回0。这些数值包括了从开始捕获数据以来至今共捕获到的数据包统计。如果出错或不支持数据包统计,则返回-1,且可调用pcap_perror()或pcap_geterr()函数来获取错误消息。
- 获取文件名
函数名称:FILE *pcap_file(pcap_t *p) 函数功能:返回被打开文件的文件名。
- 获取文件描述字号码
函数名称:int pcap_fileno(pcap_t *p) 函数功能:返回被打开文件的文件描述字号码。
- 捕获错误消息
函数名称:void pcap_perror(pcap_t *p, char *prefix) 函数功能:在标准输出设备上显示最后一个pcap库错误消息。以prefix参数指定的字符串为消息头。
- 捕获错误消息
函数名称:char *pcap_geterr(pcap_t *p) 函数功能:返回最后一个pcap库错误消息。
- 捕获错误消息
函数名称:char *pcap_strerror(int error) 函数功能:如果strerror()函数不可用,则可调用pcap_strerror函数替代。
- 关闭包捕捉句柄
函数名称:void pcap_close(pcap_t *p) 函数功能:关闭p参数相应的文件,并释放资源。
- 关闭文件
函数名称:void pcap_dump_close(pcap_dumper_t *p) 函数功能:关闭相应的被打开文件。
数据结构
本部分为pcap的关键数据结构:
- pcap_if:typedef struct pcap_if pcap_if_t 保存网卡基本信息的类型。通常用指针来使用,pcap_if_t *alldevs
struct pcap_if {
struct pcap_if *next; //指向下一个网卡 char *name; //网卡的标识符,唯一识别一个网卡 char *description; //用来描述网卡 struct pcap_addr*address; //网卡的地址,包括IP地址,网络掩码,广播地址等,类型中的成员变量在后面会写到 bpf_u_int32 flags; //接口标志 }
- pcap_pkthdr:数据报头部
struct pcap_pkthdr {
struct timeval ts; //获得packet的时间,ts是一个结构struct timeval,它有两个部分,第一部分是1900开始以来的秒数,第二部分是当前秒之后的毫秒数 bpf_u_int32 caplen; //抓取到的packet长度 bpf_u_int32 len; //packet的真实长度 };
使用流程
char * device; /* 用来捕获数据包的网络接口的名称 */ pcap_t * p; /* 捕获数据包句柄,最重要的数据结构 */ struct bpf_program fcode; /* BPF 过滤代码结构 */ /* 第一步:查找可以捕获数据包的设备 */ device = pcap_lookupdev(errbuf); /* 第二步:创建捕获句柄,准备进行捕获 */ p = pcap_open_live(device, 8000, 1, 500, errbuf); /* 第三步:如果用户设置了过滤条件,则编译和安装过滤代码 */ pcap_compile(p, &fcode, filter_string, 0, netmask); pcap_setfilter(p, &fcode); /* 第四步:进入(死)循环,反复捕获数据包 */ for( ; ; ) {
while((ptr = (char *)(pcap_next(p, &hdr))) == NULL); /* 第五步:对捕获的数据进行类型转换,转化成以太数据包类型 */ eth = (struct libnet_ethernet_hdr *)ptr; /* 第六步:对以太头部进行分析,判断所包含的数据包类型,做进一步的处理 */ if(eth->ether_type == ntohs(ETHERTYPE_IP)) ………… if(eth->ether_type == ntohs(ETHERTYPE_ARP)) ………… } /* 最后一步:关闭捕获句柄,一个简单技巧是在程序初始化时增加信号处理函数, 以便在程序退出前执行本条代码 */ pcap_close(p);
例子
#include <linux/if_ether.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <netinet/udp.h> #define APP_NAME "sniffex" #define APP_DESC "Sniffer example using libpcap" #define APP_COPYRIGHT "Copyright (c) 2021" #define APP_DISCLAIMER "THERE IS ABSOLUTELY NO WARRANTY FOR THIS PROGRAM." #include <pcap.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> /* default snap length (maximum bytes per packet to capture) */ #define SNAP_LEN 1518 /* ethernet headers are always exactly 14 bytes [1] */ #define SIZE_ETHERNET 14 /* Ethernet addresses are 6 bytes */ #define ETHER_ADDR_LEN 6 /* Ethernet header */ struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN]; /* destination host address */ u_char ether_shost[ETHER_ADDR_LEN]; /* source host address */ u_short ether_type; /* IP? ARP? RARP? etc */ }; /* IP header */ struct sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */ u_char ip_tos; /* type of service */ u_short ip_len; /* total length */ u_short ip_id; /* identification */ u_short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_char ip_ttl; /* time to live */ u_char ip_p; /* protocol */ u_short ip_sum; /* checksum */ struct in_addr ip_src, ip_dst; /* source and dest address */ }; #define IP_HL( ip ) ( ( (ip)->ip_vhl) & 0x0f) #define IP_V( ip ) ( ( (ip)->ip_vhl) >> 4) /* TCP header */ typedef u_int tcp_seq; struct sniff_tcp {
u_short th_sport; /* source port */ u_short th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ u_char th_offx2; /* data offset, rsvd */ #define TH_OFF( th ) ( ( (th)->th_offx2 & 0xf0) >> 4) u_char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 #define TH_FLAGS (TH_FIN | TH_SYN | TH_RST | TH_ACK | TH_URG | TH_ECE | TH_CWR) u_short th_win; /* window */ u_short th_sum; /* checksum */ u_short th_urp; /* urgent pointer */ }; void got_packet( u_char *args, const struct pcap_pkthdr *header, const u_char *packet ); void print_payload( const u_char *payload, int len ); void print_hex_ascii_line( const u_char *payload, int len, int offset ); void print_app_banner( void ); void print_app_usage( void ); /* * app name/banner */ void print_app_banner( void ) {
printf( "%s - %s\n", APP_NAME, APP_DESC ); printf( "%s\n", APP_COPYRIGHT ); printf( "%s\n", APP_DISCLAIMER ); printf( "\n" ); return; } /* * print help text */ void print_app_usage( void ) {
printf( "Usage: %s [interface]\n", APP_NAME ); printf( "\n" ); printf( "Options:\n" ); printf( " interface Listen on <interface> for packets.\n" ); printf( "\n" ); return; } /* * print data in rows of 16 bytes: offset hex ascii * * 00000 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a GET / HTTP/1.1.. */ void print_hex_ascii_line( const u_char *payload, int len, int offset ) {
int i; int gap; const u_char *ch; /* offset */ printf( "%05d ", offset ); /* hex */ ch = payload; for ( i = 0; i < len; i++ ) {
printf( "%02x ", *ch ); ch++; /* print extra space after 8th byte for visual aid */ if ( i == 7 ) printf( " " ); } /* print space to handle line less than 8 bytes */ if ( len < 8 ) printf( " " ); /* fill hex gap with spaces if not full line */ if ( len < 16 ) {
gap = 16 - len; for ( i = 0; i < gap; i++ ) {
printf( " " ); } } printf( " " ); /* ascii (if printable) */ ch = payload; for ( i = 0; i < len; i++ ) {
if ( isprint( *ch ) ) printf( "%c", *ch ); else printf( "." ); ch++; } printf( "\n" ); return; } void print_hex_ascii_lines( const u_char *payload, int len, int offset, int line_width ) {
int len_rem = len; int line_len; for (;; ) {
/* compute current line length */ line_len = line_width % len_rem; /* print line */ print_hex_ascii_line( payload, line_len, offset ); /* compute total remaining */ len_rem = len_rem - line_len; /* shift pointer to remaining bytes to print */ payload = payload + line_len; /* add offset */ offset = offset + line_width; /* check if we have line width chars or less */ if ( len_rem <= line_width ) {
/* print last line and get out */ print_hex_ascii_line( payload, len_rem, offset ); break; } } } /* * print packet payload data (avoid printing binary data) */ void print_payload( const u_char *payload, int len ) {
int line_width = 16; /* number of bytes per line */ int offset = 0; /* zero-based offset counter */ const u_char *ch = payload; if ( len <= 0 ) return; printf( "========================================================================\n" ); /* data fits on one line */ if ( len <= line_width ) {
print_hex_ascii_line( ch, len, offset ); return; }else {
/* data spans multiple lines */ print_hex_ascii_lines( ch, len, offset, line_width ); return; } } /* * dissect/print packet */ void got_packet( u_char *args, const struct pcap_pkthdr *header, const u_char *packet ) {
static int count = 1; /* packet counter */ /* declare pointers to packet headers */ const struct sniff_ethernet *ethernet; /* The ethernet header [1] */ const struct sniff_ip *ip; /* The IP header */ const struct sniff_tcp *tcp; /* The TCP header */ const u_char *payload; /* Packet payload */ int size_ip; int size_tcp; int size_payload; printf( "\nPacket number %d:\n", count ); count++; /* define ethernet header */ ethernet = (struct sniff_ethernet *) (packet); /* define/compute ip header offset */ ip = (struct sniff_ip *) (packet + SIZE_ETHERNET); size_ip = IP_HL( ip ) * 4; if ( size_ip < 20 ) {
printf( " * Invalid IP header length: %u bytes\n", size_ip ); return; } /* print source and destination IP addresses */ printf( " From: %s\n", inet_ntoa( ip->ip_src ) ); printf( " To: %s\n", inet_ntoa( ip->ip_dst ) ); /* determine protocol */ switch ( ip->ip_p ) {
case IPPROTO_TCP: printf( " Protocol: TCP\n" ); break; case IPPROTO_UDP: printf( " Protocol: UDP\n" ); return; case IPPROTO_ICMP: printf( " Protocol: ICMP\n" ); return; case IPPROTO_IP: printf( " Protocol: IP\n" ); return; default: printf( " Protocol: unknown\n" ); return; } /* * OK, this packet is TCP. */ /* define/compute tcp header offset */ tcp = (struct sniff_tcp *) (packet + SIZE_ETHERNET + size_ip); size_tcp = TH_OFF( tcp ) * 4; if ( size_tcp < 20 ) {
printf( " * Invalid TCP header length: %u bytes\n", size_tcp ); return; } printf( " Src port: %d\n", ntohs( tcp->th_sport ) ); printf( " Dst port: %d\n", ntohs( tcp->th_dport ) ); /* define/compute tcp payload (segment) offset */ payload = (u_char *) (packet + SIZE_ETHERNET + size_ip + size_tcp); /* compute tcp payload (segment) size */ size_payload = ntohs( ip->ip_len ) - (size_ip + size_tcp); /* * Print payload data; it might be binary, so don't just * treat it as a string. */ if ( size_payload > 0 ) {
printf( " Payload (%d bytes):\n", size_payload ); print_payload( payload, size_payload ); } return; } int main( int argc, char **argv ) {
char *dev = NULL; /* capture device name */ char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */ pcap_t *handle; /* packet capture handle */ char filter_exp[] = "tcp"; /* filter expression [3] */ struct bpf_program fp; /* compiled filter program (expression) */ bpf_u_int32 mask; /* subnet mask */ bpf_u_int32 net; /* ip */ int num_packets = 0; /* number of packets to capture */ print_app_banner(); /* check for capture device name on command-line */ if ( argc == 2 ) {
dev = argv[1]; } else if ( argc > 2 ) {
fprintf( stderr, "error: unrecognized command-line options\n\n" ); print_app_usage(); exit( EXIT_FAILURE ); } else{
/* find a capture device if not specified on command-line */ dev = pcap_lookupdev( errbuf ); if ( dev == NULL ) {
fprintf( stderr, "Couldn't find default device: %s\n", errbuf ); exit( EXIT_FAILURE ); } } /* get network number and mask associated with capture device */ if ( pcap_lookupnet( dev, &net, &mask, errbuf ) == -1 ) {
fprintf( stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf ); net = 0; mask = 0; } /* print capture info */ printf( "Device: %s\n", dev ); printf( "Number of packets: %d\n", num_packets ); printf( "Filter expression: %s\n", filter_exp ); /* open capture device */ handle = pcap_open_live( dev, SNAP_LEN, 1, 1000, errbuf ); if ( handle == NULL ) {
fprintf( stderr, "Couldn't open device %s: %s\n", dev, errbuf ); exit( EXIT_FAILURE ); } /* make sure we're capturing on an Ethernet device [2] */ if ( pcap_datalink( handle ) != DLT_EN10MB ) {
fprintf( stderr, "%s is not an Ethernet\n", dev ); exit( EXIT_FAILURE ); } /* compile the filter expression */ if ( pcap_compile( handle, &fp, filter_exp, 0, net ) == -1 ) {
fprintf( stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr( handle ) ); exit( EXIT_FAILURE ); } /* apply the compiled filter */ if ( pcap_setfilter( handle, &fp ) == -1 ) {
fprintf( stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr( handle ) ); exit( EXIT_FAILURE ); } /* now we can set our callback function */ pcap_loop( handle, num_packets, got_packet, NULL ); /* cleanup */ pcap_freecode( &fp ); pcap_close( handle ); printf( "\nCapture complete.\n" ); return(0); }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/116703.html