基于ARP协议获取局域网内主机MAC地址

本文深入剖析了ARP协议的工作原理,详细介绍了ARP帧的数据结构,并通过具体的代码实现展示了如何使用Winpcap库来构建ARP请求和响应包,从而实现扫描局域网内活动主机的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ARP帧数据结构

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> #define BROADMAC{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF} // 广播MAC
#define EH_TYPE0x0806 // ARP类型
#define ARP_HRD0X0001 // 硬件类型:以太网接口类型为
#define ARP_PRO0x0800 // 协议类型:IP协议类型为X0800
#define ARP_HLN0x06 // 硬件地址长度:MAC地址长度为B
#define ARP_PLN0x04 // 协议地址长度:IP地址长度为B
#define ARP_REQUEST0x0001 // 操作:ARP请求为
#define ARP_REPLY0x0002 // 操作:ARP应答为
#define ARP_THA{0,0,0,0,0,0} // 目的MAC地址:ARP请求中该字段没有意义,设为;ARP响应中为接收方的MAC地址
#define ARP_PAD{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} // 18字节的填充数据
#define SPECIAL0x70707070 // 定义获得自己MAC地址的特殊源IP,.112.112.112
#define ETH_HRD_DEFAULT{BROADMAC,{0,0,0,0,0,0},htons(EH_TYPE)} // 广播ARP包帧头
#define ARP_HRD_DEFAULT{htons(ARP_HRD),htons(ARP_PRO),ARP_HLN,ARP_PLN,htons(ARP_REQUEST),{0,0,0,0,0,0},0,ARP_THA,0,ARP_PAD}
#define IPTOSBUFFERS12
#define WM_PACKETWM_USER+105 // 用户自定义消息

struct ethernet_head
{
// 物理帧帧头结构
unsigned char dest_mac[ 6 ]; // 目标主机MAC地址(6字节)
unsigned char source_mac[ 6 ]; // 源端MAC地址(6字节)
unsigned short eh_type; // 以太网类型(2字节)
};
struct arp_head
{
// ARP数据帧
unsigned short hardware_type; // 硬件类型:以太网接口类型为
unsigned short protocol_type; // 协议类型:IP协议类型为X0800
unsigned char add_len; // 硬件地址长度:MAC地址长度为B
unsigned char pro_len; // 协议地址长度:IP地址长度为B
unsigned short option; // 操作:ARP请求为,ARP应答为

unsigned
char sour_addr[ 6 ]; // 源MAC地址:发送方的MAC地址
unsigned long sour_ip; // 源IP地址:发送方的IP地址
unsigned char dest_addr[ 6 ]; // 目的MAC地址:ARP请求中该字段没有意义;ARP响应中为接收方的MAC地址
unsigned long dest_ip; // 目的IP地址:ARP请求中为请求解析的IP地址;ARP响应中为接收方的IP地址
unsigned char padding[ 18 ];
};

struct arp_packet // 最终arp包结构
{ // 物理帧结构
ethernet_headeth; // 以太网头部
arp_headarp; // arp数据包头部
};

获取本机的网络适配器

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> int i = 1 ;
string strDev = "" ;
m_Dev.AddString(
" 经分析,本系统网络适配器列表如下: " );
pcap_if_t
* alldevs = 0 ;
pcap_if_t
* pDev = 0 ;
pcap_addr_t
* pAdr = 0 ;
char errbuf[PCAP_ERRBUF_SIZE + 1 ];
if (pcap_findalldevs( & alldevs,errbuf) == - 1 )
{
// 获得设备列表
MessageBox(errbuf,NULL,MB_OK | MB_ICONINFORMATION); // 若没有设备则弹出警告
exit( 1 );
}
for (pDev = alldevs;pDev;pDev = pDev -> next)
{
// 遍历所有成员
if (pDev -> description)
{
strDev
= char (i + 48 );
strDev
+= " . " ;
strDev
+= DelSpace(pDev -> description); // 去掉网卡描述过多的空格
pAdr = pDev -> addresses; // IP地址
if (pAdr != NULL)
{
if (pAdr -> addr -> sa_family == AF_INET)
{
// pAdr->addr是否IP地址类型
strDev += " -> " ;
strDev
+= IpToStr((( struct sockaddr_in * )pAdr -> addr) -> sin_addr.s_addr);
if (IpToStr((( struct sockaddr_in * )pAdr -> addr) -> sin_addr.s_addr)[ 0 ] != ' 0 ' )
{
m_Dev_No
= i;
UpdateData(FALSE);
// 传递变量值去界面
}
strDev
+= " &[ " ;
strDev
+= IpToStr((( struct sockaddr_in * )pAdr -> netmask) -> sin_addr.s_addr); // 子网掩码
strDev += " ] " ;
GetDlgItem(IDC_GET_MAC)
-> EnableWindow(TRUE); // 若网卡有IP地址,则使抓包按钮可用
}
}
m_Dev.InsertString(i
++ ,strDev.c_str());
}
}
pcap_freealldevs(alldevs);
// 不再需要网络适配器列表,释放

