我们都知道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欺骗程序也是基于这个平发包的,算是受益甚多吧。
