如何发现和防止Sniffer嗅探

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何发现和防止Sniffer嗅探相关的知识,希望对你有一定的参考价值。

详细一点

平时的通讯尽量用加密等级高的方式,比如上支持https的站点。计算机路由器的密码都要定期更换,系统重新做,这样就可以防止信息被窃。如果真的存在嗅探者,那你们可要当心。不过,我的办法能让你的防御等级上一个台阶。看看下面这些吧!
Sniffer(嗅探器)是一种常用的收集有用数据方法,这些数据可以是用户的帐号和密码,可以是一些商用机密数据等等。Snifffer可以作为能够捕获网络报文的设备,ISS为Sniffer这样定义:Sniffer是利用计算机的网络接口截获目的地为其他计算机的数据报文的一种工具。

Sniffer的正当用处主要是分析网络的流量,以便找出所关心的网络中潜在的问题。例如,假设网络的某一段运行得不是很好,报文的发送比较慢,而我们又不知道问题出在什么地方,此时就可以用嗅探器来作出精确的问题判断。 在合理的网络中,sniffer的存在对系统管理员是致关重要的,系统管理员通过sniffer可以诊断出大量的不可见模糊问题,这些问题涉及两台乃至多台计算机之间的异常通讯有些甚至牵涉到各种的协议,借助于sniffer%2C系统管理员可以方便的确定出多少的通讯量属于哪个网络协议、占主要通讯协议的主机是哪一台、大多数通讯目的地是哪台主机、报文发送占用多少时间、或着相互主机的报文传送间隔时间等等,这些信息为管理员判断网络问题、管理网络区域提供了非常宝贵的信息。

嗅探器与一般的键盘捕获程序不同。键盘捕获程序捕获在终端上输入的键值,而嗅探器则捕获真实的网络报文。

为了对sniffer的工作原理有一个深入的了解,我们先简单介绍一下HUB与网卡的原理。

预备知识

HUB工作原理
由于以太网等很多网络(常见共享HUB连接的内部网)是基于总线方式,物理上是广播的,就是当一个机器发给另一个机器的数据,共享HUB先收到然后把它接收到的数据再发给其他的(来的那个口不发了)每一个口,所以在共享HUB下面同一网段的所有机器的网卡都能接收到数据。

交换式HUB的内部单片程序能记住每个口的MAC地址,以后就该哪个机器接收就发往哪个口,而不是像共享HUB那样发给所有的口,所以交换HUB下只有该接收数据的机器的网卡能接收到数据,当然广播包还是发往所有口。显然共享HUB的工作模式使得两个机器传输数据的时候其他机器别的口也占用了,所以共享HUB决定了同一网段同一时间只能有两个机器进行数据通信,而交换HUB两个机器传输数据的时候别的口没有占用,所以别的口之间也可以同时传输。这就是共享HUB与交换HUB不同的两个地方,共享HUB是同一时间只能一个机器发数据并且所有机器都可以接收,只要不是广播数据交换HUB同一时间可以有对机器进行数据传输并且数据是私有的。

网卡工作原理
再讲讲网卡的工作原理。网卡收到传输来的数据,网卡内的单片程序先接收数据头的目的MAC地址,根据计算机上的网卡驱动程序设置的接收模式判断该不该接收,认为该接收就在接收后产生中断信号通知CPU,认为不该接收就丢弃不管,所以不该接收的数据网卡就截断了,计算机根本就不知道。CPU得到中断信号产生中断,操作系统就根据网卡驱动程序中设置的网卡中断程序地址调用驱动程序接收数据,驱动程序接收数据后放入信号堆栈让操作系统处理。

局域网如何工作
数据在网络上是以很小的称为帧(Frame)的单位传输的帧由好几部分组成,不同的部分执行不同的功能。(例如,以太网的前12个字节存放的是源和目的的地址,这些位告诉网络:数据的来源和去处。以太网帧的其他部分存放实际的用户数据、TCP/IP的报文头或IPX报文头等等)。