获取本机MAC地址

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> unsigned char * BuildArpRequestPacket(unsigned char * source_mac,unsigned char * arp_sha,unsigned long chLocalIP,unsigned long arp_tpa, int PackSize)
{
// 封装ARP请求包
static arp_packetarpPackStru;
static const arp_packetarpDefaultPack = {ETH_HRD_DEFAULT,ARP_HRD_DEFAULT};
memcpy(
& arpPackStru, & arpDefaultPack, sizeof (arpDefaultPack));
// 填充源MAC地址
memcpy(arpPackStru.eth.source_mac,source_mac, 6 ); // 源MAC
memcpy(arpPackStru.arp.sour_addr,arp_sha, 6 ); // 源MAC
arpPackStru.arp.sour_ip = chLocalIP; // 源IP地址
arpPackStru.arp.dest_ip = arp_tpa; // 目的IP地址
return (unsigned char * ) & arpPackStru;
}
unsigned
char * GetSelfMac( char * pDevName,unsigned long chLocalIP)
{
// 获得本机MAC地址,pDevName为网卡名称,chLocalIP为本机IP地址
pcap_t * pAdaptHandle;
char errbuf[PCAP_ERRBUF_SIZE + 1 ];
// 打开网卡适配器
if ((pAdaptHandle = pcap_open_live(pDevName, 60 , 1 , 100 ,errbuf)) == NULL)
{
MessageBox(NULL,
" 无法打开适配器,可能与之不兼容! " , " Note " ,MB_OK);
return NULL;
}
struct pcap_pkthdr * header; // 包头部
const u_char * pkt_data; // 包数据部
int res;
unsigned
short arp_op;
static unsigned char arp_sha[ 6 ];
unsigned
long arp_spa = 0 ;
unsigned
long arp_tpa = 0 ;
unsigned
char source_mac[ 6 ] = { 0 , 0 , 0 , 0 , 0 , 0 };
unsigned
char * arp_packet_for_self;
arp_packet_for_self
= BuildArpRequestPacket(source_mac,source_mac,SPECIAL,chLocalIP, 60 ); // 把自己作为目的,构建一个广播ARP请求包,伪造请求来自.112.112.112
while ( ! GetMacSignal)
{
pcap_sendpacket(pAdaptHandle,arp_packet_for_self,
60 ); // 发送ARP请求包
Sleep( 10 );
res
= pcap_next_ex(pAdaptHandle, & header, & pkt_data);
if (res == 0 )
{
continue ;
}
// 物理帧头部占字节,然后是硬件类型,上层协议类型,硬件地址长度,IP地址长度,这四个占去字节,具体参看ARP帧的数据结构
memcpy( & arp_op,pkt_data + 20 , 2 ); // 操作类型(请求或应答)
memcpy(arp_sha,pkt_data + 22 , 6 ); // 源MAC
memcpy( & arp_spa,pkt_data + 28 , 4 ); // 源IP
memcpy( & arp_tpa,pkt_data + 38 , 4 ); // 目标IP

if (arp_op == htons(ARP_REPLY) && arp_spa == chLocalIP && arp_tpa == SPECIAL)
{
// 是本机
GetMacSignal = 1 ;
pcap_close(pAdaptHandle);
return arp_sha;
}
Sleep(
100 ); // 若不成功再等ms再发,让网卡歇歇
}
pcap_close(pAdaptHandle);
return arp_sha;
}

发送ARP请求线程

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> void SendArpRequest(pcap_if_t * pDev,unsigned char * bLocalMac)
{
// 发送ARP请求
pcap_addr_t * pAdr = 0 ;
unsigned
long chLocalIp = 0 ; // 存放本地ip地址
unsigned long arp_tpa = 0 ;
unsigned
long snd_tpa = 0 ;
unsigned
long nlNetMask = 0 ;
int netsize = 0 ;
const char * pDevName = strSelDeviceName.c_str();
pcap_t
* pAdaptHandle;
char errbuf[PCAP_ERRBUF_SIZE + 1 ];
// 打开网卡适配器
if ((pAdaptHandle = pcap_open_live(pDev -> name, 60 , 0 , 100 ,errbuf)) == NULL)
{
MessageBox(NULL,
" 无法打开适配器,可能与之不兼容! " , " Send " ,MB_OK);
return ;
}
unsigned
char * arp_packet_for_req;
arp_packet_for_req
= BuildArpRequestPacket(bLocalMac,bLocalMac,chLocalIp,chLocalIp, 60 ); // 构造包
unsigned long ulOldMask = 0 ;
for (pAdr = pDev -> addresses;pAdr;pAdr = pAdr -> next)
{
if ( ! nThreadSignal)
{
// 判断线程是否应该中止
break ;
}
chLocalIp
= (( struct sockaddr_in * )pAdr -> addr) -> sin_addr.s_addr; // 得到本地ip
if ( ! chLocalIp)
{
continue ;
}
nlNetMask
= (( struct sockaddr_in * )(pAdr -> netmask)) -> sin_addr.S_un.S_addr; // 得到子网掩码
if (ulOldMask == nlNetMask)
{
continue ;
}
ulOldMask
= nlNetMask;
netsize
= ~ ntohl(nlNetMask); // 子网大小
arp_tpa = ntohl(chLocalIp & nlNetMask); // IP地址
for ( int i = 0 ;i < netsize;i ++ )
{
if ( ! nThreadSignal)
{
break ;
}
arp_tpa
++ ;
snd_tpa
= htonl(arp_tpa);
memcpy(arp_packet_for_req
+ 38 , & snd_tpa, 4 ); // 目的IP在子网范围内按序增长
pcap_sendpacket(pAdaptHandle,arp_packet_for_req, 60 ); // 发送ARP请求包
Sleep( 5 ); // 休息一下再发ARP请求包
}
}
}
UINTStartArpScan(LPVOIDmainClass)
{
// 发送ARP请求线程
AfxGetApp() -> m_pMainWnd -> SendMessage(WM_PACKET, 0 , 1 ); // 开始发送ARP请求包
SendArpRequest(pDevGlobalHandle,bLocalMac); // 对选中设备的所有绑定的IP网段进行ARP请求
AfxGetApp() -> m_pMainWnd -> SendMessage(WM_PACKET, 0 , 2 ); // 全部ARP请求包发送完毕
return 0 ;
}

