libpcap编程-编写自己的网络嗅探程序.pdf
《libpcap编程-编写自己的网络嗅探程序.pdf》由会员分享,可在线阅读,更多相关《libpcap编程-编写自己的网络嗅探程序.pdf(19页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、 Programming with LibpcapProgramming with Libpcap -Sniffing the NetworkSniffing the Network Author:Luis Martin GarciaAuthor:Luis Martin Garcia =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*你将会学到你将会学到什么什么?*|1.数据包捕获的原则;|2.如何利用 libpcap 捕获数据包;|3.关于我们何时需要编写数|据包捕获程序等方面.|*你应该所掌握的你应该所掌握的.*.*|1.C 语言程序设计;|2.网络的基本工作原理和
2、 OSI 的标|准模型;|3.了解常见的协议,比如以太协仪,|TCP/IP 协议,或者 ARP 协议。|=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*概要概要*自从 1969 年第一条电子信息带着研究人员的期望成功地通过阿葩网(ARPANET)进行了有史以来最便捷的信息传递方式,计算机网络已经发生了巨大的变化。以前网络(此处及以下均指计算机网络)规模小,结构简单,利用一些简单的诊断工具通常便可以解决网络问题。但随着网络的不断复杂化,对复杂网络的管理和检测的需求日益增加。现如今,计算机网络不仅规模大而且通常有着各种系统利用大量不同种类的协议所进行的通信。这种复杂的局面
3、产生了更多的可以监视和检测网络通信的智能化工具。今天,在任何一个网络管理员的管理工具箱中都有着这样的一个工具,那就是嗅探器(sniffer)。嗅探器,也称做数据包分析器,是一些拥有拦截网络传输数据的能力的程序。这些程序在网络管理人员和黑帽社区之中相当流行,因为他们既可被环绕于正义的光环也可沦为邪恶的爪牙。在本文中,我们将阐述数据包捕获的主要原则和方法并且为大家介绍 libpcap(一个开源并可移植的数据捕获库,著名的网络工具 tcpdump,dsniff,kismet,snort 和 ettercap的核心便有 libpcap 库中的各种 API)。*数数据包捕获据包捕获*数据包捕获是在数据传
4、输的网络上进行数据收集的一种行为。嗅探器是捕获数据包的最佳实现,但是许多其它种类的应用需要通过网卡才能完成数据包的捕获,它们包括网络数据统计工具,入侵检测系统,端口锁定守护进程,密码嗅探器,ARP 注入攻击,路由检测器等等。首先让我们大致了解一下数据包捕获在以太网络中的工作原理。每当一个网卡收到一个 以太帧,它就会检测该帧的目的网卡地址(MAC address)是否与自己的网卡地址相符(相同)。如果相符,网卡便产生一个中断请求,该中断请求将由负责处理此类中断的系统网卡驱动程序处理。该驱动给接收到的数据打上时间戳,然后将数据从网卡缓冲区复制到系统内核空间的一块内存上。接着系统通过查看以太数据帧头
5、的以太类型区域判断接收到的数据包是属于哪一种类型继而将该包中的数据传递给协议堆栈由相应的协议处理机制处理。大多数情况下数据包包含一个 IPv4 数据报,如此,IPv4 协议处理机制将被激活。这种处理机制将进行一系列验证行为来确保比如数据包没有遭到损坏,本机的确是该包的目的地等等。当所有验证均通过后,IP 头被移除,剩下的部分再被传递到下一层协议处理机制(可能是 TCP 或者 UDP)。这种处理过程不断重复直到数据到达由用户空间的应用程序来处理的应用层。当我们使用嗅探器时,数据包将完成上述的相同过程,但除了一个地方:网络驱动程序也将拷贝接收到的或是发出的任何数据到内核中名叫数据包过滤器的部分。而
6、正是数据包过滤器使数据包的捕获成为可能。默认的情况下,数据包过滤器允许任意包通过,但是,我们稍候将会看到它们通常提供了高级的过滤能力。由于数据包捕获可能涉及到网络安全,因此多数操作系统要求必须要有管理员的权限才能使用数据过滤的这一项功能。图 1 阐释了捕获数据包的过程。*LibpcapLibpcap*Libpcap 是一个提供了针对网络数据包捕获系统的高层接口的开源函数库。它是在 1994 年由麦克坎尼(McCanne),莱乐士(Leres)和杰科宾森(Jacobson)创建的。当时他们是美国加洲柏克利大学劳恩斯国家实验室的研究生,而 Libpcap 正是他们研究和改善 TCP 和英特网网关功
7、能的一部分成果。Libpcap 作者的主要愿望是开创一个独立平台的应用程序接口(API)以此消除程序中针对不同操作系统所包含的数据包捕获代码模块,因为通常每一个操作系统商都会实现他们自己的捕获机制。(也就是解决了移植性的问题,这有利于提高程序员开发的效率-译者注)Libpcap 应用程序接口(API)被设计用于 C 或者 C+语言。然而后来出现很多封装包使它也可用于其它语言,比如:perl 语言,python 语言,Java 语言,C或者 Ruby 语言。Libpcap运行于大多数类 UNIX 操作系统上(Linux,Solaris,BSD,HP-UX.)。当然,也有 Windows版本,曰
8、Winpcap。现在 libpcap 由 Tcpdump 团队维护。完整的文档和源代码可以从 tcpdump的官方网站上获得:http:/www.tcpdump.org.(http:/www.winpcap.org/for Winpcap)*我们之于我们之于 libpcaplibpcap 的第一步的第一步*现在我们已经知道数据包捕获的原理,让我们编写自己的嗅探程序吧!我们需要的第一件事情是一个用于监听的网络接口。我们可以自己明确的详细说明一个接口,或者让 libpcap 为我们获取一个。函数 char*pcap_lookupdev(char*errbuf)char*pcap_lookupdev
9、(char*errbuf)返回一个指向包含第一个适合于数据包捕获的网络设备名称的字符串的指针。通常当用户自己没有说明任何一个网络接口时,这个函数应该被调用。使用硬编码的接口名称是一个很差的主意,因为他们几乎没有移植性。函数 pcap_lookupdev()中的参数 errbuf 是应由用户提供的一段缓冲区,如果有不正常的地方 libpcap 库将用这个缓冲区存储出错信息。许多由 libpcap 实现的函数所带有这个参数。当我们请求分配这个缓冲区时我们一定要谨慎,因为该缓冲区必须要至少可以容纳下PCAP_ERRBUF_SIZE 个字节(目前它被定义为 256 个字节)。当函数出错时返回 NULL
10、.一旦我们获取了网络设备的名称,我们还需要打开它。函数 pcap_t*pcap_open_live(const char*device,pcap_t*pcap_open_live(const char*device,int snaplen,int promisc,int snaplen,int promisc,int to_ms,char*errbuf)int to_ms,char*errbuf)便可以做到。该函数返回一个 pcap_t 类型的接口描述符,此描述符稍候将会被 libpcap 的其他函数用到。(与此类似的比如文件描述符-译者注)如果函数调用失败,就返回 NULL.函数 pcap_
11、open_live()的第一个参数是一个指向包含我们想要打开的网络设备名称的字符串指针,显然该参数可由 pcap_lookupdev()获得。第二个参数是我们要捕获的数据包的最大字节数。给这个参数设定一个较小的值在某些情况下也会起到一定作用,比如:我们只想抓获包头或者是在内存资源紧张的嵌入式系统中的程序编写。通常最大的以太帧大小是1518 字节。但是其它的链接类型,比如 FDDI 或者是 802.11 有跟大的上限值。65535 这个数值对于容纳任何网络的任何数据包应该是足够的。参数 to_ms 定义了在把捕获的数据从内核空间复制到用户空间之前内核应该等待多少个毫秒。反复地改变缓冲区的内容将严
12、重地消耗昂贵的计算时间。如果我们是在一个繁忙的网络传输环境中捕获数据包,那么最好是让内核在内核空间和用户空间之间拷贝数据之前先将数据包聚集,然后一起拷贝。当我们把 to_ms 的值是赋为零时,这将导致读操作将永远进行下去直到足够的数据包到达网络接口(在拷贝数据之前驱动程序要从网络接口读入数据)。Libpcap 文档对该参数没有提供任何建议值,不过我们可以通过参考其他的嗅探器程序来获取一些灵感。Tcpdump 用的是数值 1000,dsniff 用的是数值 512,此外 ettercap 在 linux或 OpenBSD 操作系统下用数值 0,其他操作系统下用数值 10。参数 promisc 决
13、定是否将网卡置于混杂模式。也就是说网卡是否可以接收目的地不是自己的数据包。将其置零我们会获取非混杂模式,其他任何值都将置网卡于混杂模式。请注意即使是我们让 libpcap 将网卡置于非混杂模式,但如果网络接口在此之前已经处于混杂模式,那么他将继续保持在混杂模式的状态下。我们不能保证我们不会收到传输给其它主机的数据包,相反,我们最好像后面做的那样利用 libpcap 提供的过滤能力。一旦我们打开了一个可以捕获数据包的网络接口,我们必须告诉 libpcap 我们想要开始捕获数据包了。对此,我们有以下选择:*函数 const u_char*pcap_next(pcap_t*p,struct pcap
14、_pkthdr*h)const u_char*pcap_next(pcap_t*p,struct pcap_pkthdr*h)将利用由 pcap_open_live 返回的接口描述符 pcap_t,一个指向 pcap_pkthdr 类型的结构体进行处理后返回第一个到达网络接口的数据包。*函数 int pcap_loop(pcap_t*p,int cnt,int pcap_loop(pcap_t*p,int cnt,pcap_handler calpcap_handler callback,u_char*user)lback,u_char*user)被用于收集数据包并且处理它们。该函数直到 cn
15、t 个数据包被捕获后才会返回。如果 cnt 是负值,那么 pcap_loop()只有在出现错误时才会返回。你可能正在疑惑:如果这些函数只是返回一些整数,那么那些被捕获的数据包在哪儿呢?答案听起来有些复杂。pcap_loop()并没有返回那些捕获的数据包,相反,每当一个数据包被读取时它调用一个由用户定义的函数。这样我们就可以在一个分开的函数中实现我们自己的数据处理方式而不是循环调用 pcap_next()并在循环内部处理这些数据。然而,这里面有一个问题,如果 pcap_loop()调用我们的函数,我们如何向它传递参数呢?我们需要应用丑陋的全局规则吗?答案是否定的。libpcap 的开发团队早已考
16、虑过这些问题并找到一种可以向回调函数(callback function)传递参数的方法,那就是参数 user。这个指针在函数的每次调用中均被传递,该指针是指向 u_char 类型的,因此当调用 pcap_loop()和当在回调函数内部使用该参数时我们必须按照程序的需求对它进行强制类型转换。我们的数据包处理函数一 定要有具体的原型,否则 pcap_loop()将不会知道如何去使用它。它应该按如下方式被声明:void function_name(u_char*userarg,void function_name(u_char*userarg,const struct pcap_ptkhdr,co
17、nst u_char*packet);const struct pcap_ptkhdr,const u_char*packet);第一个参数是用于传递给 pcap_loop()的用户指针,第二个指针是指向一个包含与被捕获数据包相关的信息的结构体。列表 1 给出了这种结构体的定义。列表列表 1 1.Structure pcap_pkthdrStructure pcap_pkthdr struct pcap_pkthdrstruct pcap_pkthdr struct timeval ts;/timestamp of capturestruct timeval ts;/timestamp of
18、capture bpf_u_int32 caplen;/number of bytes that were storedbpf_u_int32 caplen;/number of bytes that were stored bpf_u_int32 len;/total length of the packetbpf_u_int32 len;/total length of the packet ;该结构体中的成员 caplen 通常和成员 len 的值一样,但除了以下情况:捕获到的数据包大小超过了我们在函数 pcap_open_live()中给 snaplen 赋的值。*函数函数 int p
19、cap_dispatch(pcap_t*p,int cnt,int pcap_dispatch(pcap_t*p,int cnt,pcap_handler callback,u_char*user)pcap_handler callback,u_char*user)是我们的第三个选择。它与 pcap_loop()类似,但当函数 pcap_open_live()中定义的延时参数 to_ms 消耗完后函数 pcap_dispatch()也会立即返回。列表 2 提供了一个将捕获到的数据包的原始数据打印出来的简单嗅探程序,请注意头文件 pcap.h 必须包含在程序代码中。为了使代码清晰明了,我们省略了
20、错误检测(编写适当的错误检测代码是程序员应具备的良好习惯-译者注)。列表列表 2 2/*/*Listing 2.Simple snifferListing 2.Simple sniffer To compile:gcc simplesniffer.c To compile:gcc simplesniffer.c-o simplesinffer o simplesinffer-lpcaplpcap */*/#include#include#include#include#include#include#def#define MAXBYTES2CAPTURE 2048ine MAXBYTES2CA
21、PTURE 2048 void processPacket(u_char*arg,const struct pcap_pkthdr*pkthdr,void processPacket(u_char*arg,const struct pcap_pkthdr*pkthdr,const u_char*packet)const u_char*packet)int i=0,*counter=(int*)arg;int i=0,*counter=(int*)arg;printf(Pcaket Count:%dprintf(Pcaket Count:%d n,+(*counter);n,+(*counter
22、);printf(Received Packet Size:%dprintf(Received Packet Size:%d n,pkthdrn,pkthdr-lelen);n);printf(Payload:printf(Payload:n);n);for(i=0;ipkthdrfor(i=0;ilen;i+)len;i+)if(isprint(packeti)if(isprint(packeti)printf(%c,packeti);printf(%c,packeti);elseelse printf(.);printf(.);if(i%16=0&i!=0)|i=pkthdrif(i%16
23、=0&i!=0)|i=pkthdr-lenlen-1)1)printf(printf(n);n);return;return;int main()int main()int i=0,counint i=0,count=0;t=0;pcap_t*descr=NULL;pcap_t*descr=NULL;char errbufPCAP_ERRBUF_SIZE,*device=NULL;char errbufPCAP_ERRBUF_SIZE,*device=NULL;memset(errbuf,0,PCAP_ERRBUF_SIZE);memset(errbuf,0,PCAP_ERRBUF_SIZE)
24、;/*/*获取第一个适合捕获的网络设备名称获取第一个适合捕获的网络设备名称*/*/device=pcap_lookupdev(errbuf);device=pcap_lookupdev(errbuf);printf(Opening device%sprintf(Opening device%s n,device);n,device);/*/*以混杂模式打开网络设备以混杂模式打开网络设备*/*/descr descr=pcap_open_live(device,MAXBYTES2CAPTURE,1,512,errbuf);=pcap_open_live(device,MAXBYTES2CAPTU
25、RE,1,512,errbuf);/*/*死循环并在每一次接收到数据包死循环并在每一次接收到数据包时时调用调用回调回调函数函数 processPacket()*/processPacket()*/pcap_loop(descr,pcap_loop(descr,-1,processPacket,(u_char*)&count);1,processPacket,(u_char*)&count);return 0;return 0;*一旦我们捕获旦我们捕获到到数据包数据包*当一个数据包被捕获,我们程序得到的唯一的东西是一组数据。通常网卡驱动程序和协议堆栈为我们处理那些数据,但是当我们自己的程序捕获数
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- libpcap 编程 编写 自己 网络 程序
限制150内