【Linux网络编程】原始套接字实例:MAC 地址扫描器
如果 A (192.168.1.1 )向 B (192.168.1.2 )發(fā)送一個(gè)數(shù)據(jù)包,那么需要的條件有 ip、port、使用的協(xié)議(TCP/UDP)之外還需要 MAC 地址,因?yàn)樵谝蕴W(wǎng)數(shù)據(jù)包中 MAC 地址是必須要有的。那么怎樣才能知道對(duì)方的 MAC 地址?答案是:它通過(guò) ARP 協(xié)議來(lái)獲取對(duì)方的 MAC 地址。
ARP(Address Resolution Protocol,地址解析協(xié)議),是 TCP/IP 協(xié)議族中的一個(gè),主要用于查詢指定 ip 所對(duì)應(yīng)的的 MAC(通過(guò) ip 找 MAC)。
請(qǐng)求方使用廣播來(lái)發(fā)送請(qǐng)求,應(yīng)答方使用單播來(lái)回送數(shù)據(jù)。收到返回消息后將該 IP 地址和物理地址存入本機(jī) ARP 緩存中并保留一定時(shí)間,下次請(qǐng)求時(shí)直接查詢 ARP 緩存以節(jié)約資源。
以機(jī)器 A 獲取機(jī)器 B 的 MAC 為例,A 廣播發(fā)送一個(gè) ARP 請(qǐng)求包,和 A 同在一個(gè)局域網(wǎng)的主機(jī)都會(huì)收到這個(gè)請(qǐng)求包,每個(gè)機(jī)器都會(huì)比較自己的 ip 和請(qǐng)求包的目的 ip 是不是一樣的,如果不一樣,就丟棄這個(gè)請(qǐng)求包,結(jié)果,只有 B 機(jī)器符合條件,B 機(jī)器單獨(dú)給 A 發(fā)送 ARP 應(yīng)答包,應(yīng)答包帶上了 B 的 ip 所對(duì)應(yīng)的 MAC 地址,當(dāng) A 收到這個(gè)應(yīng)答包后,就把 B 的 ip 以及其對(duì)應(yīng)的 MAC 地址存入本機(jī) ARP 緩存中。
在 Linux 查看 ARP 緩存表:arp
在 Windows 查看 ARP 緩存表:arp -a
ARP頭部
1、Dest MAC:目的 MAC 地址
2、Src MAC:源 MAC 地址
3、幀類型:0x0806
4、硬件類型:1(以太網(wǎng))
5、協(xié)議類型:0x0800(IP地址)
6、硬件地址長(zhǎng)度:6
7、協(xié)議地址長(zhǎng)度:4
8、OP:1(ARP請(qǐng)求),2(ARP應(yīng)答),3(RARP請(qǐng)求),4(RARP應(yīng)答)
接下來(lái)這個(gè)例子為,虛擬機(jī)(ubuntu)獲取 PC 機(jī)的 MAC 地址:
先查看 ubuntu 的 ip 和 MAC 地址:
完整代碼如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <net/if.h> //struct ifreq #include <sys/ioctl.h> //ioctl、SIOCGIFADDR #include <sys/socket.h> #include <netinet/ether.h> //ETH_P_ALL #include <netpacket/packet.h> //struct sockaddr_ll #include <netinet/in.h>int main(int argc,char *argv[]) {//1.創(chuàng)建通信用的原始套接字int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));//2. 根據(jù)各種協(xié)議首部格式構(gòu)建發(fā)送數(shù)據(jù)報(bào)unsigned char send_msg[1024] = {//--------------組MAC--------14------0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //dst_mac: FF:FF:FF:FF:FF:FF0x00, 0x0c, 0x29, 0x97, 0xc7,0xc1, //src_mac: 00:0c:29:97:c7:c10x08, 0x06, //類型:0x0806 ARP協(xié)議//--------------組ARP--------28-----0x00, 0x01, 0x08, 0x00, //硬件類型1(以太網(wǎng)地址),協(xié)議類型0x0800(IP) 0x06, 0x04, 0x00, 0x01, //硬件、協(xié)議地址分別是6、4,op:(1:arp請(qǐng)求,2:arp應(yīng)答)0x00, 0x0c, 0x29, 0x97, 0xc7,0xc1, //發(fā)送端的MAC地址10, 221, 0, 11, //發(fā)送端的IP地址0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //目的MAC地址(由于要獲取對(duì)方的MAC,所以目的MAC置零)10, 221, 20, 10 //目的IP地址};//3.數(shù)據(jù)初始化struct sockaddr_ll sll; //原始套接字地址結(jié)構(gòu)struct ifreq req; //網(wǎng)絡(luò)接口地址strncpy(req.ifr_name, "eth0", IFNAMSIZ); //指定網(wǎng)卡名稱//4.將網(wǎng)絡(luò)接口賦值給原始套接字地址結(jié)構(gòu)ioctl(sock_raw_fd, SIOCGIFINDEX, &req);bzero(&sll, sizeof(sll));sll.sll_ifindex = req.ifr_ifindex;//5. 發(fā)送 ARP 請(qǐng)求包int len = sendto(sock_raw_fd, send_msg, 42, 0 , (struct sockaddr *)&sll, sizeof(sll));if(len == -1){perror("sendto");}//6.接收對(duì)方的ARP應(yīng)答unsigned char recv_msg[1024] = {0};recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0, NULL, NULL);if(recv_msg[21] == 2) //ARP應(yīng)答{char resp_mac[18] = ""; //arp響應(yīng)的MACchar resp_ip[16] = ""; //arp響應(yīng)的IPsprintf(resp_mac, "%02x:%02x:%02x:%02x:%02x:%02x", \recv_msg[22],recv_msg[23],recv_msg[24],recv_msg[25],recv_msg[26],recv_msg[27]);sprintf(resp_ip, "%d.%d.%d.%d", recv_msg[28], recv_msg[29], recv_msg[30], recv_msg[31]);printf("IP:%s - MAC:%s\n",resp_ip, resp_mac);}return 0; }
程序運(yùn)行結(jié)果如下:
查看 PC 的網(wǎng)卡信息:
下面的例子能夠獲取指定網(wǎng)段所有機(jī)器的 MAC 地址:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <net/if.h> //struct ifreq #include <sys/ioctl.h> //ioctl、SIOCGIFADDR #include <sys/socket.h> #include <netinet/ether.h> //ETH_P_ALL #include <netpacket/packet.h> //struct sockaddr_ll #include <pthread.h> #include <netinet/in.h> void *send_arp_ask(void *arg); int main(int argc,char *argv[]) {//1.創(chuàng)建通信用的原始套接字int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));//2.創(chuàng)建發(fā)送線程pthread_t tid;pthread_create(&tid, NULL, (void *)send_arp_ask, (void *)sock_raw_fd);while(1){//3.接收對(duì)方的ARP應(yīng)答unsigned char recv_msg[1024] = "";recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0, NULL, NULL);if(recv_msg[21] == 2) //ARP應(yīng)答{char resp_mac[18] = ""; //arp響應(yīng)的MACchar resp_ip[16] = ""; //arp響應(yīng)的IPsprintf(resp_mac, "%02x:%02x:%02x:%02x:%02x:%02x", \recv_msg[22],recv_msg[23],recv_msg[24],recv_msg[25],recv_msg[26],recv_msg[27]);sprintf(resp_ip, "%d.%d.%d.%d", recv_msg[28], recv_msg[29], recv_msg[30], recv_msg[31]);printf("IP:%s - MAC:%s\n",resp_ip, resp_mac);}}return 0; }void *send_arp_ask(void *arg) {int i = 0;int sock_raw_fd = (int)arg;//1.根據(jù)各種協(xié)議首部格式構(gòu)建發(fā)送數(shù)據(jù)報(bào)unsigned char send_msg[1024] = {//--------------組MAC--------14------0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //dst_mac: FF:FF:FF:FF:FF:FF0x00, 0x0c, 0x29, 0x75, 0xa6, 0x51, //src_mac: 00:0c:29:75:a6:510x08, 0x06, //類型:0x0806 ARP協(xié)議//--------------組ARP--------28-----0x00, 0x01, 0x08, 0x00, //硬件類型1(以太網(wǎng)地址),協(xié)議類型0x0800(IP) 0x06, 0x04, 0x00, 0x01, //硬件、協(xié)議地址分別是6、4,op:(1:arp請(qǐng)求,2:arp應(yīng)答)0x00, 0x0c, 0x29, 0x75, 0xa6, 0x51, //發(fā)送端的MAC地址172, 20, 226, 12, //發(fā)送端的IP地址0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //目的MAC地址(由于要獲取對(duì)方的MAC,所以目的MAC置零)172, 20, 226, 11 //目的IP地址};//2.數(shù)據(jù)初始化struct sockaddr_ll sll; //原始套接字地址結(jié)構(gòu)struct ifreq req; //網(wǎng)絡(luò)接口地址strncpy(req.ifr_name, "eth0", IFNAMSIZ); //指定網(wǎng)卡名稱//3.將網(wǎng)絡(luò)接口賦值給原始套接字地址結(jié)構(gòu)ioctl(sock_raw_fd, SIOCGIFINDEX, &req);bzero(&sll, sizeof(sll));sll.sll_ifindex = req.ifr_ifindex;//4.本地機(jī)的IPif(!(ioctl(sock_raw_fd, SIOCGIFADDR, &req))) {int num = ntohl(((struct sockaddr_in*) (&req.ifr_addr))->sin_addr.s_addr);for(i=0; i<4; i++){send_msg[31-i] = num>>8*i & 0xff; //將發(fā)送端的IP地址組包}}//5.獲取本地機(jī)(eth0)的MACif (!(ioctl(sock_raw_fd, SIOCGIFHWADDR, (char *) &req))){for(i=0; i<6; i++){//將src_mac、發(fā)送端的MAC地址組包send_msg[22+i] = send_msg[6+i] = (unsigned char) req.ifr_hwaddr.sa_data[i]; }}while(1){int i = 0;int num[4] = {0};unsigned char input_buf[1024] = "";//6.獲取所要掃描的網(wǎng)段(172.20.226.0)printf("input_dst_Network:172.20.226.0\n");fgets(input_buf, sizeof(input_buf), stdin);sscanf(input_buf, "%d.%d.%d.", &num[0], &num[1], &num[2]//目的IP地址 );//7.將鍵盤輸入的信息組包for(i=0;i<4;i++)send_msg[38+i] = num[i];//將目的IP地址組包//8.給1~254的IP發(fā)送ARP請(qǐng)求for(i=1; i<255; i++){send_msg[41] = i;int len = sendto(sock_raw_fd, send_msg, 42, 0 , (struct sockaddr *)&sll, sizeof(sll));if(len == -1){perror("sendto");}}sleep(1);}return; }程序運(yùn)行結(jié)果如下:
總結(jié)
以上是生活随笔為你收集整理的【Linux网络编程】原始套接字实例:MAC 地址扫描器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【Linux网络编程】原始套接字实例:M
- 下一篇: 【Linux网络编程】IP 数据报格式详