请选择 进入手机版 | 继续访问电脑版
MSIPO技术圈 首页 IT技术 查看内容

windows下使用arp 协议

2023-07-13

/
//自动扫描局域网存活主机

本程序是利用arp协议去获取局域网中的存活主机

arp协议概述

地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到局域网络上的所有主机,并接收返回消息,以此确定目标的物理地址

定义数据结构

这个数据结构位ip和mac对应
typedef struct s_ip_fname
{
	//一个网卡可能有多个IP地址
	std::vector<std::string> ips;
	std::string mac;
}s_ip_fname;
下面的数据结构是为了存储mac地址的
typedef struct list_nim
{
	atomic<uint32_t> v_n = 0;
	std::map<std::string,std::string> nim;
	std::mutex  mutex;
	void clear()
	{
		nim.clear();
	}
	void push(string &ip, string &mac)
	{
		mutex.lock();
		nim[mac] = ip;
		mutex.unlock();
	}
}list_nim;

获取IP地址

获取到本机的IP地址,本例子是可以获取网卡的多个IP地址

std::string main_ip()
{
	std::vector<s_ip_fname> ip_mac_vector;
	get_ipfname(ip_mac_vector);
	std::string ip;
	if (ip_mac_vector.size() == 0)
		return ip;
	auto it = ip_mac_vector.begin();
	if(it->ips.size()>0)
		ip = it->ips[0];
	return ip;
}

其中获取get_ipfname 获取ip地址列表,写成了一个静态函数,我们无论如何都要想到,一个主机不一定只有一个网卡,一个网卡不一定只有一个ip

static int get_ipfname(std::vector<s_ip_fname>& ff)
{
#define ADDR(x) pIpAdapterInfo->Address[x]
	PIP_ADAPTER_INFO pIpAdapterInfo = new IP_ADAPTER_INFO();
	unsigned long stSize = sizeof(IP_ADAPTER_INFO);
	int nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
	DWORD netCardNum = 0;
	GetNumberOfInterfaces(&netCardNum);
	int IPnumPerNetCard = 0;
	if (ERROR_BUFFER_OVERFLOW == nRel)
	{
		delete pIpAdapterInfo;
		pIpAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[stSize];
		nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
		//return -1;
	}
	if (ERROR_SUCCESS == nRel)
	{
		//maybe we have >1 network card
		while (pIpAdapterInfo)
		{
			s_ip_fname ipf;
			switch (pIpAdapterInfo->Type)
			{
			case MIB_IF_TYPE_OTHER:
				//cout << "OTHER" << endl; 
				break;
			case MIB_IF_TYPE_ETHERNET:
				//cout << "ETHERNET" << endl; 
				break;
			case MIB_IF_TYPE_TOKENRING:
				//cout << "TOKENRING" << endl; 
				break;
			case MIB_IF_TYPE_FDDI:
				//cout << "FDDI" << endl; 
				break;
			case MIB_IF_TYPE_PPP:
				//cout << "PPP" << endl; 
				break;
			case MIB_IF_TYPE_LOOPBACK:
				//cout << "LOOPBACK" << endl; 
				break;
			case MIB_IF_TYPE_SLIP:
				//cout << "SLIP" << endl; 
				break;
			default:
				//cout << "" << endl; 
				break;
			}
			char buffer[128];
			sprintf(buffer, "%02X-%02X-%02X-%02X-%02X-%02X",
				//sprintf(buffer, "%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X",
				ADDR(0), ADDR(1), ADDR(2), ADDR(3), ADDR(4), ADDR(5));
			ipf.mac = buffer;
			IPnumPerNetCard = 0;
			//可能网卡有多IP
			IP_ADDR_STRING* pIpAddrString = &(pIpAdapterInfo->IpAddressList);
			do
			{
				ipf.ips.push_back(pIpAddrString->IpAddress.String);
				pIpAddrString = pIpAddrString->Next;

			} while (pIpAddrString);
			ff.push_back(ipf);
			pIpAdapterInfo = pIpAdapterInfo->Next;
		}
	}
	//释放内存空间
	if (pIpAdapterInfo)
	{
		delete pIpAdapterInfo;
	}
	return 0;
}

线程函数

改函数启动一个线程,去发送arp请求

	void start_thread(int tn,uint32_t ulNetName)
	{
		thread th[256];
		for (int i = 0; i < tn; i++)
		{
			th[i] = thread([this,ulNetName] {
				while (1)
				{
					uint32_t x = v_number--;
					uint32_t n = htonl(ulNetName + x + 1);
					this->send_arp(n);
					if (v_number < 0)
						break;
				}
			});
			//th.detach();
		}
		for (int i = 0; i < tn; i++)
			th[i].join();
	}

