carenobody
一天,一位同学打电话给我说,让我帮忙开发一个基于WinPcap工具的UDP发包工具,还特地叮嘱是基于WinPcap,不要原始套接字Raw Socket。而且,时间只有一个白天,它晚上就要,而打电话给我的时候,已经临近中午了。我一听,同学一场,那就举手之劳吧。
之前,自己就是用WinPcap开发过一些小程序,例如网卡遍历程序、Arp欺骗攻击程序等,所以,对WinPcap还算是熟悉。做这样的UDP发包程序,应该倒也不是那没事。
现在,为了配合这篇文章的讲解,我特地对这个程序重新开发。把程序的实现原理和过程整理成文档,分享给大家。
程序是使用VS2013开发的,程序中要使用WinPcap工具提供的功能函数的话,就需要将对VS2013进行配置,将WinPcap库导入到程序中,现在,就先介绍WinPcap环境的配置过程:
1.下载并安装WinPcap运行库http://www.winpcap.org/install/default.htm 。一些捕包软件会捆绑安装WinPcap,MentoHust也会附带WinPcap,这种情况下一般可以跳过此步。
2.下载WinPcap开发包http://www.winpcap.org/devel.htm ,解压到纯英文路径。
3.打开VS工程项目,在VS工程项目的 属性 —> VC++目录 中,包含目录 选项添加WpdPack\Include 目录,在 库目录 选项中添加 WpdPack\Lib 目录。
4.在 属性 —> C/C++ —> 预处理器 中,添加 WPCAP 和 HAVE_REMOTE 这两个宏定义。
5.在VS工程项目的 属性 —> 链接器 —> 输入 中,添加 wpcap.lib 和 ws2_32.lib 两个库。
这样,就可以将WinPcap所需的库文件包含到工程项目中了。接下来,我们就开始讲解UDP发包程序的实现过程。
首先,我们调用WinPcap函数pcap_findalldevs_ex获取设备列表信息,函数会将计算机上所有网卡设备信息返回到指向pcap_if_t结构体指针里。我们只需要对这个结构体指针进行遍历,就可以获取每个设备的详细信息。
// 获取网卡设备列表
if (-1 == pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, szErr))
{
ShowError("pcap_findalldevs_ex");
return;
}
程序将每个设备的信息显示在界面上,供用户选中使用哪个网卡进行操作。
我们使用 pcap_open 函数来设置并打开网卡,函数中第 1 个参数stAdapterInfo. szAdapterName,表示网卡的名称;第 2 个参数 65535,表示设置保留数据包的长度,65535即每个数据包的前65535字节长度的数据被保留在缓冲区中;第 3 个参数 PCAP_OPENFLAG_DATATX_UDP 表示使用UDP协议来处理数据传输;第 4 个参数 1 ,表示以毫秒为单位的读取超时时间。读取超时用来处理,捕获一个数据包后,读操作并不必需要立即返回的情况。但这可能等待一些时间以允许捕获更多的数据包,这样用户层的一次读操作就可从操作系统的内核中读取更多的数据包。第 5 个参数为 NULL;第 6 个参数 errbuf,可以获取返回的出错信息。
// 打开网卡
m_adhandle = pcap_open(stAdapterInfo.szAdapterName, 65535, PCAP_OPENFLAG_DATATX_UDP, 1, NULL, errbuf);
if (NULL == m_adhandle)
{
ShowError("pcap_open");
return;
}
我们根据源MAC地址、目的MAC地址、源IP地址、目的IP地址、源端口、目的端口、数据内容以及数据内容长度,构造UDP数据包。具体构造过程如下:
首先,构造以太网帧头。
memcpy((void*)FinalPacket, (void*)DestinationMAC, 6);
memcpy((void*)(FinalPacket + 6), (void*)SourceMAC, 6);
USHORT TmpType = 8;
memcpy((void*)(FinalPacket + 12), (void*)&TmpType, 2);
然后,构造IP头。
memcpy((void*)(FinalPacket + 14), (void*)"\x45", 1);
memcpy((void*)(FinalPacket + 15), (void*)"\x00", 1);
TmpType = htons(TotalLen);
memcpy((void*)(FinalPacket + 16), (void*)&TmpType, 2);
TmpType = htons(0x1337);
memcpy((void*)(FinalPacket + 18), (void*)&TmpType, 2);
memcpy((void*)(FinalPacket + 20), (void*)"\x00", 1);
memcpy((void*)(FinalPacket + 21), (void*)"\x00", 1);
memcpy((void*)(FinalPacket + 22), (void*)"\x80", 1);
memcpy((void*)(FinalPacket + 23), (void*)"\x11", 1);
memcpy((void*)(FinalPacket + 24), (void*)"\x00\x00", 2);
memcpy((void*)(FinalPacket + 26), (void*)&SourceIP, 4);
memcpy((void*)(FinalPacket + 30), (void*)&DestIP, 4);
接着,构造UDP头。
TmpType = htons(SourcePort);
memcpy((void*)(FinalPacket + 34), (void*)&TmpType, 2);
TmpType = htons(DestinationPort);
memcpy((void*)(FinalPacket + 36), (void*)&TmpType, 2);
USHORT UDPTotalLen = htons(UserDataLen + 8);
memcpy((void*)(FinalPacket + 38), (void*)&UDPTotalLen, 2);
memcpy((void*)(FinalPacket + 42), (void*)UserData, UserDataLen);
要注意的是,IP校验和以及UDP的校验和计算。这样,我们就可以成功构造UDP的数据包,接下来,就可以对数据包进行发送。
我们使用 pcap_sendpacket 函数发送单个数据包,第 1 个参数表示打开网卡的时候获取的句柄;第 2 个参数就是发送的数据内容;第 3 个参数表示发送数据内容的长度。注意,缓冲数据将直接发送到网络,而不会进行任何加工和处理。这就意味着应用程序需要创建一个正确的协议首部,来使这个数据包更有意义。
// 发送数据包
if (0 != pcap_sendpacket(m_adhandle, FinalPacket, (UserDataLen + 42)))
{
char *szErr = pcap_geterr(m_adhandle);
ShowError(szErr);
return;
}
经过上面 4 步操作,便可以实现使用 WinPcap 发送 UDP 数据包了。要注意IP校验和以及UDP校验和的计算,这两个的值注意不要算错。
我们以管理员权限运行程序,并对一个UDP程序发包,观察UDP程序能否接收到我们发包程序发送的UDP数据包。其中,UDP程序使用的是《Socket通信之UDP通信小程序》这篇文章中实现的UDP程序。
经过测试结果,UDP程序成功接收到UDP数据包。
这个程序是基于WinPcap实现的,所以,可能有很多人之前还没有接触过WinPcap方面的知识,所以,一下子使用和理解起来就比较困难。关键是耐下心来,把程序中调用到的不理解的WinPcap函数,在网上查找说明,对函数理解清晰。
其中,要注意的是,使用WinPcap工具,需要有管理员权限。
参考自《Windows黑客编程技术详解》一书
keyboard_arrow_left上一篇 : C语言/ARM嵌入式-V4L视频监控系统 分布式并行计算 : 下一篇keyboard_arrow_right