定义:网络嗅探也叫网络侦听,指的是使用特定的网络协议来分解捕获到的数据包,并根据对应的网络协议识别对应数据片断
作用:管理员可以用来监听网络的流量情况
开发网络应用的程序员可以监视程序的网络情况
黑客可以用来刺探网络情报
网卡的四种接收数据模式
①广播模式:该模式下的网卡能够接收网络中的广播信息;
②组播模式:设置在该模式下的网卡能够接收组播数据;
③直接模式:在这种模式下,只有目的网卡才能接收该数据;
④混杂模式:在这种模式下的网卡能够接收一切通过它的数据,而不管该数据是否是传给它的。
通常,网卡的缺省配置是支持前三种模式。 为了监听网络上的流量,必须设置为混杂模式
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//打印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_remts).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);
}