帧通过特定的网络驱动程序进行成型,然后通过网卡发送到网线上。通过网线到达它们的目的机器,在目的机器的一端执行相反的过程。接收端机器的以太网卡捕获到这些帧,并告诉操作系统帧的到达,然后对其进行存储。就是在这个传输和接收的过程中,嗅探器会造成安全方面的问题。

通常在局域网(LAN)中同一个网段的所有网络接口都有访问在物理媒体上传输的所有数据的能力,而每个网络接口都还应该有一个硬件地址,该硬件地址不同于网络中存在的其他网络接口的硬件地址,同时,每个网络至少还要一个广播地址。(代表所有的接口地址),在正常情况下,一个合法的网络接口应该只响应这样的两种数据帧:

1、帧的目标区域具有和本地网络接口相匹配的硬件地址。
2、帧的目标区域具有“广播地址”。

在接受到上面两种情况的数据包时,网卡通过cpu产生一个硬件中断,该中断能引起操作系统注意,然后将帧中所包含的数据传送给系统进一步处理。

当采用共享HUB,用户发送一个报文时,这些报文就会发送到LAN上所有可用的机器。在一般情况下,网络上所有的机器都可以“听”到通过的流量,但对不属于自己的报文则不予响应(换句话说,工作站A不会捕获属于工作站B的数据,而是简单的忽略这些数据)。

如果局域网中某台机器的网络接口处于杂收(promiscuous)模式(即网卡可以接收其收到的所有数据包,下面会详细地讲),那么它就可以捕获网络上所有的报文和帧,如果一台机器被配置成这样的方式,它(包括其软件)就是一个嗅探器。

Sniffer

Sniffer原理
有了这HUB、网卡的工作原理就可以开始讲讲SNIFFER。首先,要知道SNIFFER要捕获的东西必须是要物理信号能收到的报文信息。显然只要通知网卡接收其收到的所有包(一般叫作杂收promiscuous模式:指网络上的所有设备都对总线上传送的数据进行侦听,并不仅仅是它们自己的数据。),在共享HUB下就能接收到这个网段的所有包,但是交换HUB下就只能是自己的包加上广播包。

要想在交换HUB下接收别人的包,那就要让其发往你的机器所在口。交换HUB记住一个口的MAC是通过接收来自这个口的数据后并记住其源MAC,就像一个机器的IP与MAC对应的ARP列表,交换HUB维护一个物理口(就是HUB上的网线插口,这之后提到的所有HUB口都是指网线插口)与MAC的表,所以可以欺骗交换HUB的。可以发一个包设置源MAC是你想接收的机器的MAC,那么交换HUB就把你机器的网线插的物理口与那个MAC对应起来了,以后发给那个MAC的包就发往你的网线插口了,也就是你的网卡可以SNIFFER到了。注意这物理口与MAC的表与机器的ARP表一样是动态刷新的,那机器发包后交换HUB就又记住他的口了,所以实际上是两个在争,这只能应用在只要收听少量包就可以的场合。

内部网基于IP的通信可以用ARP欺骗别人机器让其发送给你的机器,如果要想不影响原来两方的通信,可以欺骗两方,让其都发给你的机器再由你的机器转发,相当于做中间人,这用ARP加上编程很容易实现。并且现在很多设备支持远程管理,有很多交换HUB可以设置一个口监听别的口,不过这就要管理权限了。

利用这一点,可以将一台计算机的网络连接设置为接受所有以太网总线上的数据,从而实现sniffer。Sniffer就是一种能将本地网卡状态设成‘杂收’状态的软件,当网卡处于这种“杂收”方式时,该网卡具备“广播地址”,它对遇到的每一个帧都产生一个硬件中断以便提醒操作系统处理流经该物理媒体上的每一个报文包。(绝大多数的网卡具备置成杂收方式的能力)