send arp 函数实际上就是调用windows的辅助函数SendARP

	void send_arp(uint32_t naddr_f)
	{
		//for (uint32_t t = naddr_f; t < naddr_t; t++)
		//{
			struct in_addr in;
			in.S_un.S_addr = naddr_f;
			string ip = inet_ntoa(in);
			std::cout << "scan:";
			std::cout << ip.c_str() << endl;
			HRESULT result;
			ULONG c[2] = { 0 }, len = 6;
			//unsigned int ipn = htonl(inet_addr(ip.c_str()));
			//发送ARP包
			result = SendARP(naddr_f, NULL, c, &len);

			if (result == NO_ERROR)
			{
				BYTE *g = (BYTE *)&c;
				if (len)
				{
					string strmac(32, 0);
					sprintf(const_cast<char*>(strmac.c_str()),
						"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",
						g[0], g[1], g[2], g[3], g[4], g[5]);
					v_lnim.push(strmac, ip);

				}
			}
		//}
		//return 0;
	}

目标

//自动扫描局域网存活主机
程序清单,复制可用
/
//自动扫描局域网存活主机
//优点:不用输入ip
//缺点:如果遇到主机多IP只能扫描其中一个

#include <winsock2.h>
#include <stdio.h>
#include <iphlpapi.h>

#pragma comment (lib,"ws2_32.lib")  
#pragma comment (lib,"iphlpapi.lib")

#include <iostream>
#include <list>
#include <windows.h>
#include <thread>
#include <atomic>
#include <mutex>
#include <map>
#include <vector>
using namespace std;

typedef struct s_ip_fname
{
	//一个网卡可能有多个IP地址
	std::vector<std::string> ips;
	std::string mac;
}s_ip_fname;

typedef struct list_nim
{
	atomic<uint32_t> v_n = 0;
	std::map<std::string,std::string> nim;
	std::mutex  mutex;
	void clear()
	{
		nim.clear();
	}
	void push(string &ip, string &mac)
	{
		mutex.lock();
		nim[mac] = ip;
		mutex.unlock();
	}
}list_nim;



static int get_ipfname(std::vector<s_ip_fname>& ff)
{
#define ADDR(x) pIpAdapterInfo->Address[x]
	PIP_ADAPTER_INFO pIpAdapterInfo = new IP_ADAPTER_INFO();
	unsigned long stSize = sizeof(IP_ADAPTER_INFO);
	int nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
	DWORD netCardNum = 0;
	GetNumberOfInterfaces(&netCardNum);
	int IPnumPerNetCard = 0;
	if (ERROR_BUFFER_OVERFLOW == nRel)
	{
		delete pIpAdapterInfo;
		pIpAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[stSize];
		nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
		//return -1;
	}
	if (ERROR_SUCCESS == nRel)
	{
		//maybe we have >1 network card
		while (pIpAdapterInfo)
		{
			s_ip_fname ipf;
			switch (pIpAdapterInfo->Type)
			{
			case MIB_IF_TYPE_OTHER:
				//cout << "OTHER" << endl; 
				break;
			case MIB_IF_TYPE_ETHERNET:
				//cout << "ETHERNET" << endl; 
				break;
			case MIB_IF_TYPE_TOKENRING:
				//cout << "TOKENRING" << endl; 
				break;
			case MIB_IF_TYPE_FDDI:
				//cout << "FDDI" << endl; 
				break;
			case MIB_IF_TYPE_PPP:
				//cout << "PPP" << endl; 
				break;
			case MIB_IF_TYPE_LOOPBACK:
				//cout << "LOOPBACK" << endl; 
				break;
			case MIB_IF_TYPE_SLIP:
				//cout << "SLIP" << endl; 
				break;
			default:
				//cout << "" << endl; 
				break;
			}
			char buffer[128];
			sprintf(buffer, "%02X-%02X-%02X-%02X-%02X-%02X",
				//sprintf(buffer, "%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X",
				ADDR(0), ADDR(1), ADDR(2), ADDR(3), ADDR(4), ADDR(5));
			ipf.mac = buffer;
			IPnumPerNetCard = 0;
			//可能网卡有多IP
			IP_ADDR_STRING* pIpAddrString = &(pIpAdapterInfo->IpAddressList);
			do
			{
				ipf.ips.push_back(pIpAddrString->IpAddress.String);
				pIpAddrString = pIpAddrString->Next;

			} while (pIpAddrString);
			ff.push_back(ipf);
			pIpAdapterInfo = pIpAdapterInfo->Next;
		}
	}
	//释放内存空间
	if (pIpAdapterInfo)
	{
		delete pIpAdapterInfo;
	}
	return 0;
}