接收ARP响应线程

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> UINTWaitForArpRepeatPacket(LPVOIDmainClass)
{
pcap_t
* pAdaptHandle;
const char * pDevName = strSelDeviceName.c_str();
char errbuf[PCAP_ERRBUF_SIZE + 1 ];
// 打开网卡适配器
if ((pAdaptHandle = pcap_open_live(pDevName, 60 , 0 , 100 ,errbuf)) == NULL)
{
MessageBox(NULL,
" 无法打开适配器,可能与之不兼容! " , " wait " ,MB_OK);
return - 1 ;
}
string ipWithMac;
char * filter = " etherproto//arp " ;
bpf_programfcode;
int res;
unsigned
short arp_op = 0 ;
unsigned
char arp_sha[ 6 ];
unsigned
long arp_spa = 0 ;
struct pcap_pkthdr * header;
const u_char * pkt_data;
if (pcap_compile(pAdaptHandle, & fcode,filter, 1 ,(unsigned long )( 0xFFFF0000 )) < 0 )
{
MessageBox(NULL,
" 过滤条件语法错误! " , " wait " ,MB_OK);
return - 1 ;
}
// setthefilter
if (pcap_setfilter(pAdaptHandle, & fcode) < 0 )
{
MessageBox(NULL,
" 适配器与过滤条件不兼容! " , " wait " ,MB_OK);
return - 1 ;
}
while ( 1 )
{
if ( ! nThreadSignal)
{
break ;
}
int i = 0 ;
ipWithMac
= "" ;
res
= pcap_next_ex(pAdaptHandle, & header, & pkt_data);
if ( ! res)
{
continue ;
}
memcpy(
& arp_op,pkt_data + 20 , 2 ); // 包的操作类型
memcpy(arp_sha,pkt_data + 22 , 6 ); // 源MAC地址
memcpy( & arp_spa,pkt_data + 28 , 4 ); // 源IP地址
ipWithMac += IpToStr(arp_spa);
for ( int j = strlen(IpToStr(arp_spa));j < 16 ;j ++ )
{
ipWithMac
+= " " ;
}
ipWithMac
+= " --*-> " ;
ipWithMac
+= MacToStr(arp_sha);
for (i = 6 ;i > 0 ;i -- )
{
if (arp_sha[i - 1 ] != bLocalMac[i - 1 ])
{
break ;
}
}
if (arp_op == htons(ARP_REPLY) && i)
{
AfxGetApp()
-> m_pMainWnd -> SendMessage(WM_PACKET,WPARAM( & ipWithMac), 0 ); // 通知主线程更新界面
}
}
return 0 ;
}

主线程消息处理

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> LRESULTCArpByWinpcapDlg::OnPacket(WPARAMwParam,LPARAMlParam)
{
string * transPack = ( string * )wParam;
// 处理捕获到的数据包
if (lParam == 0 )
{
m_Mac_list.AddString(transPack
-> c_str());
m_count
= " 发现 " ;
char buffer[ 5 ];
itoa(m_Mac_list.GetCount(),buffer,
10 ); // 将数量转化为进制字符串;
m_count += buffer;
m_count
+= " 台活动主机 " ;
}
else if (lParam == 1 )
{
m_sending
= " 正在发送ARP请求包! " ;
}
else if (lParam == 2 )
{
if (nThreadSignal)
{
m_sending
= " 全部ARP请求包发送完毕! " ; // 判断是自行发送完毕还是用户终止的?
};
}
UpdateData(FALSE);
return 0 ;
}

参考资料:《Winpcap网络开发库入门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值