可见,sniffer工作在网络环境中的底层,它会拦截所有的正在网络上传送的数据,并且通过相应的软件处理,可以实时分析这些数据的内容,进而分析所处的网络状态和整体布局。值得注意的是:sniffer是极其安静的,它是一种消极的安全攻击。

嗅探器在功能和设计方面有很多不同。有些只能分析一种协议,而另一些可能能够分析几百种协议。一般情况下,大多数的嗅探器至少能够分析下面的协议:标准以太网、TCP/IP、IPX、DECNet。

嗅探器造成的危害
sniffing是作用在网络基础结构的底层。通常情况下, 用户并不直接和该层打交道,有些甚至不知道有这一层存在。所以,应该说snffer的危害是相当之大的,通常,使用sniffer是在网络中进行欺骗的开始。它可能造成的危害:

嗅探器能够捕获口令。这大概是绝大多数非法使用sniffer的理由,sniffer可以记录到明文传送的userid和passwd。
能够捕获专用的或者机密的信息。比如金融帐号,许多用户很放心在网上使用自己的信用卡或现金帐号,然而sniffer可以很轻松截获在网上传送的用户姓名、口令、信用卡号码、截止日期、帐号和pin。比如偷窥机密或敏感的信息数据,通过拦截数据包,入侵者可以很方便记录别人之间敏感的信息传送,或者干脆拦截整个的email会话过程。
可以用来危害网络邻居的安全,或者用来获取更高级别的访问权限。
窥探低级的协议信息。
这是很可怕的事,通过对底层的信息协议记录,比如记录两台主机之间的网络接口地址、远程网络接口ip地址、ip路由信息和tcp连接的字节顺序号码等。这些信息由非法入侵的人掌握后将对网络安全构成极大的危害,通常有人用sniffer收集这些信息只有一个原因:他正要进行一次欺骗(通常的ip地址欺骗就要求你准确插入tcp连接的字节顺序号),如果某人很关心这个问题,那么sniffer对他来说只是前奏,今后的问题要大得多。(对于高级的hacker而言,我想这是使用sniffer的唯一理由吧)
事实上,如果你在网络上存在非授权的嗅探器就意味着你的系统已经暴露在别人面前了。

一般Sniffer只嗅探每个报文的前200到300个字节。用户名和口令都包含在这一部分中,这是我们关心的真正部分。工人,也可以嗅探给定接口上的所有报文,如果有足够的空间进行存储,有足够的那里进行处理的话,将会发现另一些非常有趣的东西……

简单的放置一个嗅探器并将其放到随便什么地方将不会起到什么作用。将嗅探器放置于被攻击机器或网络附近,这样将捕获到很多口令,还有一个比较好的方法就是放在网关上。sniffer通常运行在路由器,或有路由器功能的主机上。这样就能对大量的数据进行监控。sniffer属第二层次的攻击。通常是攻击者已经进入了目标系统,然后使用sniffer这种攻击手段,以便得到更多的信息。如果这样的话就能捕获网络和其他网络进行身份鉴别的过程
参考技术A 如何在网络中发现一个Sniffer,简单的一个回答是你发现不了。因为他们根本就没有留下任何痕迹,sniffer是如此嚣张又安静,如何知道有没有sniffer存在,这也是一个很难说明的问题。

查找网络存在sniffer
一、网络通讯掉包率反常的高
通过一些网络软件,可以看到信息包传送情况,向ping这样的命令会告诉你掉了百分几的包。如果网络中有人在Listen,那么信息包传送将无法每次都顺畅的流到目的地。(这是由于sniffer拦截每个包导致的) 。

二、网络带宽出现反常
通过某些带宽控制器(通常是防火墙所带),可以实时看到目前网络带宽的分布情况,如果某台机器长时间的占用了较大的带宽,这台机器就有可能在监听。在非高速信道上,如56Kddn等,如果网络中存在sniffer,你应该也可以察觉出网络通讯速度的变化。