class c_arp
{
	list_nim v_lnim;
	std::atomic<int> v_number = 0;
		
public:
	~c_arp()
	{
		v_lnim.clear();
	}
	size_t size()
	{
		return v_lnim.nim.size();
	}
	void set_max_ip(uint32_t number)
	{
		v_number = number;
	}
	void send_arp(uint32_t naddr_f)
	{
		//for (uint32_t t = naddr_f; t < naddr_t; t++)
		//{
			struct in_addr in;
			in.S_un.S_addr = naddr_f;
			string ip = inet_ntoa(in);
			std::cout << "scan:";
			std::cout << ip.c_str() << endl;
			HRESULT result;
			ULONG c[2] = { 0 }, len = 6;
			//unsigned int ipn = htonl(inet_addr(ip.c_str()));
			//发送ARP包
			result = SendARP(naddr_f, NULL, c, &len);

			if (result == NO_ERROR)
			{
				BYTE *g = (BYTE *)&c;
				if (len)
				{
					string strmac(32, 0);
					sprintf(const_cast<char*>(strmac.c_str()),
						"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",
						g[0], g[1], g[2], g[3], g[4], g[5]);
					v_lnim.push(strmac, ip);

				}
			}
		//}
		//return 0;
	}


	void start_thread(int tn,uint32_t ulNetName)
	{
		thread th[256];
		for (int i = 0; i < tn; i++)
		{
			th[i] = thread([this,ulNetName] {
				while (1)
				{
					uint32_t x = v_number--;
					uint32_t n = htonl(ulNetName + x + 1);
					this->send_arp(n);
					if (v_number < 0)
						break;
				}
			});
			//th.detach();
		}
		for (int i = 0; i < tn; i++)
			th[i].join();
	}
	void print()
	{
		for (auto iter : v_lnim.nim)
		{
			std::cout << iter.first.c_str() << ":" << iter.second.c_str() << endl;
		}
	}
};



std::string main_ip()
{
	std::vector<s_ip_fname> ip_mac_vector;
	get_ipfname(ip_mac_vector);
	std::string ip;
	if (ip_mac_vector.size() == 0)
		return ip;
	auto it = ip_mac_vector.begin();
	if(it->ips.size()>0)
		ip = it->ips[0];
	return ip;
}
int main()
{
	// 初始化socket
	WSADATA data;
	WORD wVersion = MAKEWORD(2, 2);
	WSAStartup(wVersion, &data);


	hostent *pLocalHost;
//	HANDLE hEvent;

	// 获得本机IP结构
	pLocalHost = ::gethostbyname("");
	std::string ip = main_ip();

	

	// 这样获得是网络字节序
	//ULONG ulIpAddress = (*(struct in_addr *)*(pLocalHost->h_addr_list)).S_un.S_addr;
	ULONG ulIpAddress = inet_addr(ip.c_str());

	PIP_ADAPTER_INFO pAdapterInfo = NULL;
	ULONG ulLen = 0;

	// 为适配器结构申请内存
	::GetAdaptersInfo(pAdapterInfo, &ulLen);
	pAdapterInfo = (PIP_ADAPTER_INFO)::GlobalAlloc(GPTR, ulLen);


	c_arp arp;
	if (::GetAdaptersInfo(pAdapterInfo, &ulLen) == ERROR_SUCCESS)
	{
		while (pAdapterInfo != NULL)
		{
			if(ip.compare(pAdapterInfo->IpAddressList.IpAddress.String) == 0)
			//if (::inet_addr(pAdapterInfo->IpAddressList.IpAddress.String) == ulIpAddress)
			{
				// 这里要转换为主机字节序
				ULONG ulIpMask = ntohl(::inet_addr(pAdapterInfo->IpAddressList.IpMask.String));
				// 与获得网络号
				ULONG ulNetName = ntohl(ulIpAddress) & ulIpMask;
				//获得网段内的主机数
				UINT unNum = ~ulIpMask;
				UINT nNumofHost = unNum - 2;
				arp.set_max_ip(nNumofHost);

相关阅读

手机版|MSIPO技术圈 皖ICP备19022944号-2

Copyright © 2024, msipo.com

返回顶部