项目场景:
最近需要解决一个在局域网中获取对方IP的问题,所以我想到一个ARP探测的方法。在网上找了一个代码,但是需要改进。这边分享一下改进后的代码
问题描述
需要在局域网中获取对方的IP,MAC地址
解决方案:
代码展示
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
/* 6个FF代表广播 */
#define MACLEN 6
#define IPLEN 4
#define DESIPLEN 20
unsigned char broad_mac[MACLEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
unsigned char local_mac[MACLEN] = {0}; // 存储本机硬件地址
unsigned char local_ip[IPLEN] = {0}; // 存储本机IP地址
unsigned char des_ip[DESIPLEN] = {0}; // 存储目的IP地址
/* 根据 RFC 0826 修改*/
typedef struct _Ether_pkg Ether_pkg;
struct _Ether_pkg
{
/* 前面是ethernet头 */
unsigned char ether_dhost[MACLEN]; /* 目地硬件地址 */
unsigned char ether_shost[MACLEN]; /* 源硬件地址 */
unsigned short int ether_type; /* 网络类型 */
/* 下面是arp协议 */
unsigned short int ar_hrd; /* 硬件地址格式 */
unsigned short int ar_pro; /* 协议地址格式 */
unsigned char ar_hln; /* 硬件地址长度(字节) */
unsigned char ar_pln; /* 协议地址长度(字节) */
unsigned short int ar_op; /* 操作代码 */
unsigned char arp_sha[MACLEN]; /* 源硬件地址 */
unsigned char arp_spa[IPLEN]; /* 源协议地址 */
unsigned char arp_tha[MACLEN]; /* 目地硬件地址 */
unsigned char arp_tpa[IPLEN]; /* 目地协议地址 */
};
/* 得到本机的mac地址和ip地址 */
int GetLocalMac(const char *device, unsigned char *mac, unsigned char *ip)
{
int sockfd;
struct ifreq req;
/* 创建套接字 SOCK_DGRAM:UDP报文常用 */
if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
{
fprintf(stderr, "Sock Error:%s\n\a", strerror(errno));
return -1;
}
memset(&req, 0, sizeof(req));
strcpy(req.ifr_name, device);
/* 获取硬件地址类型 */
if (ioctl(sockfd, SIOCGIFHWADDR, (char *)&req) == -1)
{
fprintf(stderr, "Get hardware mac error:%s\n\a", strerror(errno));
close(sockfd);
return -1;
}
memcpy(mac, req.ifr_hwaddr.sa_data, MACLEN);
req.ifr_addr.sa_family = PF_INET;
if (ioctl(sockfd, SIOCGIFADDR, (char *)&req) == -1)
{
fprintf(stderr, "ioctl SIOCGIFADDR:%s\n\a", strerror(errno));
close(sockfd);
return -1;
}
// 获取ip 并存储
memcpy(ip, &((struct sockaddr_in *)&req.ifr_addr)->sin_addr, IPLEN);
printf("ip= %s mac %s \n", inet_ntoa(*(struct in_addr *)ip), ether_ntoa(mac));
return 0;
}
int fill_pkg(Ether_pkg *pkg)
{
unsigned char desip[IPLEN] = {0};
memset(pkg, 0, sizeof(pkg));
/*
目的MAC地址 | 源MAC地址 | 帧类型 | 硬件类型 | 协议类型 |硬件地址长度 |协议长度 | 操作类型 |发送方硬件地址|发送方IP地址|目标硬件地址|目标IP地址|补充报文
*/
/* 填充以太网头部报文 */
memcpy(pkg->ether_dhost, broad_mac, MACLEN); // 目的MAC地址
memcpy(pkg->ether_shost, local_mac, MACLEN); // 填充本机mac地址
pkg->ether_type = htons(ETHERTYPE_ARP); // 帧类型 arp
/* 填充arp报文 */
pkg->ar_hrd = htons(ARPHRD_ETHER); // 硬件类型
pkg->ar_pro = htons(ETHERTYPE_IP); // 协议类型
pkg->ar_hln = 6; // 硬件协议长度
pkg->ar_pln = 4; // 协议长度
pkg->ar_op = htons(ARPOP_REQUEST); // 操作类型 请求
memcpy(pkg->arp_sha, local_mac, MACLEN); // 填充源mac地址
memcpy(pkg->arp_spa, local_ip, IPLEN); // 填充源IP地址
memcpy(pkg->arp_tha, broad_mac, MACLEN); // 填充目的硬件地址
/* 填入目的IP地址 */
if (inet_aton(des_ip, (struct in_addr *)desip) == 0)
{
return -1;
}
memcpy(pkg->arp_tpa, desip, IPLEN);
return 0;
}
void get_desip(unsigned char *dstip, unsigned char *srcip, unsigned char num)
{
/* 假设取出IP 192.168.144 */
char *des = inet_ntoa(*(struct in_addr *)srcip);
/* 取出字符串 .144 */
char *tmp = strrchr(des, '.');
/* 这边指针相减 取出 192.168.144 */
char newdes[20] = {0};
memcpy(newdes, des, tmp - des);
sprintf(dstip, "%s.%d", newdes, num);
}
int sendpkg(Ether_pkg *pkg, char *net)
{
struct hostent *host = NULL;
struct sockaddr sa;
int sockfd, len;
printf("Resolve [%s],Please Waiting...\n", des_ip);
fflush(stdout);
/* 实际应该使用 PF_PACKET */
if ((sockfd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ARP))) == -1)
{
fprintf(stderr, "Socket Error:%s\n\a", strerror(errno));
return (0);
}
memset(&sa, '\0', sizeof(sa));
strcpy(sa.sa_data, net); // 指定网络的出口
len = sendto(sockfd, pkg, sizeof(*pkg), 0, &sa, sizeof(sa));
if (len != sizeof(*pkg))
{
fprintf(stderr, "Sendto Error:%s\n\a", strerror(errno));
return (0);
}
return sockfd;
}
int get_arpreply(int sockfd)
{
int len = 0;
char buffer[255];
Ether_pkg *parse = (Ether_pkg *)buffer;
fd_set readfds;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500000; /*500毫秒 超时时间*/
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
/* 开始轮询 */
len = select(sockfd + 1, &readfds, 0, 0, &tv);
if (len > -1)
{
/* 查看套接字是否可读 */
if (FD_ISSET(sockfd, &readfds))
{
memset(buffer, 0, sizeof(buffer));
len = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, &len);
if ((ntohs(parse->ether_type) == ETHERTYPE_ARP) &&
(ntohs(parse->ar_op) == ARPOP_REPLY))
{
printf("ARP REPLY IP=[%s] MAC=[%s]\n", inet_ntoa(*(struct in_addr *)parse->arp_spa), ether_ntoa(parse->arp_sha));
}
}
}
return 1;
}
/* 修改增加参数 网口设置 */
int main(int argc, char **argv)
{
Ether_pkg pkg;
int sockfd;
unsigned char start, end = 0;
if (argc < 2)
{
printf("please input right param\n");
printf("Example: ./xxx eth0 1 50\n"); //查看 1-50 地址的IP
return -1;
}
start = atoi(argv[2]);
end = atoi(argv[3]);
/* 获取要使用arp探测的网口名称 */
if (GetLocalMac(argv[1], local_mac, local_ip) == -1)
return (-1);
/* 填充我们所需的报文 */
for (start; start <= end; start++)
{
/* 设置对方的ARP地址 */
get_desip(des_ip, local_ip, start);
/* 填充ARP报文 */
fill_pkg(&pkg);
/* 发送ARP报文 */
sockfd = sendpkg(&pkg, argv[1]);
if(sockfd <=0)
{
return -1;
}
/* 获取报文 */
get_arpreply(sockfd);
}
return 0;
}
使用方法
./send_arp eth0 1 50
参数 | 含义 |
---|---|
eth0 | 发送arp的网口 |
1 | (xxx.xxx.xxx.1) |
50 | (xxx.xxx.xxx.50) |
总结
这边是抄的网上的一个代码改编的,但是我忘记链接地址了。在这边感谢那个老哥。