三、查看计算机上当前正在运行的所有程序。
但这通常并不可靠,但可以控制计算机中程序运行。在Unix系统下使用下面的命令: ps -aux 或: ps -augx。 这个命令列出当前的所有进程,启动这些进程的用户,它们占用CPU的时间,占用内存的多少等等。
Windows系统下,按下Ctrl+Alt+Del,看一下任务列表。不过,编程技巧高的Sniffer即使正在运行,也不会出现在这里的。
系统中搜索,查找可疑的文件。但入侵者可能使用自己编写的程序,所以都会给发现sniffer造成相当大的困难。
还有许多工具,能用来看看你的系统会不会在杂收模式。从而发现是否有一个Sniffer正在运行。

防止sniffer 驻入
对于嗅探器如此强大的‘灵敏度’,你最关心的可能是传输一些比较敏感的数据,如用户ID或口令等等。有些数据是没有经过处理的,一旦被sniffer,就能获得这些信息,解决这些问题的办法是加密。

介绍一下SSH,全名Secure Shell,是一个在应用程序中提供安全通信的协议,建立在客户机/服务器模型上的。SSH服务器的分配的端口是22,连接是通过使用一种来自RSA的算法建立的,在授权完成后,接下来的通信数据是用IDEA技术来加密的。这通常是较强的,适合与任何非秘密和非经典的通讯。

SSH后来发展成为F-SSH,提供了高层次的,军方级别的对通信过程的加密。它为通过TCP/IP网络通信提供了通用的最强的加密。如果某个站点使用F-SSH,用户名和口令成为不是很重要的一点。目前,还没有人突破过这种加密方法。即使是sniffer,收集到的信息将不再有价值,当然最关键的是怎样使用它。
另一个比较容易接受的是使用安全拓扑结构。这听上去很简单,但实现起来花销是很大的。这样的拓扑结构需要有这样的规则:一个网络段必须有足够的理由才能信任另一网络段。网络段应该考虑你的数据之间的信任关系上来设计,而不是硬件需要。开始处理网络拓扑则要做到以下几点:

第一点:一个网络段是仅由能互相信任的计算机组成的。通常它们在同一个房间里,或在同一个办公室里。比如你的财务信息,应该固定在某一节点,就象你的财务部门被安排在办公区域的的一个不常变动的地方。

第二点:注意每台机器是通过硬连接线接到Hub的。Hub再接到交换机上。由于网络分段了,数据包只能在这个网段上被sniffer。其余的网段将不可能被sniffer。

第三点:所有的问题都归结到信任上面。计算机为了和其他计算机进行通信,它就必须信任那台计算机。作为系统管理员,你的工作是决定一个方法,使得计算机之间的信任关系很小。这样,就建立了一种框架,可以告诉你什么时候放置了一个sniffer,它放在那里了,是谁放的等等。

第四点:如果你的局域网要和INTERNET相连,仅仅使用防火墙是不够的。入侵者已经能从一个防火墙后面扫描,并探测正在运行的服务。你要关心的是一旦入侵者进入系统,他能得到些什么。你必须考虑一条这样的路径,即信任关系有多长。举个例子,假设你的WEB服务器对某一计算机A是信任的。那么有多少计算机是A信任的呢。又有多少计算机是受这些计算机信任的呢?在信任关系中,这台计算机之前的任何一台计算机都可能对你的计算机进行攻击,并成功。你的任务就是保证一旦出现的Sniffer,它只对最小范围有效。
参考技术B 查找网络存在sniffer
一、网络通讯掉包率反常的高
通过一些网络软件,可以看到信息包传送情况,向ping这样的命令会告诉你掉了百分几的包。如果网络中有人在Listen,那么信息包传送将无法每次都顺畅的流到目的地。(这是由于sniffer拦截每个包导致的) 。

二、网络带宽出现反常
通过某些带宽控制器(通常是防火墙所带),可以实时看到目前网络带宽的分布情况,如果某台机器长时间的占用了较大的带宽,这台机器就有可能在监听。在非高速信道上,如56Kddn等,如果网络中存在sniffer,你应该也可以察觉出网络通讯速度的变化。

三、查看计算机上当前正在运行的所有程序。
但这通常并不可靠,但可以控制计算机中程序运行。在Unix系统下使用下面的命令: ps -aux 或: ps -augx。 这个命令列出当前的所有进程,启动这些进程的用户,它们占用CPU的时间,占用内存的多少等等。
Windows系统下,按下Ctrl+Alt+Del,看一下任务列表。不过,编程技巧高的Sniffer即使正在运行,也不会出现在这里的。
系统中搜索,查找可疑的文件。但入侵者可能使用自己编写的程序,所以都会给发现sniffer造成相当大的困难。
还有许多工具,能用来看看你的系统会不会在杂收模式。从而发现是否有一个Sniffer正在运行。

防止sniffer 驻入
对于嗅探器如此强大的‘灵敏度’,你最关心的可能是传输一些比较敏感的数据,如用户ID或口令等等。有些数据是没有经过处理的,一旦被sniffer,就能获得这些信息,解决这些问题的办法是加密。

介绍一下SSH,全名Secure Shell,是一个在应用程序中提供安全通信的协议,建立在客户机/服务器模型上的。SSH服务器的分配的端口是22,连接是通过使用一种来自RSA的算法建立的,在授权完成后,接下来的通信数据是用IDEA技术来加密的。这通常是较强的,适合与任何非秘密和非经典的通讯。

SSH后来发展成为F-SSH,提供了高层次的,军方级别的对通信过程的加密。它为通过TCP/IP网络通信提供了通用的最强的加密。如果某个站点使用F-SSH,用户名和口令成为不是很重要的一点。目前,还没有人突破过这种加密方法。即使是sniffer,收集到的信息将不再有价值,当然最关键的是怎样使用它。
另一个比较容易接受的是使用安全拓扑结构。这听上去很简单,但实现起来花销是很大的。这样的拓扑结构需要有这样的规则:一个网络段必须有足够的理由才能信任另一网络段。网络段应该考虑你的数据之间的信任关系上来设计,而不是硬件需要。开始处理网络拓扑则要做到以下几点:

第一点:一个网络段是仅由能互相信任的计算机组成的。通常它们在同一个房间里,或在同一个办公室里。比如你的财务信息,应该固定在某一节点,就象你的财务部门被安排在办公区域的的一个不常变动的地方。

第二点:注意每台机器是通过硬连接线接到Hub的。Hub再接到交换机上。由于网络分段了,数据包只能在这个网段上被sniffer。其余的网段将不可能被sniffer。

第三点:所有的问题都归结到信任上面。计算机为了和其他计算机进行通信,它就必须信任那台计算机。作为系统管理员,你的工作是决定一个方法,使得计算机之间的信任关系很小。这样,就建立了一种框架,可以告诉你什么时候放置了一个sniffer,它放在那里了,是谁放的等等。

第四点:如果你的局域网要和INTERNET相连,仅仅使用防火墙是不够的。入侵者已经能从一个防火墙后面扫描,并探测正在运行的服务。你要关心的是一旦入侵者进入系统,他能得到些什么。你必须考虑一条这样的路径,即信任关系有多长。举个例子,假设你的WEB服务器对某一计算机A是信任的。那么有多少计算机是A信任的呢。又有多少计算机是受这些计算机信任的呢?在信任关系中,这台计算机之前的任何一台计算机都可能对你的计算机进行攻击,并成功。你的任务就是保证一旦出现的Sniffer,它只对最小范围有效。本回答被提问者采纳
参考技术C 装防火墙。

实验一:网络嗅探器

一:实验背景

  为了深入了解和掌握TCP/IP协议栈以及数据包的格式,练习使用libpcap对网络数据包进行解析。

  libpcap提供的接口函数主要实现和封装了与数据包截获有关的过程。利用libpcap的C函数库的接口,网络安全工具开发人员可以很方便地编写出具有结构化强、健壮性好、可移植性高等特点的程序。因此,这些函数库在网络安全工具的开发中具有很大的价值,在scanner、sniffer、firewall、IDS等领域都获得了极其广泛的应用,著名的tcpdump软件、ethereal软件等就是在libpcap的基础上开发的。

