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 地址:



完整代码如下:

[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <net/if.h>               //struct ifreq  
  5. #include <sys/ioctl.h>            //ioctl、SIOCGIFADDR  
  6. #include <sys/socket.h>  
  7. #include <netinet/ether.h>        //ETH_P_ALL  
  8. #include <netpacket/packet.h> //struct sockaddr_ll  
  9. #include <netinet/in.h>  
  10.   
  11. int main(int argc,charchar *argv[])  
  12. {  
  13.     //1.创建通信用的原始套接字  
  14.     int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));  
  15.       
  16.     //2. 根据各种协议首部格式构建发送数据报  
  17.     unsigned char send_msg[1024] = {  
  18.         //--------------组MAC--------14------  
  19.         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //dst_mac: FF:FF:FF:FF:FF:FF  
  20.         0x000x0c, 0x290x970xc7,0xc1//src_mac: 00:0c:29:97:c7:c1  
  21.         0x080x06,                         //类型:0x0806 ARP协议  
  22.           
  23.         //--------------组ARP--------28-----  
  24.         0x000x010x080x00,             //硬件类型1(以太网地址),协议类型0x0800(IP)     
  25.         0x060x040x000x01,             //硬件、协议地址分别是6、4,op:(1:arp请求,2:arp应答)  
  26.         0x000x0c, 0x290x970xc7,0xc1,  //发送端的MAC地址  
  27.         10,  221,  011,               //发送端的IP地址  
  28.         0x000x000x000x000x000x00//目的MAC地址(由于要获取对方的MAC,所以目的MAC置零)  
  29.         102212010             //目的IP地址  
  30.     };  
  31.       
  32.     //3.数据初始化  
  33.     struct sockaddr_ll sll;                 //原始套接字地址结构  
  34.     struct ifreq ethreq;                    //网络接口地址  
  35.     strncpy(ethreq.ifr_name"eth0", IFNAMSIZ); //指定网卡名称  
  36.       
  37.     //4.将网络接口赋值给原始套接字地址结构  
  38.     ioctl(sock_raw_fd, SIOCGIFINDEX, (charchar *)ðreq);  
  39.     bzero(&sll, sizeof(sll));  
  40.     sll.sll_ifindex = ethreq.ifr_ifindex;  
  41.       
  42.     //5. 发送 ARP 请求包  
  43.     int len = sendto(sock_raw_fd, send_msg, 420 , (struct sockaddr *)&sll, sizeof(sll));  
  44.     if(len == -1)  
  45.     {  
  46.         perror("sendto");  
  47.     }  
  48.       
  49.     //6.接收对方的ARP应答  
  50.     unsigned char recv_msg[1024] = {0};  
  51.     recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0NULLNULL);  
  52.     if(recv_msg[21] == 2)           //ARP应答  
  53.     {  
  54.         char resp_mac[18] = "";     //arp响应的MAC  
  55.         char resp_ip[16] = "";      //arp响应的IP  
  56.           
  57.         sprintf(resp_mac, "%02x:%02x:%02x:%02x:%02x:%02x", \  
  58.         recv_msg[22],recv_msg[23],recv_msg[24],recv_msg[25],recv_msg[26],recv_msg[27]);  
  59.         sprintf(resp_ip, "%d.%d.%d.%d", recv_msg[28], recv_msg[29], recv_msg[30], recv_msg[31]);  
  60.         printf("IP:%s - MAC:%s\n",resp_ip, resp_mac);  
  61.     }  
  62.       
  63.     return 0;  
  64. }  

程序运行结果如下:



查看 PC 的网卡信息:



下面的例子能够获取指定网段所有机器的 MAC 地址

