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

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函数来将数据包原样发送到网络中。最后关闭设备即可。


 

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

C/C++手动构造ARP包并发送至网络_第1张图片



3.实验环境及平台搭建

实验环境:

语言:C/C++

编译器:VS2013

操作系统:win7 旗舰版64

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

内存:8G

 

搭建winpcap过程

 

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

C/C++手动构造ARP包并发送至网络_第2张图片

C/C++手动构造ARP包并发送至网络_第3张图片

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




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请求

C/C++手动构造ARP包并发送至网络_第4张图片

通过wareshark抓包,可以看到该arp已经成功发送出去,并且立马收到两条arp应答

C/C++手动构造ARP包并发送至网络_第5张图片

打开该数据包,查看数据:

C/C++手动构造ARP包并发送至网络_第6张图片

可以清晰地看到,所填充数据准确无误。符合数据包格式规范要求。


测试数据第二组(reply测试)

 

测试数据:172.18.105.25 172.18.106.232 047d7bcdc702 1    //发送ARP响应


通过抓包可以看到:


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


C/C++手动构造ARP包并发送至网络_第7张图片



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__<


 


你可能感兴趣的:(个人方案)