二:实验步骤

实验步骤:

结合上传的代码,学习和理解Libpcap中主要API。

1. char *pcap_lookupdev(char *errbuf)

该函数用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络设备名(一个字符串指针)。如果函数出错,则返回NULL,同时errbuf中存放相关的错误消息。

2.    int pcap_lookupnet(char *device, bpf_u_int32 *netp,bpf_u_int32 *maskp, char *errbuf)

获得指定网络设备的网络号和掩码。netp参数和maskp参数都是bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相关的错误消息。

3.   打开设备 

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时用于传递错误消息。

4.   编译和设置过滤器

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。抓取下一个数据包

5.   抓取数据包

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中的最后一个参数,不管给pcap_loop的最后一个参数传递什么值,当回调函数被调用时,它都会作为第一个参数传递给回调函数,第二个参数是pcap头,它包含一些信息:抓包时间,包多大,等等,这个结构在pcap.h中定义,如下
struct pcap_pkthdr {
struct timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* length this packet (off wire) */
};

第三个参数则是数据内容,主要是对其进行分析。

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()函数将始终循环运行,除非出现错误。

u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

返回指向下一个数据包的u_char指针。

6.    void pcap_close(pcap_t *p)

关闭p参数相应的文件,并释放资源。

正确使用以上接口,能较为容易地获取经过网卡的数据包。值得注意的是,运行程序时需要使用超级权限;以及代码编译时需要使用-lpcap选项,指明链接额外的函数库。

struct ip * ip = (struct ip *)(packet + ETHER_SIZE);

7.packet为回调函数中的第三个参数,是指向从以太层开始的数据包头的u_char类型的指针;通过将指针后移ETHER_SIZE字节,可以找到IP头部。通过对之后的数据进行强制类型转换(struct ip*),可以将接下来的20个字节转换为具有格式的结构体。ip头部的具体内容可以参见/usr/include/netinet中的头文件ip.h中的内容。

8.类似地,struct tcphdr *tcp = (struct tcphdr *)(packet + 14 + ip_hl);可以获得数据包中tcp的包头部。

代码如下

#define APP_NAME        "sniffex"
#define APP_DESC        "Sniffer example using libpcap"
#define APP_COPYRIGHT    "Copyright (c) 2005 The Tcpdump Group"
#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;
}

/*
 * print packet payload data (avoid printing binary data)
 */
void
print_payload(const u_char *payload, int len)
{

    int len_rem = len;
    int line_width = 16;            /* number of bytes per line */
    int line_len;
    int offset = 0;                    /* zero-based offset counter */
    const u_char *ch = payload;

    if (len <= 0)
        return;

    /* data fits on one line */
    if (len <= line_width) {
        print_hex_ascii_line(ch, len, offset);
        return;
    }

    /* data spans multiple lines */
    for ( ;; ) {
        /* compute current line length */
        line_len = line_width % len_rem;
        /* print line */
        print_hex_ascii_line(ch, line_len, offset);
        /* compute total remaining */
        len_rem = len_rem - line_len;
        /* shift pointer to remaining bytes to print */
        ch = ch + 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(ch, len_rem, offset);
            break;
        }
    }

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 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[] = "ip";        /* 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 = 100;            /* 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;
}

 

以上是关于如何发现和防止Sniffer嗅探的主要内容,如果未能解决你的问题,请参考以下文章

Python黑帽编程 4.1 Sniffer(嗅探器)之数据捕获(上)

Python_sniffer(网络嗅探器)

sniffer嗅探

网络安全:基于ARP欺骗的嗅探原理

Raw Socket(原始套接字)实现Sniffer(嗅探)但是收不到数据包

KademliaDHTKRPCBitTorrent 协议DHT Sniffer