qfedu-network-advanced-level/day6/n5.c

141 lines
5.8 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
扫描所有局域网IP地址的MAC信息
发ARP请求报文通过for循环组成同一个网段的不同IP地址发送出来并接收响应的ARP报文信息
*/
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ether.h>
#include <netpacket/packet.h>
#include <sys/ioctl.h>
#include <net/if.h> // ifreq
#include <unistd.h>
#include <string.h> // bzero
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// 发送原始数据报
ssize_t send_datapacket(int fd, unsigned char *buf, ssize_t buf_size, const char *ether_name);
void *recv_packetdata(void *arg)
{
// 创建原始套接字
int sock_fd = *((int *)arg);
while (1)
{
// 接链接层的数据报文
unsigned char buf[1518] = "";
int len = recvfrom(sock_fd, buf, sizeof(buf), 0, NULL, NULL);
if (len < 18)
{
perror("recvfrom");
continue;
}
// 拆解MAC数据报文
unsigned char dst_mac[18] = "";
unsigned char src_mac[18] = "";
unsigned short mac_type = ntohs(*((unsigned short *)(buf + 12)));
sprintf(dst_mac, "%02x:%02x:%02x:%02x:%02x:%02x",
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
sprintf(src_mac, "%02x:%02x:%02x:%02x:%02x:%02x",
buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]);
if (mac_type == 0x0806) // 当类型为ARP时
{
printf("------ARP数据报-----\n");
unsigned char src_ip[16] = "";
unsigned char dst_ip[16] = "";
unsigned short op = ntohs(*((unsigned short *)(buf + 20)));
if (op == 2) // 只显示ARP应答的数据
{
// inet_ntop函数被用于将存储在buf + 28位置的二进制IPv4地址转换为字符串表示形式并将结果存储在src_ip变量中。
inet_ntop(AF_INET, (unsigned int *)(buf + 28), src_ip, 16);
inet_ntop(AF_INET, (unsigned int *)(buf + 38), dst_ip, 16);
printf("%s(%s) -> %s(%s)\n", src_mac, src_ip, dst_mac, dst_ip);
}
}
}
}
int main(int argc, char const *argv[])
{
// 创建原始套接字 // htons(ETH_P_ALL)的作用是将以太网协议号转换为网络字节序,以便在创建原始套接字时使用正确的协议号。
int sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock_fd < 0)
{
perror("raw socket");
return -1;
}
// 创建接收数据报的线程
pthread_t tid;
pthread_create(&tid, NULL, recv_packetdata, &sock_fd);
// pthread_detach(tid);
// 组织ARP应答报文(如果是ARP欺骗则源MAC地址可以修改为全0或修改为其他用于伪装的MAC地址)
uint32_t src_ip = inet_addr("10.12.156.204");
// 扫描所有网内的IP的MAC地址
for (int i = 150; i < 253; i++)
// for (int i = 2; i < 255; i++)
{
unsigned char dst_ip_[16] = "";
sprintf(dst_ip_, "10.12.156.%d", i);
// inet_addr函数被用于将点分十进制表示的IPv4地址转换为32位无符号整数的网络字节序表示。
uint32_t dst_ip = inet_addr(dst_ip_);
unsigned char *src_ip_pointer = (unsigned char *)&src_ip; // 定义源地址ip指针
unsigned char *dst_ip_pointer = (unsigned char *)&dst_ip;
unsigned char buf[] = {
// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 用于广播, 常用于扫描MAC地址
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /*目标MAC*/
0x00, 0x0c, 0x29, 0x85, 0xcc, 0x67, /*源MAC*/
0x08, 0x06, /*帧类型(ARP)*/
0x00, 0x01, /*硬件类型(以太网)*/
0x08, 0x00, /*协议类型(IP)*/
0x06, 0x04, /*硬件地址长度(6字节)和协议地址长度(4字节)*/
0x00, 0x01, /*操作类型(ARP请求)*/
0x00, 0x0c, 0x29, 0x85, 0xcc, 0x67, /*发送方MAC*/
src_ip_pointer[0], src_ip_pointer[1], src_ip_pointer[2], src_ip_pointer[3], /*发送方IP*/
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /*目标MAC*/
dst_ip_pointer[0], dst_ip_pointer[1], dst_ip_pointer[2], dst_ip_pointer[3] /*目标IP*/
};
// 单播(发送ARP请求): 通过网卡名查找网卡索引, 选择合适的网卡索引, 发送数据
ssize_t len = send_datapacket(sock_fd, buf, sizeof(buf), "ens38");
if (len > 0)
{
printf("发送ARP应答报文成功\n");
}
usleep(1000 * 1000);
}
pthread_join(tid, NULL);
close(sock_fd);
return 0;
}
ssize_t send_datapacket(int fd, unsigned char *buf, ssize_t buf_size, const char *ether_name)
{
// 1. 获取网络接口类型(通过网卡名查找网卡索引)
struct ifreq ether_req;
bzero(&ether_req, sizeof(ether_req));
strncpy(ether_req.ifr_name, ether_name, IF_NAMESIZE); // # define ifr_name ifr_ifrn.ifrn_name
if (ioctl(fd, SIOCGIFINDEX, &ether_req) == -1)
{
perror("ioctl");
return -1;
}
// 2. 选择发送数据的网络接口索引(选择合适的网卡索引)
struct sockaddr_ll sll;
bzero(&sll, sizeof(sll));
sll.sll_ifindex = ether_req.ifr_ifindex; // # define ifr_ifindex ifr_ifru.ifru_ivalue
// 3. 发送数据
ssize_t len = sendto(fd, buf, buf_size, 0, (struct sockaddr *)&sll, sizeof(sll));
return len;
}