ARP报文发送的可视化实现
一、安装VS2013,下载wpdpack,为VS2010配置WinpCap环境:
⑴首先在View中选择Property Manager,然后展开工程,再展开Debug|Win32 ,接着右击 Mircrosoft.Cpp.Win32.user选择Properties(此处设置全局有效)
⑵之后分三步:
①设置环境目录
在VC++ Directiories中 Include Directories和Library Directories中添加路径。
假如将wpdpack放到c盘。则:
Include Directories:c:/wpdpack/Include;
Library Directories:c:/wpdpack/Lib;
注意分号!
②设置编译条件
C/C++下Preprocessor(预处理器)的Preprocessor Definitions(预处理定义)
添加WPCAP;HAVE_REMOTE;
注意分号,每个预定义符用”;分开。
③设置链接器
Linker(链接器)下的Input(输入)中的Additional Dependencies(附加依赖项)中添加:
wpcap.lib;ws2_32.lib;(注意分号)
二、使用mfc实现可视化界面:
目的视图:
1、新建项目:
选择Visual C++ -> MFC -> MFC应用程序 –>修改项目名称test1 -> 下一步 –> 选择基于对话框 -> 完成
2、相关视图介绍:
①解决方案资源管理器:可以看见所有的头文件和源(.cpp)文件,此程序只需用到test1Dlg.cpp和test1Dlg.h。另外MFC中main函数被封装起来了,并且我们也并不需要知道main函数在哪,因为我们希望实现的功能通常是触发某个控件,这个控件的触发函数为我们实现,并不需要将代码写到main函数内。
②类视图:在类视图中,我们可以看到三个类,此处只需要用到Ctest1Dlg,点击类名,可以在下方很方便的找到类中的函数和变量。
③属性管理器:在为VS配置全局环境时会用到。
④资源视图:在test1 -> test1.rc -> Dialog -> IDD_TEST1_DIALOG可以看见自己设计的可视化界面。
⑤属性:处理控件时,在此处修改控件的属性。
3、相关控件的使用:
①静态文本框Static Text:caption修改文字内容
②编辑框Edit Control:Read Only设置是否只读
③IP地址编辑框IP Address Control:用法与普通编辑框一样,只不过读取内容时可以使用GetAddress()函数。
④组合框Combo Box:Type分为三种:简易(Simple)组合框、下拉式(Dropdown)组合框和下拉列表式(Drop List)组合框,这里选择Drop List。
三、详细设计:
1.ARP发送中重要方法:
填充ARP包方法的流程图:
四、添加函数:
1、 更改各控件ID,并给部分空间增加变量。Combo Box的默认ID更改为IDC_NIC_COMBO,增加控件变量m_comboNic;目的MAC地址的编辑框ID设为IDC_DESTI_MACADDRESS_EDIT,添加控件变量为m_editDesti;目的IP地址的编辑框ID设为IDC_DESTI_IPADDRESS,添加控件变量为m_ipaddressSesti;源MAC地址的编辑框ID设为IDC_SOURCE_MACADDRESS_EDIT,添加控件变量为m_editSource;源IP地址的编辑框ID设为IDC_SOURCE_IPADDRESS,添加控件变量为m_ipaddressSource;结果显示编辑框ID设为IDC_RESULT_EDIT,添加控件变量为m_editResult。
2、 依据ARP包的结构定义结构体,并将需要引入的头文件、结构体和相关常量的定义写入test1Dlg.h文件中。
#include "stdafx.h" #include <pcap.h> #define ETH_ARP 0x0806 //以太网帧类型表示后面数据的类型,对于ARP请求或应答来说,该字段的值为x0806 #define ARP_HARDWARE 1 //硬件类型字段值为表示以太网地址 #define ETH_IP 0x0800 //协议类型字段表示要映射的协议地址类型值为x0800表示IP地址 #define ARP_REQUEST 1 //ARP请求 #define ARP_RESPONSE 2 //ARP应答 //14字节以太网首部 struct EthernetHeader { u_char DestMAC[6]; //目的MAC地址 6字节 u_char SourMAC[6]; //源MAC地址 6字节 u_short EthType; //上一层协议类型,如0x0800代表上一层是IP协议,0x0806为arp 2字节 }; //28字节ARP帧结构 struct ArpHeader { unsigned short hdType; //硬件类型 unsigned short proType; //协议类型 unsigned char hdSize; //硬件地址长度 unsigned char proSize; //协议地址长度 unsigned short op; //操作类型,ARP请求(1),ARP应答(2),RARP请求(3),RARP应答(4)。 u_char smac[6]; //源MAC地址 u_char sip[4]; //源IP地址 u_char dmac[6]; //目的MAC地址 u_char dip[4]; //目的IP地址 }; //定义整个arp报文包,总长度42字节 struct ArpPacket { EthernetHeader ed; ArpHeader ah; };
3、 在Ctest1Dlg::OnInitDialog()中写入初始化的界面的代码。
/* 获取本机设备列表 */ if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) { m_comboNic.AddString(_T("Error in pcap_findalldevs!")); exit(1); } /* 打印列表 */ for (d = alldevs; d; d = d->next) { ++i; if (d->description) m_comboNic.AddString(LPCTSTR(CString(d->name))); else m_comboNic.AddString(_T("(No description available)")); } if (i == 0) { m_comboNic.AddString(_T("No interfaces found! Make sure WinPcap is installed.")); return -1; } // 默认选择第一项 m_comboNic.SetCurSel(0); //目的MAC地址编辑框中默认显示"00-00-00-00-00-00" SetDlgItemText(IDC_EDIT1, _T("00-00-00-00-00-00"));
4、 在确认按钮的响应函数内添加以下代码:
// 获取组合框控件的列表框中选中项的索引 inum = m_comboNic.GetCurSel(); /* 跳转到选中的适配器 */ for (d = alldevs, i = 0; i<= inum - 1; d = d->next, i++); /* 打开设备 */ if ((adhandle = pcap_open(d->name, // 设备名 65536, // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容 PCAP_OPENFLAG_PROMISCUOUS, // 混杂模式 1000, // 读取超时时间 NULL, // 远程机器验证 errbuf // 错误缓冲池 )) == NULL) { m_editResult.SetWindowText(_T("Unable to open the adapter. \rIt is not supported by WinPcap")); /* 释放设备列表 */ pcap_freealldevs(alldevs); exit(-1); } unsigned char sendbuf[42]; //arp包结构大小,42个字节 BYTE IPByte[4]; m_ipaddressSesti.GetAddress(IPByte[0], IPByte[1], IPByte[2], IPByte[3]); BYTE IPByte2[4]; m_ipaddressSource.GetAddress(IPByte2[0], IPByte2[1], IPByte2[2], IPByte2[3]); CString str; char *c; BYTE mac[7] = { 0 };//源MAC地址 m_editSource.GetWindowText(str);//获取字符串 c = cs2ca(str); //CString转为char* sscanf_s(c, "%h2x-%h2x-%h2x-%h2x-%h2x-%h2x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); EthernetHeader eh; ArpHeader ah; if (IsDlgButtonChecked(IDC_RADIO1) == 1) { //请求报文 .....//相应代码 } else if (IsDlgButtonChecked(IDC_RADIO2) == 1) { //应答报文 .....//相应代码 } //构造一个ARP请求 memset(sendbuf, 0, sizeof(sendbuf)); //ARP清零 memcpy(sendbuf, &eh, sizeof(eh)); memcpy(sendbuf + sizeof(eh), &ah, sizeof(ah)); //如果发送成功 if (pcap_sendpacket(adhandle, sendbuf, 42) == 0) { m_editResult.SetWindowText(_T("\nPacketSend succeed\n")); } else { m_editResult.SetWindowText(_T("\nPacketSendPacket in getmine Error\n")); } /* 释放设备列表 */ pcap_freealldevs(alldevs);
其中主要遇到的困难是,如何获取三个编辑框中用户输入的地址,将这种Cstring格式的地址转为u_char格式的字符数组。IP ADDRESS CONTROL可以使用其函数GetAddress(IPByte[0], IPByte[1], IPByte[2], IPByte[3]);获得。而MAC地址的编辑框获得的字符串就不太好转换了。碰过好多好多坑,试过好多好多方法之后,最终找到一种可行的方法:先把Cstring字符串利用函数转换为char*型,再使用sscanf将每两位代表十六进制的字符存在BYTE mac[7]数组中,但是貌似在vs2015中无法使用这个方法。
四、运行效果:
传送门:
1.鸡啄米VS2010/MFC教程
2.WinPcap实战(一)——发送ARP包。
3.用sscanf转换MAC字符串为BYTE数组时遇到的问题