使用libpcap库用c编写网络嗅探器
定义:网络嗅探也叫网络侦听,指的是使用特定的网络协议来分解捕获到的数据包,并根据对应的网络协议识别对应数据片断
作用:管理员可以用来监听网络的流量情况
开发网络应用的程序员可以监视程序的网络情况
黑客可以用来刺探网络情报
网卡的四种接收数据模式
①广播模式:该模式下的网卡能够接收网络中的广播信息;
②组播模式:设置在该模式下的网卡能够接收组播数据;
③直接模式:在这种模式下,只有目的网卡才能接收该数据;
④混杂模式:在这种模式下的网卡能够接收一切通过它的数据,而不管该数据是否是传给它的。
通常,网卡的缺省配置是支持前三种模式。 为了监听网络上的流量,必须设置为混杂模式
libpcap简介
Packet Capture library,即数据包捕获函数库。该库提供的C函数接口可用于需要捕获经过网络接口(只要经过该接口,目标地址不一定为本机)数据包的系统开发上。
程序流程
Libpcap API介绍
pcap_lookupdev( )
char *pcap_lookupdev(char *errbuf)
用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络
设备名指针。如果函数出错,则返回NULL,同时errbuf中存放相关的
错误消息。
pcap_lookupnet( )
int pcap_lookupnet(char *device, bpf_u_int32 *netp,bpf_u_int32 *maskp, char *errbuf)
获得指定网络设备的网络号和掩码。netp参数和maskp参数都是
bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相
关的错误消息。
pcap_open_live( )
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_setfilter( )
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
指定一个过滤程序。fp参数是bpf_program结构指针,通常取自
pcap_compile()函数调用。出错时返回-1;成功时返回0。
pcap_compile( )
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参数指定本地网络的网络掩码。
pcap_next( )
u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)
返回指向下一个数据包的u_char指针。
pcap_dispatch( )
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()函数获取错误消息。
pcap_loop( )
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()函数将始终循环运行,除非出现错误。
过滤规则
规则是由标识和修饰符与逻辑符组成的。
修饰符
确定方向的修饰符:src/dst;
确定类型的修饰符:host/net/port;
确定协议的修饰符:IP/TCP/UDP/ARP;
逻辑符 and 或 && not 或! or 或 ||
打印程序思路
回调函数:回调函数,就是由你自己写的。你需要调用另外一个函数,而这个函数的其中一个参数,就是你的这个回调函数名。这样,系统在必要的时候,就会调用你写的回调函数,这样你就可以在回调函数里完成你要做的事。
程序能实现的功能:
使用Libpcap库捕获局域网中的IP包,要求:
打印数据包的源与目的物理地址;
打印源IP与目的IP地址;
打印出上层协议类型;
如果上层协议为TCP或UDP协议,打印目的与源端口信息;
如果上层协议为TCP或UDP协议,将数据以16进制与ASCII的两种方式同时打印出来,不可打印字符以‘.’代替;
00000 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a GET / HTTP/1.1..
示例代码
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/time.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/in_systm.h>
#include<netinet/ip.h>
#include<netinet/if_ether.h>
#include<netinet/tcp.h>
#include<netinet/udp.h>
#include<pcap.h>
#include<netdb.h>
#include<time.h>
#include<sys/time.h>
#include<stdlib.h>
#include<ctype.h>//打印16进制和ascii
void print(u_char*payload,int len,int offset,int maxlen)
{printf("%.5d ",offset); //打印偏移量(宽度为5)int max=maxlen; //数据包的有效载荷和长度int i;for(i=0;i<16;i++) //打印16个字节的16进制payload{if((len-i)>0) //还没打完{printf("%.2x ",payload[max-(len-i)]);}else //已打完,最后一个后面有空格{printf(" ");}}printf(" ");for(i=0;i<16;i++) //打印16个字节的asciipayload{if(isprint(payload[max-(len-i)])) //为可打印字符{printf("%c",payload[max-(len-i)]);}else //打印不出来的用"."表示{printf(".");}}
}//打印数据包
void print_data(u_char *payload,int len)
{int line_width=16; //一行16个字节int len_rem=len; //剩余长度int maxlen=len; //数据包的有效载荷和长度int offset=0; //偏移量while(1){if(len_rem<line_width) //最后一次打印{if(len_rem==0) //已打印完break;else { //还没打印完print(payload,len_rem,offset,maxlen); //调用print函数,传入payload地址、剩余长度、偏移量和数据包的有效载荷和长度offset=offset+len_rem; //偏移量后移printf("\n");break;}}else //不是最后一次打印{ print(payload,len_rem,offset,maxlen); //调用print函数,传入payload地址、剩余长度、偏移量和数据包的有效载荷和长度offset=offset+16; //偏移量后移(由于非最后一次打印,所以固定打16个字节 - 偏移量后移16个字节)printf("\n");}len_rem=len_rem-line_width; //剩余长度减少}
}//打印mac地址
void print_mac(u_char* macadd){int i;for(i=0;i<5;i++){printf("%.2x:",macadd[i]); //16进制,两位宽度}printf("%.2x",macadd[i]);
}//打印ip地址
void print_ip(u_char* ipadd){int i;for(i=0;i<3;++i){printf("%d.",ipadd[i]);}printf("%d",ipadd[i]);
}
pcap_handler callback(u_char *user,const struct pcap_pkthdr *h,const u_char *p)//一个从pcap_dispatch()函数传递过来的u_char指针,一个pcap_pkthdr结构的指针,和一个数据包大小的u_char指针。
{struct ether_header *eth; //以太网帧头部static long int packet_num=0; //当前包编号(包数量),为静态分配struct ether_arp *arppkt; //arp帧头部(以arp包的源以太地址是RTSG, 目标地址是全以太网段)struct ip *iph; //IP包头部struct icmphdr *icmp; //ICMP包头部struct tcphdr *tcph; //tcp头部struct udphdr *udph; //udp头部int m;char *buf;printf("...............................................................................\n");printf("\n"); printf("Recieved at ----- %s",ctime((const time_t*)&(h->ts).tv_sec)); //显示时间printf("Packet number:%d\n",++packet_num); //显示当前包编号printf("Packet length:%d\n",h->len); //显示包长度(脱机长度)int i;eth=(struct ether_header *)p;printf("Source Mac Address: "); print_mac(eth->ether_shost); //调用print_mac函数,传入源主机的mac地址printf("\n");printf("Destination Mac Address:");print_mac(eth->ether_dhost); //调用print_mac函数,传入目的主机的mac地址printf("\n");//判断网络层协议unsigned int typeno;typeno=ntohs(eth->ether_type);printf("network layer protocal:");switch(typeno){case ETHERTYPE_IP:printf("IPV4\n");break;case ETHERTYPE_PUP:printf("PUP\n");break;case ETHERTYPE_ARP:printf("ARP\n");break;default:printf("unknown network layer types\n");} if(typeno==ETHERTYPE_IP) //为IP协议{iph=(struct ip*)(p+sizeof(struct ether_header)); //获得ip包头部地址printf("Source Ip Address:");print_ip((u_char*)&(iph->ip_src)); //调用print_ip函数,传入源主机的ip地址printf("\n");printf("Destination Ip address:");print_ip((u_char *)&(iph->ip_dst)); //调用print_ip函数,传入目的主机的ip地址printf("\n");//判断传输层协议printf("Transport layer protocal:");if(iph->ip_p==1){printf("ICMP\n");}else if(iph->ip_p==2){printf("IGMP\n");} else if(iph->ip_p==6) //为TCP协议{printf("TCP\n");tcph=(struct tcphdr*)(p+sizeof(struct ether_header)+sizeof(struct ip)); //获得tcp头部地址printf("destport :%d\n",ntohs(tcph->dest)); //打印目的端口号printf("sourport:%d\n",ntohs(tcph->source)); //打印源端口号printf("Payload");printf("(%d bytes): \n",h->len); print_data(p,h->len);}else if(iph->ip_p==17) //为UDP协议{printf("UDP\n");udph=(struct udphdr*)(p+sizeof(struct ether_header)+sizeof(struct ip)); //获得udp头部地址printf("dest port:%d\n",ntohs(udph->dest)); //打印目的端口号printf("source port:%d\n",ntohs(udph->source)); //打印源端口号printf("Payload");printf("(%d bytes): \n",h->len); print_data(p,h->len); }else {printf("unknown protocol\n");}}
}
int main(int argc,char** argv)
{char * dev;char *net_c; //字符串形式网络地址,用于打印输出char *mask_c; //字符串形式的网络掩码地址char errbuf[PCAP_ERRBUF_SIZE];struct in_addr addr;struct pcap_pkthdr header;//libpcap包头结构,包含捕获时间,捕获长度与数据包实际长度const u_char *packet;//捕获到的实际数据包内容pcap_t *handle;//libpcap设备描述符号struct bpf_program fp;//过滤器char filter_exp[] = "tcp port 80";//实际的过滤规则struct tm *now_tm;time_t now;bpf_u_int32 net = 0;bpf_u_int32 mask = 0;dev = NULL;memset(errbuf,0,PCAP_ERRBUF_SIZE);//pcap_lookupdev 返回设备名称 dev = pcap_lookupdev(errbuf);if (dev == NULL) {fprintf(stderr, "Couldn't find default device: %s\n", errbuf);return(2);}printf("Device: %s\n", dev);//lookupnet获得指定网络设备的网络号和掩码 if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {fprintf(stderr, "Can't get netmask for device %s\n", dev);net_c = 0;mask_c = 0;return(2);}//转换网络地址 addr.s_addr = net;net_c = inet_ntoa(addr);printf("Net: %s\n", net_c);addr.s_addr = mask;mask_c = inet_ntoa(addr);printf("Mask: %s\n",mask_c);printf("==================================================\n");//pcap_open_live打开设备文件准备读取数据 handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);if (handle == NULL) {fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);return(2);}//编译过滤规则 if (pcap_compile(handle, &fp, "ip", 1, mask) == -1) {fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));return(2);}//设置过滤规则 if (pcap_setfilter(handle, &fp) == -1) {fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));return(2);}//捕获数据包//pcap_loop循环抓取网络数据报文 采用回调函数实现 if(pcap_loop(handle,-1,callback,NULL)<0){(void)fprintf(stderr,"pcap_loop:%s\n",pcap_geterr(handle));exit(0);}//关闭库 pcap_close(handle);return(0);
}
使用libpcap库用c编写网络嗅探器相关推荐
- socket编程之DEV C++配置winpcap开发环境并编写网络嗅探器sniffer
欢迎关注我的个人博客:www.zuzhiang.cn 期末计算机网络课程设计让做一个网络嗅探器,要求可以检测和选择网卡,并打开到混杂模式,监听局域网中的所有数据包并解析出所用网络协议以及首部各个字段的 ...
- python//网络嗅探器
python编写网络嗅探器 网络嗅探器 网络嗅探器又称网络监听器,简称Sniffer子系统,放置于网络节点上,对网络中的数据帧进行捕获的一种被动监听手段,是一种常用的收集有用信息的方法. 嗅探器原理 ...
- 基于Python实现的网络嗅探器【100010784】
基于C语言的网络嗅探器 一.引言 1.1 编写目的 通过编写网络嗅探器,可以提高编程能力,加深对网络协议的理解,培养团队协作能力,完成课程项目 二.功能概述 2.1 节 功能模块命名原则 使用英文进行 ...
- 简单网络嗅探器编写--------java
功能设计: 在设计之初,本设计被期望实现下面功能: ①有一个对用户友好的操作界面,使用户对软件上手快和操作简单: ②能对网卡进行选择,选择用户自己需要进行监听抓包的网卡: ③能够侦听所有进出本主机的数 ...
- (网络实验)基于Jnetpcap的网络嗅探器(抓包程序)设计与实现
基于Jnetpcap的网络嗅探器(抓包程序)设计与实现 作者:程哥哥 学号:xxxx 引言: 此程序是由自己编写的个人网络嗅探器,相当于著名的包捕获软件Wireshark的简化版,界面参考至别人的博客 ...
- 基于QT的网络嗅探器实现(网络安全课程设计)
在这学期的网络安全课程设计中,我们需要自己实现一个基于WinPcap编程接口的网络嗅探器,历时两周完成,主要参考资料: 1.WinPcap 中文技术文档(http://www.ferrisxu.com ...
- 计算机网络课程设计网络嗅探器
一.题目 网络嗅探器 二.环境 Win10 codeblocks 三.代码实现 WinPcap的安装教程 winpcap要在CB中使用,还需配置一些.h文件,请自行百度,百度上有. 请熟悉教材I ...
- 网络嗅探器的设计(2)
参考https://blog.csdn.net/qq_34838643/article/details/78891127 标题 使用java进行实现网络嗅探器. 实验环境 Win10+Eclipse+ ...
- C语言实现网络嗅探器
这里介绍一个用C语言和网络数据包和分析开发工具libpcap及winpcap实现的简易网络Sniffer. 2网络嗅探器程序实现 在c环境下编程,源码如下: #include #include //必 ...
最新文章
- (C#)AJAX post方式传值
- C# 实现FTP上传与下载
- 秒杀应用的MySQL数据库优化
- 2013 javaB1 世纪末的日期
- 如何让tomcat只支持ipv4
- [第一财经周刊] 疯狂的团购
- shell编程基础之根据输入进行相应的操作
- 关于个人防火墙的真相
- HTML第二课——css
- .NET 中使用 SQlite 数据库_1.新数据库的创建
- I/O接口标准(1):LVTTL、LVCMOS、SSTL、HSTL
- javassm框架项目实例_面试官:小伙子,给我说一下spring框架吧
- 太空战机c语言源码,funcode编写的太空战机
- 计算机硬件acc作用,累加器A的主要作用是什么_一文解析累加器a和acc的区别
- Win11双系统引导怎么设置?Win11双系统引导设置方法介绍
- Excel表格文本/数字/科学计数法的格式转换问题
- STM32低功耗模式
- 屈原与司马迁的对话--理想主义与辩证法
- Oracle:错误码ORA-28040 的坑
- 学习如何使用php创建一张空白的图片
热门文章
- 通过对比 5 月补丁分析 win32k 空指针解引用漏洞
- mysql建图书馆表_数据库图书管理建表与修改表
- navicat创建连接 2002-can‘t connect to server on localhost(10061)且mysql服务已启动问题
- HCNP——Hello报文
- HDP 2.6.4 kettle 7.1 spark-submit配置
- 苏慧伦“柠檬树” 英文翻唱歌词 Fool's garden Lemon tree
- Python_数据科学库_pandas
- python制表符怎么用_python怎样输入制表符
- 使用JavaScript制作动态网页-1
- 数学分析教程史济怀练习10.5