colaghost

自己的世界。。。

利用ARP协议来获取局域网内活动主机的IP跟MAC地址

我们都知道ARP协议可以用来获取已知IP主机的MAC地址,那是不是可以这样说,在同一个局域网内,我们可以利用ARP回应包来判断局域网内的活动主机?其实是行的,只要局域网内的主机是处于开机状态,那就会对ARP请求包作出回应,通过分析ARP请求包就可以把局域网内所有活动主机的IP地址跟MAC地址提取出来。下面是ARP帧结构和以太网帧结构的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//28字节ARP帧结构
struct arp_head
{
unsigned short hardware_type;    //硬件类型
unsigned short protocol_type;    //协议类型
unsigned char hardware_add_len; //硬件地址长度
unsigned char protocol_add_len; //协议地址长度
unsigned short operation_field; //操作字段
unsigned char source_mac_add[6]; //源mac地址
unsigned long source_ip_add;    //源ip地址
unsigned char dest_mac_add[6]; //目的mac地址
unsigned long dest_ip_add;      //目的ip地址
};
//14字节以太网帧结构
struct ethernet_head
{
unsigned char dest_mac_add[6];    //目的mac地址
unsigned char source_mac_add[6]; //源mac地址
unsigned short type;              //帧类型
};
 
//arp最终包结构
struct arp_packet
{
ethernet_head ed;
arp_head ah;
};

这里是用winpcap开发包来实现发包的(利用pcap_sendpacket()函数可以发送构造后的协议包),具体内容可以自己去看下winpcap开发包的开发文档,可看下这里http://www.winpcap.org/。由于winpcap涉及到网络层以下,故需要自己构建各个协议头部,相对来说比较麻烦,但假如说要实现IP包欺骗、ARP网关欺骗什么的就大有好处了。由于发送ARP请求包过程中会有回应包返回,如果单一线程的话就只能等待发送完所有ARP请求包后(总共255个,你不要说不知道)才能执行接收操作,这样势必会丢失大量的回应包,故这里我采用了两个线程,一个负责发送ARP请求包,一个负责接收回应包。发送线程每发一个包就会Sleep大概50ms,主要是怕发包太快导致程序接收不到一些回应包。下面是具体代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//获得自己主机的MAC地址
int GetSelfMac()
{
unsigned char sendbuf[42];//arp包结构大小
int i = -1;
int res;
ethernet_head eh;
arp_head ah;
struct pcap_pkthdr * pkt_header;
const u_char * pkt_data;
 
memset(eh.dest_mac_add,0xff,6);
memset(eh.source_mac_add,0x0f,6);
 
memset(ah.source_mac_add,0x0f,6);
memset(ah.dest_mac_add,0x00,6);
 
eh.type = htons(ETH_ARP);
ah.hardware_type = htons(ARP_HARDWARE);
ah.protocol_type = htons(ETH_IP);
ah.hardware_add_len = 6;
ah.protocol_add_len = 4;
ah.source_ip_add = inet_addr("219.219.71.230"); //随便设的请求方ip
ah.operation_field = htons(ARP_REQUEST);
// unsigned long ip;
// ip = ntohl(inet_addr("192.168.1.101"));
// ah.dest_ip_add =htonl(ip + loop);
ah.dest_ip_add = localIP;//inet_addr("192.168.1.102");
memset(sendbuf,0,sizeof(sendbuf));
memcpy(sendbuf,&eh,sizeof(eh));
memcpy(sendbuf+sizeof(eh),&ah,sizeof(ah));
 
if(pcap_sendpacket(adhandle,sendbuf,42)==0)
{
//   printf("\nPacketSend succeed\n");
}
else
{
printf("PacketSendPacket in getmine Error: %d\n",GetLastError());
return 0;
}
 
while((res = pcap_next_ex(adhandle,&pkt_header,&pkt_data)) >= 0)
{
if(*(unsigned short *)(pkt_data+12) == htons(ETH_ARP)&&
*(unsigned short*)(pkt_data+20) == htons(ARP_REPLY)&&
*(unsigned long*)(pkt_data+38) == inet_addr("219.219.71.230"))
{
 
for(i=0; i<6; i++)
{
selfMac[i] = *(unsigned char*)(pkt_data+22+i);
printf("%02x",selfMac[i]);
}
break;
}
}
if(i==6) return 1;
else return 0;
}
 
//向局域网内的所有可能的IP地址发送ARP请求包线程
 
void sendArpPacket()
{
 
unsigned char sendbuf[42];//arp包结构大小
unsigned long ip;
ethernet_head eh;
arp_head ah;
memset(eh.dest_mac_add,0xff,6);
memcpy(eh.source_mac_add,selfMac,6);
 
memcpy(ah.source_mac_add,selfMac,6);
memset(ah.dest_mac_add,0x00,6);
 
eh.type = htons(ETH_ARP);
ah.hardware_type = htons(ARP_HARDWARE);
ah.protocol_type = htons(ETH_IP);
ah.hardware_add_len = 6;
ah.protocol_add_len = 4;
ah.operation_field = htons(ARP_REQUEST);
ah.source_ip_add = inet_addr("192.168.1.101");
 
for (unsigned long i=0; i<HostNum; i++)
{
ip = iptosendh;
ah.dest_ip_add =htonl(ip + i);
memset(sendbuf,0,sizeof(sendbuf));
memcpy(sendbuf,&eh,sizeof(eh));
memcpy(sendbuf+sizeof(eh),&ah,sizeof(ah));
if(pcap_sendpacket(adhandle,sendbuf,42)==0)
{
// printf("\nRequest Packet succeed\n");
}
else
{
printf("Request Packet in getmine Error: %d\n",GetLastError());
}
Sleep(50);
}
Sleep(1000);
flag = TRUE;
}
 
//接收ARP响应线程,分析数据包后即可获得活动的主机IP地址等
 
void GetlivePc()
{
//pcap_t *p=(pcap_t *)lpParameter;
int res;
 
// arp_head ah;
struct pcap_pkthdr *pkt_header;
const u_char * pkt_data;
unsigned char tempMac[6];
while (true)
{
if(flag)
{
printf("扫描完毕,监听线程退出!\n");
//ExitThread(0);
break;
}
 
if ((res = pcap_next_ex(adhandle,&pkt_header,&pkt_data)) >= 0)
{
 
// printf("%x",ntohs(*(unsigned short *)(pkt_data+12)));
if(*(unsigned short *)(pkt_data+12) == htons(ETH_ARP))
{
arp_packet *recv = (arp_packet*)pkt_data;
if(*(unsigned short *)(pkt_data+20) == htons(ARP_REPLY))
{
printf("xx\n");
printf("IP地址:%d.%d.%d.%d---------->mac地址:",\
recv->ah.source_ip_add&255, recv->ah.source_ip_add>>8&255,\
recv->ah.source_ip_add>>16&255, recv->ah.source_ip_add>>24&255);
pcGroup[aliveNum].ip = *(unsigned long *)(pkt_data+28);
memcpy(pcGroup[aliveNum].mac,(pkt_data+22),6);
aliveNum++;
for(int i=0; i<6; i++)
{
tempMac[i] = *(unsigned char*)(pkt_data+22+i);
printf("%02x",tempMac[i]);
}
printf("\n");
}
}
}
Sleep(50);
}
}

后记:其实后来才知道WINDOWS平台上有SendArp这个函数可以获取到相应的MAC地址,实现这个东西时是正好在看WinpCap这个平发包,才突然想到做这个东西,当是练练手吧,呵呵!后来写的ARP欺骗程序也是基于这个平发包的,算是受益甚多吧。