/
//自动扫描局域网存活主机
本程序是利用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 cardwhile (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;//可能网卡有多IPIP_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 cardwhile (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;//可能网卡有多IPIP_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()
{// 初始化socketWSADATA 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);//arp.start_thread(255,ulNetName);for (uint32_t i = 0; i < nNumofHost; i++){uint32_t n = htonl(ulNetName + i + 1);thread th(std::bind(&c_arp::send_arp, &arp, n));th.detach();}break;}pAdapterInfo = pAdapterInfo->Next;}}cout<<arp.size()<<endl;arp.print();getchar();return 0;
}
编译运行,如下图所示:
windows其他函数
GetIpNetTable 函数检索本地计算机上的 ARP 表,该表将 IPv4 地址映射到物理地址。
CreateIpNetEntry 函数在本地计算机上的 ARP 表中创建 ARP 条目。
DeleteIpNetEntry 函数从本地计算机上的 ARP 表中删除 ARP 条目。
SetIpNetEntry 函数修改本地计算机上的 ARP 表中的现有 ARP 条目。
FlushIpNetTable 函数从本地计算机上的 ARP 表中删除指定接口的所有 ARP 条目。
感谢大家阅读