您的位置:首页 > 理论基础 > 计算机网络

C/C++手动构造ARP包并发送至网络

2015-08-07 16:52 1146 查看

tip:由于没有及时整理,现在按之前上交的报告来截取内容,希望对有这个需求的朋友有所帮助。如果有错误请批评指出。。



所选题目:

1. 在熟悉ARP协议并了解Winpcap编程的前提下,构造ARP包,选择并打开网卡,将ARP包发送出去。

运行格式:程序名源IP地址目的IP地址目的MAC地址flag

源 IP 目的IP 源MAC FLAG

( flag=0: ARP请求flag=1: ARP应答)



1.要求及功能

按照题目要求,成功发送ARP包到网络( flag=0: ARP请求flag=1: ARP应答)

2.原理及方法

通过WinPcap的功能函数pcap_findalldevs_ex来获取设备列表,然后选择设备中的某个设备打开,此时准备好构造一个arp包使用打开的设备。Arp包采用结构体来构造,并且嵌套在以太网的结构体内。这里最需要说明的一点是,必须使用#pragma pack(1)命令使得内存对齐,否则编译器自动对齐,会在arp包的数据之间填充一些字节来自动对齐,这样会使得arp的数据包出错。MakeArp函数以一个_ARP * 作为参数,对它的变量进行赋值。这样就构造好了一个arp包。然后使用pcap_sendpacket函数来将数据包原样发送到网络中。最后关闭设备即可。

原理可以简述为如下图所示:







3.实验环境及平台搭建

实验环境:

语言:C/C++

编译器:VS2013

操作系统:win7 旗舰版64位

处理器:Intel(R) Core(TM) i5-3317U CPU @1.70GHz

内存:8G

搭建winpcap过程:

到winpcap官网http://www.winpcap.org/ 下载开发包





将其放在自己编译器对应文件夹内。或者单独放在自己工程目录下。编程的时候导入这些文件即可。这样就完成了基本的平台搭建问题。(更好的是根据平台重新编译程序)





4.实验结果及分析

测试:

本机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响应



通过抓包可以看到:



成功发送应答数据到网络。打开数据包可以查看到数据准确无误。







5.源程序核心函数说明

// 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__<<endl;
}
free(poid_data_);
PacketCloseAdapter(lpadapter);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: