在这学期的网络安全课程设计中,我们需要自己实现一个基于WinPcap编程接口的网络嗅探器,历时两周完成,主要参考资料:
1、WinPcap 中文技术文档(http://www.ferrisxu.com/WinPcap/html/index.html)
2、一步一步开发sniffer(Winpcap+MFC)
(https://blog.csdn.net/litingli/article/details/5950962)
所参考博客中的网络嗅探器是用MFC编写的,自己基于QT对其进行实现。另外,课程设计要求我们能够对局域网中的某一台主机的进行监听,因此结合ARP欺骗原理以及WinPcap发送数据包编程接口对程序做了进一步改进,达到欺骗被监听主机并声称自己为网关从而作为中间人对数据包进行监听以及转发的目的。
废话不多说,首先展示一下程序的运行效果,大家可以按照这个样式首先设计自己的网络嗅探器的ui,对应的功能之后再一步一步完善。正所谓,万事开头难,只要开始了,后面的工作自然是水到渠成。
上图即为网络嗅探器的主界面实现:图中标注1的地方是网卡的选择,2表示设置数据包捕获规则,即过滤规则,3是对获取到的数据包的内容的解析,同时在右边显示数据包中的内容,最后4处提供了数据包统计的功能。
另外一个实现即为ARP欺骗,根据获取到的被监听主机的IP地址以及MAC地址不断发送伪造的ARP应答包,从而实现ARP欺骗攻击。效果如下图所示:
另外,程序还提供了文件的保存以及打开功能,即将一次捕获到的数据包保存为pcap格式的文件(pcap格式文件能够被Wireshark解析),在下一次选择打开时重新对保存文件进行解析并显示抓包内容。
1.1.1 开发平台
开发机器:Windows10
开发平台:QT Creator 5.9.2
开发环境:WinPcap,pcap.h头文件,wpcap.lib库文件
1.1.2 运行平台
开发机器:Windows10
第三方组件:无
交换网络下的嗅探器编程实验主要包含两个方面:
实现类似wireshark的数据包嗅探分析功能,即:列出监测主机的所有网卡,选择一个网卡进行监听,捕获所有流经该网卡的数据包,并利用WinPcap函数库设置过滤规则,并可以通过本地文件形式保存和读取已捕获到的数据包信息;对每一个捕获到的数据包,按照各种协议的格式进行解析并显示在主界面上,包含所有捕获数据包的统计信息。
另一个需要实现的是中间人攻击,从而实现监听内网中的另一台主机的流量获取敏感信息。
系统架构如下图1.1所示:
图1.1 系统总体功能设计
从图1.1中可以看到,系统设计主要分为以下几个功能模块:
1) 获取当前主机中的网卡;
2) 设置过滤规则,并依据过滤规则捕获数据包:在实际系统实现的过程中,由于直接在主界面程序中抓包会卡死,所以需要新开一个抓包线程;
3) 捕获数据包在主面板中的实时显示(类似wireshark抓包动态显示);
4) 数据包解析、协议树显示以及数据包内容十六进制显示;
5) 捕获数据包统计;
6) 捕获数据包的保存以及读取;
7) ARP中间人攻击:主要包括向受害主机发送伪造的ARP应答报文,从而将受害主机发送给网关的数据包导入攻击主机网卡,实现中间人攻击可以直接开启Windows系统下的中间路由转发功能,也可以向网关发送ARP应答包从而伪造受害主机。
系统的总体工作流程图如图1.2所示。
程序开始运行,加载运行主机上的所有网卡,用户选择当前上网的网卡,并设置数据包过滤规则;接下来,如果用户可以在两种工作模式下运行,第一种是嗅探器模式下,即监听流经网卡上的所有数据包,并显示在主面板上,同时点击任意一个数据包都会按照该数据包的协议格式进行解析并以协议树的形式显示出来;第二种是在ARP攻击模式下运行,需要指定受害主机的MAC地址以及IP地址、网关的IP地址以及MAC地址,从而发送伪造的ARP欺骗报文,到达中间人攻击的目的,此时捕获所有的受害主机的进出流量,并将所有数据包显示在主面板上,同样,点击其中的每一条都将显示其对应的协议树以及报文十六进制形式显示的数据内容。
图1.2 系统工作流程图
下面从几个关键技术点介绍系统具体实现:
1) 获取当前运行程序主机中的网卡
WinPcap中提供了pcap_findalldevs_ex()函数用于获得已连接的网络适配器列表,该函数返回的是一个pcap_if结构的链表,该结构数据域中的name和description表示一个适配器名称和一个适配器的可理解描述。
获取主机中的网卡代码定义在mainwindow.cpp文件中,如图1.3所示:
图1.3 获取主机中网卡
在上述函数中,pcap_findalldevs_ex函数将获取到的网络适配器信息存入alldevs这个元素结构类型为pcap_if的链表中,接下来的一个循环统计了网络适配器的数目。
在MainWindow的构造函数中定义如下代码:
图1.4 初始化复选框
上面代码中,首先通过调用sniff_initCap()函数获取网络适配器列表,接下来链表中每一个项目的description内容添加到复选框中,从而每次打开主界面程序,网络适配器都将重新加载并显示。
2) 设置过滤规则,并依据过滤规则捕获数据包:
系统实现中的获取过滤规则并捕获数据包的实现主要是在MainWindow.cpp文件中的sniff_startCap函数中,函数的执行流程如图1.5所示。具体函数的使用方法可以参考WinPcap提供的官方文档。
抓包线程的定义的文件为capthread.h/cpp,其代码内容如图1.6所示。抓包线程中采用的是不用回调方法捕获数据包,pcap_next_ex()函数包含一个网络适配器的描述符和两个可以初始化并返回给用户的指针,一个指向的是pcap_pkthdr结构体,另一个指向数据报数据的缓冲。
在图1.6中包含一个循环,其循环终止的条件是stopped标志位设置为true或者pcap_netx_ex()函数读取出错,也就是说,当用户在主界面中点击停止捕获按钮时,实际上是调用线程中的stop()函数将标志位置为true,从而停止数据包的捕获。
图1.5数据包捕获流程
图1.6 抓包线程抓取数据包
3) 嗅探器相关结构体的定义以及主面板数据包的动态显示(类似wireshark抓包动态显示):
其中很重要的一点就是如何将capthread中捕获到的数据包内容从线程中传入到主进程程序,在protocol.h文件中定义数据结构,如图1.7所示。
可以看到,struct _datapkt结构用于存储单个数据包的信息,包含包的类型字符串、时间戳、长度、协议各层的协议头结构体指针、isHttp标志位以及httpsize成员。
同时,下面定义了两个容器类型,
typedef std::vector datapktVec;
//该容器用于存储数据包分析之得到的结构体;
typedef std::vector dataVec;
//该容器用于存储捕获的单个数据包的全部数据,从而能够在对应控件中显示数据包的十六进制形式内容
图1.7 protocol.h头文件中定义关键数据结构
在capthread.cpp文件中线程的run()定义中可以看到,在pcap_next_ex()函数所在的循环中,每捕获到一个数据包,首先为datapkt结构体分配堆空间, data指向该结构体。
接下来调用utilities.cpp文件中定义的analyze_frame函数用于协议数据包分析,data指针以及指向接收到的全部字节流数据的指针pkt_data作为其参数。
接下来,将分析后得到的数据结构存入vector指针所指向的容器中,从而主函数即可获取到数据包的内容以及解析后的结果,其代码如图1.8所示。
图1.8 将分析结果存入到容器中
在实现协议解析的过程中,由于需要所有的数据包的解析结果及其数据包二进制内容,所以将抓包线程中将其存入到两个容器中,容器是在MainWindow.cpp中定义的,并将其指针作为参数传递给新创建的线程。
在抓包线程的run函数中,是通过信号机制将捕获到的每一个数据包的捕获时间、源MAC地址、目的MAC地址、长度、协议类型、源IP地址、目的IP地址等信息传递给进程,由于这些信息已经存储在经过analyze_frame函数分析后得到的数据结构中,所以可以直接访问获取,代码如图1.9所示:
图1.9 获取数据包信息
在线程中,每抓到一个数据包并抓取下一个数据包之前发射信号:
emit addOneCaptureLine(timestr, srcMac, dstMac, pkt_len, protoType, srcIP, dstIP);
在MainWindow主进程中通过声明的槽函数updateTableWidget将其显示在QTableWidget控件中,并根据不同的数据包类型将每一条显示成不同的颜色。
MainWindow主进程中根据容器中的每一个数据包的分析结果结构体以同样的方式提取其中的内容从而生成协议分析树。根据另一个容器中的数据实现在点击某一个条目的时候将对对应的数据包内容显示在十六进制形式展示框内。显示协议分析树的代码在MainWindow.cpp文件中的showProtoTree函数中定义,显示十六进制形式数据在showHexData函数中定义,具体细节不再赘述。
4) 接下来介绍数据包协议分析的实现:
在上面的代码中可以看到,在抓包线程的运行函数中很关键的一步是调用utilities类中的analyze_frame函数进行协议分析,该类的函数声明如图1.10所示:
图1.10 协议分析函数声明
在protocol中定义的各层协议头结构体如下所示:
图1.11 MAC帧头
图1.12 ARP头定义
图1.13 IP包头的结构体定义
图1.14 TCP包头结构体定义
图1.15 udp以及icmp包头定义
在数据包协议解析的过程中,从底层协议开始向上分析,即在analyze_frame函数中实现,在函数analyze_frame的参数中pkt指向捕获到的数据包的字节流,data指向图1.7所示结构体,即保存数据包分析的结果,最后一个npacket类型的结构体指针用于保存数据包的统计信息,也就是,在协议分析的过程中对各类型数据包的数目进行统计。链路层协议分析函数中,很关键的语句如下所示:
struct _ethhdr ethh = (struct _ethhdr )pkt;
即,将链路层结构体类型指针指向pkt所指的字节流,这是一种类型转换,从而能够方便地访问数据包链路层各个字段的内容。接下来执行如图1.16所示代码:
图1.16 analyze_frame部分代码(1)
其中for循环将数据包中的链路层的目的MAC地址以及源MAC地址保存到data结构体中,同时将npacket结构体中的链路层数据包总数加1,最后一条语句是保存链路层中的类型字段值,需要注意的是,考虑网络字节序与主机字节序的不同,需要使用ntohs函数进行转换。接下来,需要根据上层协议的类型执行不同的分析函数,如下图1.17所示:
图1.17 analyze_frame部分代码(2)
因此,可以看到图1.10中在utilities头文件所声明的协议分析函数是从上往下调用的关系,比如在链路层中需要根据类型字段的值从而调用不同的分析函数,或者是analyze_ip,又或者是analyze_arp,需要注意的是传过去的参数需要加上链路层数据包的长度使得该参数指针刚好指向下一层协议的头部。
其他函数的具体定义可见文件utilities.cpp,原理相同。
不过,另外需要说明的一个难点是在analyze_tcp函数中如何分析HTTP协议,其代码如图1.18所示。可以看到,在该函数中,通过判断源端口或者目的端口是否为80来初步过滤出http协议内容,在if代码块中,通过查找tcp上层的应用层数据中是否存在http协议中的关键字段,比如GET,POST,HTTP/1.1, HTTP/1.0等,如果存在则为HTTP协议,将HTTP数据包的统计数目加1,并将data结构体中的数据包类型改为http,同时将应用层数据拷贝到data结构体的应用层头指针所指向的分配出来的堆空间中。
协议分析的总体流程图如图1.19所示。
图1.18 analyze_tcp函数中的部分代码
图1.19 以太网协议分析流程图
5) 接下来简略介绍数据包的保存以及重新读取:
通过WinPcap官方文档,可以知道,将数据包保存到脱机堆文件中需要使用到两个函数:
pcap_dump_open(): 该调用将打开一个堆文件,并将它关联到特定的接口上;
pcap_dump():数据包通过pcap_dump()函数写入到堆文件中。
数据包的读取也很容易能够实现,即首先调用pcap_createsrcstr()创建一个源字符串,然后调用pcap_open函数打开捕获文件,其中的参数即为上一步生成的源字符串。之后的步骤与捕获数据包相同。
在抓包线程创建的时候,MainWindow通过pcap_dump_open打开当前目录SavedData文件夹下面的以当前时间命名的pcap文件,如下图所示:
图1.20 打开以当前时间命名的.pcap格式文件
将该文件的dumpfile作为参数传递给抓包线程,抓包线程每次捕获到一个数据包的时候将该数据包的内容写入到该文件中,如下图1.21所示:
图1.21 保存数据包到脱机堆文件
从本地打开之前抓包获取的文件是在MainWindow.cpp中的slotopen()函数中定义的。其首先清空控件中的内容,以及用于存储抓取的数据包的分析结果以及二进制内容的Vector类型容器中的内容。接下来调用pcap_createsrcstr函数以及pcap_open函数,打开捕获文件,如图1.22所示。
之后的内容与从网卡上捕获数据包的步骤一致,即创建抓包线程,并通过pcap_next_ex函数捕获数据包。
图1.22 读取本地保存堆文件
6) 在系统实现的最后介绍如何嗅探器的第二个主要模块,即ARP欺骗实现中间人攻击:
ARP协议是地址解析协议,是一种将IP地址转化成物理地址的协议。正常情况下,如果受害者和网关要进行通信,首先使用arp协议进行对方的MAC地址获取,但是此时如果攻击者不断地向受害者发送ARP响应报文,告诉受害者网关的MAC地址为自己的地址,包的大致内容如下所示:
图1.23 ARP应答报文内容
在这里发送方ip是网关的ip,但是发送方MAC变成了攻击者的MAC地址,受害者不断地接受这个包,便会在自己的arp缓存中建立起错误的Ip与MAC地址的对应关系,从而认为攻击者就是网关,便实现了arp断网,如果此时攻击者开启IP路由转发或者通过程序手动为受害者转发数据包,便可以实现中间人攻击。
在实验中,我们新创建一个窗口用于进行中间人攻击,具体实现可以参考arpcheatdialog.cpp文件,当点击开始欺骗,程序读取用户输入的受害主机MAC地址与IP地址,同时新建一个线程arpcheatthread,在该线程中通过BuildArpPacket填充arp数据包中的各个字段的内容构成ARP应答包,同时在该线程的run函数中有如下代码。在该函数中通过构造
图1.24 run函数代码
发送给受害主机的arp应答包以及发送给网关的arp应答包从而实现双向欺骗。该线程重复不断地发送欺骗arp应答包,直到用户点击停止按钮。
由于自己的电脑无法开启中间路由转发功能,所以手动添加代码实现转发数据包,其代码定义在arpcheatdialog.cpp文件中的on_startBtn_clicked()函数中,如下图1.25所示:
图1.25 转发所有非arp包
转发线程sendpktthread运行函数中需要将捕获到的数据包的以太网帧头部中的MAC地址修改为正确的MAC地址之后再做转发,如图1.26所示:
图1.26 转发线程部分代码
图1.27 程序主界面
图1.28 中间人欺骗攻击界面
图1.29 ARP报头解析
图1.30 IP报文头部解析
图1.31 TCP报文头部解析
图1.32 UDP报文头部解析
图1.33 ICMP报文头部解析
图1.34 应用协议报文解析
图1.35 数据包过滤以及数据包统计
图1.36 数据包保存为1111.pcap
图1.37 数据包成功保存
在文件菜单中选择打开文件,读取数据包:
图1.38 打开数据包操作
ARP欺骗攻击测试:
当前主机的IP地址如图所示:
图1.39 攻击者IP地址
攻击主机的MAC地址为68-f7-28-d8-fb-6f
受害主机的IP地址为:222.20.105.81
受害主机的MAC地址为:00-0e-c6-d9-15-d1
网关的IP地址以及MAC地址如下图1.40所示:
图1.40 网关IP地址以及MAC
图1.41 ARP欺骗攻击
中间人攻击过程中,受害主机能够正常上网,同时,其arp表中的网关MAC地址修改为了攻击主机的MAC地址,如下图1.42所示:
图1.42 受害主网关MAC地址变为了攻击主机MAC地址
图1.43 中间人攻击嗅探到的数据包
至此,介绍了数据包嗅探器的实现以及中间人攻击,,收获还是很大的。
恭喜看到了最后,源码附上,希望学习的童鞋可以自行下载。
学习代码