1. 程式人生 > >Linux 網路程式設計——原始套接字例項:MAC 地址掃描器

Linux 網路程式設計——原始套接字例項:MAC 地址掃描器

如果 A (192.168.1.1 )向 B (192.168.1.2 )傳送一個數據包,那麼需要的條件有 ip、port、使用的協議(TCP/UDP)之外還需要 MAC 地址,因為在乙太網資料包中 MAC 地址是必須要有的。那麼怎樣才能知道對方的 MAC 地址?答案是:它通過 ARP 協議來獲取對方的 MAC 地址。

ARP(Address Resolution Protocol,地址解析協議),是 TCP/IP 協議族中的一個,主要用於查詢指定 ip 所對應的的 MAC(通過 ip 找 MAC)。

請求方使用廣播來發送請求,應答方使用單播來回送資料。收到返回訊息後將該 IP 地址和實體地址存入本機 ARP 快取中並保留一定時間,下次請求時直接查詢 ARP 快取以節約資源。

以機器 A 獲取機器 B 的 MAC 為例,A 廣播發送一個 ARP 請求包,和 A 同在一個區域網的主機都會收到這個請求包,每個機器都會比較自己的 ip 和請求包的目的 ip 是不是一樣的,如果不一樣,就丟棄這個請求包,結果,只有 B 機器符合條件,B 機器單獨給 A 傳送 ARP 應答包,應答包帶上了 B 的 ip 所對應的 MAC 地址,當 A 收到這個應答包後,就把 B 的 ip 以及其對應的 MAC 地址存入本機 ARP 快取中。

在 Linux 檢視 ARP 快取表:arp

在 Windows 檢視 ARP 快取表:arp -a

ARP頭部

1、Dest MAC:目的 MAC 地址 2、Src MAC:源 MAC 地址 3、幀型別:0x0806 4、硬體型別:1(乙太網) 5、協議型別:0x0800(IP地址) 6、硬體地址長度:6 7、協議地址長度:4 8、OP:1(ARP請求),2(ARP應答),3(RARP請求),4(RARP應答)

接下來這個例子為,虛擬機器(ubuntu)獲取 PC 機的 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.建立通訊用的原始套接字     int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));          //2. 根據各種協議首部格式構建傳送資料報     unsigned char send_msg[1024] = {         //--------------組MAC--------14------         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //dst_mac: FF:FF:FF:FF:FF:FF         0x00, 0x0c, 0x29, 0x97, 0xc7,0xc1, //src_mac: 00:0c:29:97:c7:c1         0x08, 0x06,                            //型別:0x0806 ARP協議                  //--------------組ARP--------28-----         0x00, 0x01, 0x08, 0x00,                //硬體型別1(乙太網地址),協議型別0x0800(IP)             0x06, 0x04, 0x00, 0x01,                //硬體、協議地址分別是6、4,op:(1:arp請求,2:arp應答)         0x00, 0x0c, 0x29, 0x97, 0xc7,0xc1,    //傳送端的MAC地址         10,  221,  0, 11,                  //傳送端的IP地址         0x00, 0x00, 0x00, 0x00, 0x00, 0x00,    //目的MAC地址(由於要獲取對方的MAC,所以目的MAC置零)         10, 221, 20, 10                //目的IP地址     };          //3.資料初始化     struct sockaddr_ll sll;                    //原始套接字地址結構     struct ifreq req;                    //網路介面地址     strncpy(req.ifr_name, "eth0", IFNAMSIZ);    //指定網絡卡名稱          //4.將網路介面賦值給原始套接字地址結構     ioctl(sock_raw_fd, SIOCGIFINDEX, &req);     bzero(&sll, sizeof(sll));     sll.sll_ifindex = req.ifr_ifindex;          //5. 傳送 ARP 請求包     int len = sendto(sock_raw_fd, send_msg, 42, 0 , (struct sockaddr *)&sll, sizeof(sll));     if(len == -1)     {         perror("sendto");     }          //6.接收對方的ARP應答     unsigned char recv_msg[1024] = {0};     recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0, NULL, NULL);     if(recv_msg[21] == 2)            //ARP應答     {         char resp_mac[18] = "";        //arp響應的MAC         char resp_ip[16] = "";        //arp響應的IP                  sprintf(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; } 程式執行結果如下:

檢視 PC 的網絡卡資訊:

下面的例子能夠獲取指定網段所有機器的 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.建立通訊用的原始套接字     int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));          //2.建立傳送執行緒     pthread_t tid;     pthread_create(&tid, NULL, (void *)send_arp_ask, (void *)sock_raw_fd);          while(1)     {         //3.接收對方的ARP應答         unsigned char recv_msg[1024] = "";         recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0, NULL, NULL);         if(recv_msg[21] == 2)            //ARP應答         {             char resp_mac[18] = "";        //arp響應的MAC             char resp_ip[16] = "";        //arp響應的IP                          sprintf(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.根據各種協議首部格式構建傳送資料報     unsigned char send_msg[1024] = {         //--------------組MAC--------14------         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //dst_mac: FF:FF:FF:FF:FF:FF         0x00, 0x0c, 0x29, 0x75, 0xa6, 0x51, //src_mac: 00:0c:29:75:a6:51         0x08, 0x06,                            //型別:0x0806 ARP協議                  //--------------組ARP--------28-----         0x00, 0x01, 0x08, 0x00,                //硬體型別1(乙太網地址),協議型別0x0800(IP)             0x06, 0x04, 0x00, 0x01,                //硬體、協議地址分別是6、4,op:(1:arp請求,2:arp應答)         0x00, 0x0c, 0x29, 0x75, 0xa6, 0x51,    //傳送端的MAC地址         172,  20,   226,  12,                  //傳送端的IP地址         0x00, 0x00, 0x00, 0x00, 0x00, 0x00,    //目的MAC地址(由於要獲取對方的MAC,所以目的MAC置零)         172,  20,   226,  11                //目的IP地址     };          //2.資料初始化     struct sockaddr_ll sll;                    //原始套接字地址結構     struct ifreq req;                    //網路介面地址     strncpy(req.ifr_name, "eth0", IFNAMSIZ);    //指定網絡卡名稱          //3.將網路介面賦值給原始套接字地址結構     ioctl(sock_raw_fd, SIOCGIFINDEX, &req);     bzero(&sll, sizeof(sll));     sll.sll_ifindex = req.ifr_ifindex;          //4.本地機的IP     if(!(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;    //將傳送端的IP地址組包         }     }          //5.獲取本地機(eth0)的MAC     if (!(ioctl(sock_raw_fd, SIOCGIFHWADDR, (char *) &req)))     {         for(i=0; i<6; i++)         {             //將src_mac、傳送端的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.獲取所要掃描的網段(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傳送ARP請求         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; }

程式執行結果如下: