1. 在熟悉ARP协议并了解Winpcap编程的前提下,构造ARP包,选择并打开网卡,将ARP包发送出去。
运行格式:程序名源IP地址目的IP地址目的MAC地址flag
源 IP 目的IP 源MAC FLAG
( flag=0: ARP请求flag=1: ARP应答)
按照题目要求,成功发送ARP包到网络( flag=0: ARP请求flag=1: ARP应答)
2.原理及方法
通过WinPcap的功能函数pcap_findalldevs_ex来获取设备列表,然后选择设备中的某个设备打开,此时准备好构造一个arp包使用打开的设备。Arp包采用结构体来构造,并且嵌套在以太网的结构体内。这里最需要说明的一点是,必须使用#pragma pack(1)命令使得内存对齐,否则编译器自动对齐,会在arp包的数据之间填充一些字节来自动对齐,这样会使得arp的数据包出错。MakeArp函数以一个_ARP * 作为参数,对它的变量进行赋值。这样就构造好了一个arp包。然后使用pcap_sendpacket函数来将数据包原样发送到网络中。最后关闭设备即可。
实验环境:
语言:C/C++
编译器:VS2013
操作系统:win7 旗舰版64位
处理器:Intel(R) Core(TM) i5-3317U CPU @1.70GHz
内存:8G
搭建winpcap过程:
到winpcap官网http://www.winpcap.org/ 下载开发包
将其放在自己编译器对应文件夹内。或者单独放在自己工程目录下。编程的时候导入这些文件即可。这样就完成了基本的平台搭建问题。(更好的是根据平台重新编译程序)
测试:
本机ip : 172.18.105.25
目的机ip : 172.18.106.232
本机mac : 20-6a-8a-d1-52-b9
Tip:
本机 ip和mac 可通过函数得到,但由于要在控制台下启动,并且按照老师给的格式,所以最好提前准备好Ip ,可使用ipconfig /all 查看本机ip 。本机mac地址自动填充。
测试参数格式:
源ip 目的ip mac 地址 flag
(flag = 0:mac地址在程序中不采用 ,flag!=0 : mac地址被填充为目的mac地址)
测试数据第一组(request测试):
测试数据:172.18.105.25 172.18.106.232 206a8ad152b9 0 //发送ARP请求
通过wareshark抓包,可以看到该arp已经成功发送出去,并且立马收到两条arp应答。
打开该数据包,查看数据:
可以清晰地看到,所填充数据准确无误。符合数据包格式规范要求。
测试数据第二组(reply测试):
测试数据:172.18.105.25 172.18.106.232 047d7bcdc702 1 //发送ARP响应
通过抓包可以看到:
成功发送应答数据到网络。打开数据包可以查看到数据准确无误。
// to make an arp protocal
void MakeArp(_ARP * _arp, unsigned char* source_mac_addr, unsigned long source_ip_addr, unsigned long dest_ip_addr);
函数功能:为一个ARP包填充参数
返回值:void
参数说明: _arp:需要填充参数的arp结构体指针
source_mac_addr:源mac地址
source_ip_addr:源ip地址
dest_ip_addr:目的ip地址
void GetDevName(ULONG source_ip, ULONG dest_ip, PUCHAR dest_mac, USHORT flag)
函数功能:得到设备参数,并且发送数据包等
返回值:void
参数说明: source_ip:源ip地址
dest_ip:目的ip地址
dest_mac:目的mac地址
flag:数据包操作码(0 : request , 1 : reply)所有非0即为1
int pcap_findalldevs_ex(char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf);
函数功能:得到设备列表
返回值:int
参数说明:
引用官方说明:
source,: a char* buffer that keeps the 'source localtion', according to the new WinPcap syntax. This source will be examined looking for adapters (local or remote) (e.g. source can be 'rpcap://' for local adapters or 'rpcap://host:port' for adapters on a remote host) or pcap files (e.g. source can be 'file://c:/myfolder/').
The strings that must be prepended to the 'source' in order to define if we want local/remote adapters or files is defined in the new Source Specification Syntax .
auth,: a pointer to a pcap_rmtauth structure. This pointer keeps the information required to authenticate the RPCAP connection to the remote host. This parameter is not meaningful in case of a query to the local host: in that case it can be NULL.
alldevs,: a 'struct pcap_if_t' pointer, which will be properly allocated inside this function. When the function returns, it is set to point to the first element of the interface list; each element of the list is of type 'struct pcap_if_t'.
errbuf,: a pointer to a user-allocated buffer (of size PCAP_ERRBUF_SIZE) that will contain the error message (in case there is one).
voidpcap_freealldevs(pcap_if_t *);
函数功能:释放设备列表
返回值:void
参数说明:
参数1:所要释放的设备列表指针
pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf);
函数功能:关闭所打开的设备
返回值:void
参数说明:
Source: 需要打开的设备名
Snaplen:所能捕获的最大字节数
Flags: 打开模式
Read_timeout:读取超时时间
Auth:引用pcap_findalldevs_ex对该参数的解释
Errbuf: 保存出错的信息
本程序调用示例:
//open the choice
auto pcap = pcap_open(
tmp_->name,
65536,
PCAP_OPENFLAG_PROMISCUOUS, //以混杂模式打开网卡
1000,
NULL,
error);
voidpcap_close(pcap_t *);
函数功能:关闭所打开的设备
返回值:void
参数说明:
参数1:需要关闭的设备指针
// to get the local mac addr
void GetLocalMac(UCHAR * _u_char, PCHAR _adapter);
函数功能:获得本地mac地址
返回值:void
参数说明:_u_char: 用于保存mac地址的指针
_adapter: 所要得到的mac地址所属设备名
// to parse the mac addr
PUCHAR ParseMac(char * mac);
函数功能:将char * 的mac地址转换为 UCHAR 类型的mac地址
返回值:PUCHAR 存放转换后的mac地址指针
参数说明:
mac:待转换的char类型mac地址数组
//begin to send
intpcap_sendpacket(pcap_t *, const u_char *, int);
函数功能:将数据包原样发送到网络中
返回值:int
参数说明:
参数1:所打开的设备
参数2:待发送的数据
参数3:数据块大小
程序中调用示例:
pcap_sendpacket(pcap, (UCHAR*)&arp_, sizeof(arp_));
附上源码:
程序源代码:
// OpenDev.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include"iostream"
#include "iomanip"
#define HAVE_REMOTE
#include"pcap.h"
#include "packet32.h"
#include "ntddndis.h"
#include "stdlib.h"
#include "memory"
#pragma pack(1) //memory alignment
#pragma comment(lib,"wpcap.lib")
#pragma comment(lib,"Packet.lib")
#pragma comment(lib,"ws2_32.lib")
using std::cout;
using std::endl;
using std::cin;
using std::setfill;
using std::setw;
//
struct _ARP_HEAD{
u_short hardware_type; //硬件类型 0x0001
u_short protocal_type; //协议类型 0x0800
u_char hardware_addr_len; //硬件地址长度 06
u_char protocal_addr_len; //协议地址长度 04
USHORT operation_field; //操作字段 01 request , 02 response
UCHAR source_mac_addr[6]; //源mac地址 will be filled in runtime
ULONG source_ip_addr; //源ip地址 localhost
UCHAR dest_mac_addr[6]; //目的max地址 00:00:00:00:00:00
ULONG dest_ip_addr; //目的ip地址
};
//
struct _ETHER_HEAD{
UCHAR dest_mac_addr[6]; //目的 mac 地址
UCHAR source_mac_addr[6]; //源 mac 地址
USHORT type; //帧类型
};
//
struct _ARP{
_ETHER_HEAD eh;
_ARP_HEAD ah;
char padding[18]; // to make sure the sizeof(BYTES) >= 60
};
// to make an arp protocal
void MakeArp(_ARP * _arp, unsigned char* source_mac_addr, unsigned long source_ip_addr, unsigned long dest_ip_addr);
void MakeArp(_ARP * _arp, unsigned char* source_mac_addr, unsigned char* dest_mac_addr, unsigned long source_ip_addr, unsigned long dest_ip_addr, USHORT flag);
// to get devices' name and do more things in this function
void GetDevName(ULONG,ULONG,PUCHAR,USHORT);
// to get the local mac addr
void GetLocalMac(UCHAR * _u_char, PCHAR _adapter);
// to parse the mac addr
PUCHAR ParseMac(char * mac);
int main(int argc, char* argv[])
{
if (argc<1)
{
return 0;
}
PUCHAR mac_addr = ParseMac(argv[3]);
GetDevName(inet_addr(argv[1]),inet_addr(argv[2]),mac_addr,atoi(argv[4]));
free(mac_addr);
system("pause");
return 0;
}
//to parse the mac addr
PUCHAR ParseMac(char * mac)
{
int i = strlen(mac);
PUCHAR TMP = (PUCHAR)malloc(i / 2);
char tmp[2] = { 0 };
_strlwr(mac);
int count = 0;
int Counttmp = 0;
int changemod = 0;
while (count < i)
{
if (mac[count] >= 'a'&& mac[count] <= 'f')
tmp[changemod] = mac[count] - 0x60 + 9;
else if (mac[count] >= '0' && mac[count] <= '9')
{
tmp[changemod] = mac[count] - 0x30;
}
else break;
++changemod;
++count;
if (2 == changemod)
{
changemod = 0;
TMP[Counttmp] = tmp[0] * 16 + tmp[1];
++Counttmp;
}
}
return TMP;
}
/**
* Print the device info
*
*/
void GetDevName(ULONG source_ip, ULONG dest_ip, PUCHAR dest_mac, USHORT flag)
{
pcap_if_t * alldevs;
void packet_backFUNC(u_char *param, const struct pcap_pkthdr *header, const u_char * pkt_data);
int count = 0;
char source[PCAP_BUF_SIZE + 1] = { "rcap://\0" };
char error[PCAP_BUF_SIZE + 1] = { 0 };
//print all the device we had found
if (pcap_findalldevs_ex(source,NULL,&alldevs,error) == -1)
{
cout << " Can't find the device ! \n";
return;
}
else
{
// this part is to display the info of NIC ,now we don't need it
// 由于题目没有要求查看设备信息,所以注释掉这一部分,如果需要查看设备信息,可以打开这部分注释
for (auto d = alldevs; d; d = d->next)
{
++count;
/*cout << "Number : " << count << endl
<< "Name :" << d->name << endl
<< "Description : " << d->description << endl;*/
//for (auto tmp = d->addresses; tmp;tmp=tmp->next)
//{
// /*cout << "Address Family : #" << (int)tmp->addr->sa_family << endl << endl;*/
// if (tmp->addr->sa_family == AF_INET)
// {
// /* cout << "Address Family Name : AF_INET" << endl
// << "IP addr : " << inet_ntoa(((struct sockaddr_in*)tmp->addr)->sin_addr) << endl
// << "Net mask : " << inet_ntoa(((struct sockaddr_in*)tmp->netmask)->sin_addr) << endl
// << "BroadAddr : " << inet_ntoa(((struct sockaddr_in*)tmp->broadaddr)->sin_addr) << endl;*/
// }
// else{
// /*cout << "Address Family Name : Unknown\n";*/
// }
//}
/*cout.fill('-'); cout.width(79);
cout << endl << "" << endl << endl;*/
}
}
if (count == 0)
{
cout << "No Interface has found ,make sure the WinPcap has installed \n";
}
// to choose an interface
int ntmp_ = 1;
if (ntmp_ <1 || ntmp_ >count)
{
cout << "The number out of range \n";
pcap_freealldevs(alldevs);
return;
}
//jump to the choice
pcap_if_t *tmp_ = alldevs;
for (int nNumber = 1; nNumber < ntmp_; nNumber++, tmp_ = tmp_->next);
//open the choice
auto pcap = pcap_open(
tmp_->name,
65536,
PCAP_OPENFLAG_PROMISCUOUS, //以混杂模式打开网卡
1000,
NULL,
error);
if (pcap == NULL)
{
cout << "Error has occured at open the device\n FileName : " << __FILE__ << "\nLine : " << __LINE__ << endl;
pcap_freealldevs(alldevs);
return;
}
// get an input of dest IP
// to get the mac addr
UCHAR macbuff[10] = { 0 };
// "&(tmp_->name)[8]" is the beginning of a device's name with out "rpcap://" so that we can use PacketOpenAdapter successfully
GetLocalMac(macbuff, &(tmp_->name)[8]);
//create an arp
_ARP arp_;
MakeArp(&arp_,
macbuff,
dest_mac,
source_ip,
dest_ip,
flag
);
//begin to send
pcap_sendpacket(pcap, (UCHAR*)&arp_, sizeof(arp_));
//free the devices list
pcap_freealldevs(alldevs);
pcap_close(pcap);
cout << "ARP has been sent correctly !\n";
}
// #define UNDO
void packet_backFUNC(u_char *param, const struct pcap_pkthdr *header, const u_char * pkt_data)
{
// TODO
}
void MakeArp(_ARP * _arp, unsigned char* source_mac_addr, unsigned long source_ip_addr, unsigned long dest_ip_addr)
{
// set the ethernet info
memset(_arp->eh.dest_mac_addr, 0xff, 6);
memcpy(_arp->eh.source_mac_addr, source_mac_addr, 6);
_arp->eh.type = htons(0x0806);
// set the arphead info
_arp->ah.hardware_addr_len = 6;
_arp->ah.hardware_type = htons(0x0001);
_arp->ah.operation_field = htons(1);
_arp->ah.protocal_addr_len = 4;
_arp->ah.protocal_type = htons(0x0800);
_arp->ah.source_ip_addr = source_ip_addr;
memcpy(_arp->ah.source_mac_addr, source_mac_addr, 6);
_arp->ah.dest_ip_addr = dest_ip_addr;
memset(_arp->ah.dest_mac_addr, 0, 6);
//Zero the padding
ZeroMemory(_arp->padding, 18);
}
void MakeArp(_ARP * _arp, unsigned char* source_mac_addr,unsigned char* dest_mac_addr, unsigned long source_ip_addr, unsigned long dest_ip_addr, USHORT flag)
{
if (flag == 0)
{
MakeArp(_arp, source_mac_addr, source_ip_addr, dest_ip_addr);
return;
}
else
{
MakeArp(_arp, source_mac_addr, source_ip_addr, dest_ip_addr);
memcpy(_arp->eh.dest_mac_addr, dest_mac_addr, 6);
memcpy(_arp->eh.source_mac_addr, source_mac_addr, 6);
memcpy(_arp->ah.source_mac_addr, source_mac_addr, 6);
memcpy(_arp->ah.dest_mac_addr, dest_mac_addr, 6);
_arp->ah.operation_field = htons(2);
return;
}
}
// to get the local mac
#define ADAPTER_NAME_SIZE 512
void GetLocalMac(UCHAR * _u_char,PCHAR _adapter)
{
LPADAPTER lpadapter;
lpadapter = PacketOpenAdapter(_adapter);
if (!lpadapter || (INVALID_HANDLE_VALUE == lpadapter->hFile))
{
cout << "Unable to open the adapter,Error Code:" << GetLastError();
return;
}
PPACKET_OID_DATA poid_data_;
poid_data_ = (PPACKET_OID_DATA)malloc(6 + sizeof(PACKET_OID_DATA));
poid_data_->Oid = OID_802_3_CURRENT_ADDRESS;
poid_data_->Length = 6;
ZeroMemory(poid_data_->Data, 6);
auto status = PacketRequest(lpadapter, FALSE, poid_data_);
if (status)
{
memcpy(_u_char, poid_data_->Data, 6);
}
else
{
cout << "An error has occured at file : " << __FILE__ << " line : " << __LINE__<