[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <net/if.h>               //struct ifreq  
  5. #include <sys/ioctl.h>            //ioctl、SIOCGIFADDR  
  6. #include <sys/socket.h>  
  7. #include <netinet/ether.h>        //ETH_P_ALL  
  8. #include <netpacket/packet.h> //struct sockaddr_ll  
  9. #include <pthread.h>  
  10. #include <netinet/in.h>  
  11. voidvoid *send_arp_ask(voidvoid *arg);  
  12. int main(int argc,charchar *argv[])  
  13. {  
  14.     //1.创建通信用的原始套接字  
  15.     int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));  
  16.       
  17.     //2.创建发送线程  
  18.     pthread_t tid;  
  19.     pthread_create(&tid, NULL, (voidvoid *)send_arp_ask, (voidvoid *)sock_raw_fd);  
  20.       
  21.     while(1)  
  22.     {  
  23.         //3.接收对方的ARP应答  
  24.         unsigned char recv_msg[1024] = "";  
  25.         recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0NULLNULL);  
  26.         if(recv_msg[21] == 2)           //ARP应答  
  27.         {  
  28.             char resp_mac[18] = "";     //arp响应的MAC  
  29.             char resp_ip[16] = "";      //arp响应的IP  
  30.               
  31.             sprintf(resp_mac, "%02x:%02x:%02x:%02x:%02x:%02x", \  
  32.             recv_msg[22],recv_msg[23],recv_msg[24],recv_msg[25],recv_msg[26],recv_msg[27]);  
  33.             sprintf(resp_ip, "%d.%d.%d.%d", recv_msg[28], recv_msg[29], recv_msg[30], recv_msg[31]);  
  34.             printf("IP:%s - MAC:%s\n",resp_ip, resp_mac);  
  35.         }  
  36.     }  
  37.       
  38.     return 0;  
  39. }  
  40.   
  41. voidvoid *send_arp_ask(voidvoid *arg)  
  42. {  
  43.     int i = 0;  
  44.     int sock_raw_fd = (int)arg;  
  45.     //1.根据各种协议首部格式构建发送数据报  
  46.     unsigned char send_msg[1024] = {  
  47.         //--------------组MAC--------14------  
  48.         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //dst_mac: FF:FF:FF:FF:FF:FF  
  49.         0x000x0c, 0x290x750xa60x51//src_mac: 00:0c:29:75:a6:51  
  50.         0x080x06,                         //类型:0x0806 ARP协议  
  51.           
  52.         //--------------组ARP--------28-----  
  53.         0x000x010x080x00,             //硬件类型1(以太网地址),协议类型0x0800(IP)     
  54.         0x060x040x000x01,             //硬件、协议地址分别是6、4,op:(1:arp请求,2:arp应答)  
  55.         0x000x0c, 0x290x750xa60x51//发送端的MAC地址  
  56.         172,  20,   226,  12,               //发送端的IP地址  
  57.         0x000x000x000x000x000x00//目的MAC地址(由于要获取对方的MAC,所以目的MAC置零)  
  58.         172,  20,   226,  11                //目的IP地址  
  59.     };  
  60.       
  61.     //2.数据初始化  
  62.     struct sockaddr_ll sll;                 //原始套接字地址结构  
  63.     struct ifreq ethreq;                    //网络接口地址  
  64.     strncpy(ethreq.ifr_name"eth0", IFNAMSIZ); //指定网卡名称  
  65.       
  66.     //3.将网络接口赋值给原始套接字地址结构  
  67.     ioctl(sock_raw_fd, SIOCGIFINDEX, (charchar *)ðreq);  
  68.     bzero(&sll, sizeof(sll));  
  69.     sll.sll_ifindex = ethreq.ifr_ifindex;  
  70.       
  71.     //4.本地机的IP  
  72.     if(!(ioctl(sock_raw_fd, SIOCGIFADDR, (charchar *)ðreq)))      
  73.     {  
  74.         int num = ntohl(((struct sockaddr_in*) (ðreq.ifr_addr))->sin_addr.s_addr);  
  75.         for(i=0; i<4; i++)  
  76.         {  
  77.             send_msg[31-i] = num>>8*i & 0xff; //将发送端的IP地址组包  
  78.         }  
  79.     }  
  80.       
  81.     //5.获取本地机(eth0)的MAC  
  82.     if (!(ioctl(sock_raw_fd, SIOCGIFHWADDR, (charchar *) ðreq)))  
  83.     {  
  84.         for(i=0; i<6; i++)  
  85.         {  
  86.             //将src_mac、发送端的MAC地址组包  
  87.             send_msg[22+i] = send_msg[6+i] = (unsigned char) ethreq.ifr_hwaddr.sa_data[i];            
  88.         }  
  89.     }  
  90.       
  91.     while(1)  
  92.     {  
  93.         int i = 0;  
  94.         int num[4] = {0};  
  95.         unsigned char input_buf[1024] = "";  
  96.           
  97.         //6.获取所要扫描的网段(172.20.226.0)  
  98.         printf("input_dst_Network:172.20.226.0\n");  
  99.         fgets(input_buf, sizeof(input_buf), stdin);  
  100.         sscanf(input_buf, "%d.%d.%d.", &num[0], &num[1], &num[2]//目的IP地址   
  101.         );  
  102.           
  103.         //7.将键盘输入的信息组包  
  104.         for(i=0;i<4;i++)  
  105.             send_msg[38+i] = num[i];//将目的IP地址组包  
  106.           
  107.         //8.给1~254的IP发送ARP请求  
  108.         for(i=1; i<255; i++)  
  109.         {  
  110.             send_msg[41] = i;  
  111.             int len = sendto(sock_raw_fd, send_msg, 420 , (struct sockaddr *)&sll, sizeof(sll));  
  112.             if(len == -1)  
  113.             {  
  114.                 perror("sendto");  
  115.             }  
  116.         }  
  117.         sleep(1);  
  118.     }  
  119.     return;  
  120. }  

程序运行结果如下:


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值