qfedu-network-advanced-level/day6/homework/fake_feiqiu.c

249 lines
10 KiB
C
Raw 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.

// 飞秋伪装(UDP应用)
/*
MAC -> 14
IP -> 20
UDP -> 8
UDP 数据报数据部分
*/
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ether.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netpacket/packet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h> // system
ssize_t send_datapacket(int fd, unsigned char *buf, ssize_t buf_size, const char *ether_name);
unsigned short checksum(unsigned short *buf, int len);
// 伪头部(UDP 校验时需要再 UDP 报文之间加上伪头部)
typedef struct pseudo_udp_head_struct
{
unsigned int saddr; // 源IP
unsigned int daddr; // 目的IP
unsigned char flag; // 0
unsigned char protocol; // 协议类型
unsigned short len; // UDP 首部长度 + 数据部分长度
} PSEUDO_UDP_HEAD;
int main(int argc, char const *argv[])
{
// 创建原始套接字
int sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock_fd < 0)
{
perror("raw socket");
return -1;
}
/*
// 网卡结构体
struct ifreq ether_req; // 网卡接口请求结构体
strncpy(ether_req.ifr_name, "ens34", IFNAMSIZ); // 指定网卡名字, IFNAMESIZ 是接口网卡的名字大小
// 如果网卡不存在,返回错误,否则返回网卡接口索引
if (ioctl(sock_fd, SIOCGIFINDEX, &ether_req) == -1)
{
perror("ioctl");
return -1;
}
// 发送接口的结构体
struct sockaddr_ll sll; // 网络层套接字地址结构体
bzero(&sll, sizeof(sll));
sll.sll_ifindex = ether_req.ifr_ifindex; // 将网卡的接口类型赋值给发送接口 */
char sender_ip[128] = "";
printf("请输入要伪装的发送者IP: ");
scanf("%s", sender_ip);
char sender_mac[128] = "";
printf("请输入要伪装的发送者MAC: ");
scanf("%s", sender_mac);
char receiver_ip[128] = "";
printf("请输入要伪装的接收者IP: ");
scanf("%s", receiver_ip);
char recv_mac[128] = "";
printf("请输入要伪装的接收者MAC: ");
scanf("%s", recv_mac);
char sender_name[128] = "";
printf("请输入要伪装的发送者名字: ");
scanf("%s", sender_name);
char sender_pc_name[128] = "";
printf("请输入要伪装的发送者电脑名字: ");
scanf("%s", sender_pc_name);
// mac 地址格式化
unsigned char dst_mac[8] = {0}; // 目的 MAC
unsigned char src_mac[8] = {0}; // 发送者 MAC
sscanf(sender_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &src_mac[0], &src_mac[1], &src_mac[2], &src_mac[3], &src_mac[4], &src_mac[5]);
sscanf(recv_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &dst_mac[0], &dst_mac[1], &dst_mac[2], &dst_mac[3], &dst_mac[4], &dst_mac[5]);
while (1)
{
unsigned char udp_buf[1500] = ""; // UDP 数据报
// 伪装飞秋数据报(UDP)
// 1. 伪装飞秋数据报的源IP和目的IP
// 2. 伪装飞秋数据报的源端口和目的端口
// 3. 伪装飞秋数据报的校验和
// 4. 伪装飞秋数据报的数据部分
// 5. 伪装飞秋数据报的长度
// 伪装飞秋数据报的数据部分
// char msg_data[1024] = "1_lbt6_47#128#704D7B3F6397#0#0#0#4001#9:1694470508:chai:chai:32:hi liangzai";
char msg_data[1024] = "";
char say_what[1024] = "";
printf("请输入要发送的内容:");
// scanf("%s", say_what);
fgets(say_what, sizeof(say_what), stdin); // 比 scanf 的优点是可以输入空格
say_what[strlen(say_what) - 1] = '\0'; // 去掉最后的换行符
if (strcmp(say_what, "exit") == 0)
{
break;
}
if (strcmp(say_what, "clear") == 0)
{
system("clear");
continue;
}
/* if (strncpy(say_what, "change", 6) == 0)
{
printf("请输入要伪装的发送者名字:");
scanf("%s", sender_name);
printf("请输入要伪装的发送者电脑名字:");
scanf("%s", sender_pc_name);
continue;
}
if (strncmp(say_what, "help", 4) == 0)
{
printf("请输入要发送的内容:\n");
printf("1. 输入exit退出\n");
printf("2. 输入clear清屏\n");
printf("3. 输入change更改伪装信息\n");
continue;
} */
sprintf(msg_data, "1:%d:%s:%s:%d:%s", 123, sender_name, sender_pc_name, 32, say_what);
int msg_data_len = strlen(msg_data) + strlen(msg_data) % 2; // 整数补齐偶数位strlen(msg_data)%2 : 偶数+0奇数+1
printf("msg_data_len = %d ---> %s\n", msg_data_len, msg_data);
/* ---------------------组装 UDP 数据报的 UDP 首部--------------------- */
struct udphdr *udp_head = (struct udphdr *)(udp_buf + 14 + 20); // UDP 首部开始位置
udp_head->source = htons(2425); // 源端口
udp_head->dest = htons(2425); // 目的端口
udp_head->len = htons(8 + msg_data_len); // UDP 首部长度 + 数据部分长度
udp_head->check = htons(0); // UDP 首部校验和
/* ---------------------UDP 伪首部校验--------------------- */
// UDP 校验时需要再 UDP 报文之间加上伪头部
/*
UDP 校验中的伪头部pseudo header是在计算 UDP 校验和时使用的辅助数据。伪头部包含了源 IP 地址、目的 IP 地址、协议类型(通常是 UDP和 UDP 报文长度等信息。
在计算 UDP 校验和时,将伪头部和 UDP 报文的内容拼接在一起,然后计算校验和。这样做的目的是增加校验和的安全性,使其更具可靠性。
*/
// 196: 4c:e1:73:47:16:3a
// 158: e8:d8:d1:48:46:ae
unsigned char pseudo_udp_buf[1056] = ""; // 伪头部
PSEUDO_UDP_HEAD *pseudo_udp_head = (PSEUDO_UDP_HEAD *)pseudo_udp_buf;
pseudo_udp_head->saddr = inet_addr(sender_ip); // 伪装源IP
pseudo_udp_head->daddr = inet_addr(receiver_ip); // 目的IP
pseudo_udp_head->flag = 0; // 0
pseudo_udp_head->protocol = 17; // 协议类型 UDP
pseudo_udp_head->len = htons(8 + msg_data_len); // UDP 首部长度 + 数据部分长度
memcpy(pseudo_udp_buf + 12, udp_head, 8); // 拷贝 UDP 首部到伪头部
memcpy(pseudo_udp_buf + 12 + 8, msg_data, msg_data_len); // 拷贝数据部分到伪头部
// 计算 UDP 首部校验和
udp_head->check = checksum((unsigned short *)pseudo_udp_buf, 12 + 8 + msg_data_len);
memcpy(udp_buf + 14 + 20 + 8, msg_data, msg_data_len); // 拷贝数据部分到 UDP 首部
/* ---------------------组装 UDP 数据报的 IP 首部--------------------- */
struct iphdr *ip_head = (struct iphdr *)(udp_buf + 14); // IP 首部开始位置
ip_head->version = 4; // 版本号 IPv4
ip_head->ihl = 5; // 首部长度 5 * 4 = 20 字节
ip_head->tos = 0; // 服务类型
ip_head->tot_len = htons(20 + 8 + msg_data_len); // IP 首部长度 + UDP 首部长度 + 数据部分长度
ip_head->id = htons(0); // 标识
ip_head->frag_off = htons(0); // 标志和片偏移
ip_head->ttl = 128; // 生存时间
ip_head->protocol = 17; // 协议类型 UDP
// ip 校验时不需要伪头部
ip_head->check = htons(0); // 原始校验和暂时未知赋值0
ip_head->saddr = inet_addr(sender_ip); // 伪装源IP
ip_head->daddr = inet_addr(receiver_ip); // 目的IP
ip_head->check = checksum((unsigned short *)(udp_buf + 14), 20); // IP 首部校验和(计算 IP 首部的校验和)
/* ---------------------组装 UDP 数据报的 MAC 首部--------------------- */
struct ether_header *mac_head = (struct ether_header *)udp_buf; // MAC 首部开始位置
// unsigned char dst_mac[8] = {0xe8, 0x6a, 0x64, 0x6e, 0x93, 0x28}; // 主机 MAC (目的 MAC)
// unsigned char src_mac[8] = {0x4c, 0xe1, 0x73, 0x47, 0x16, 0x3a}; // 虚拟机 MAC (源 MAC)
// 使用 memcpy 函数将 dst_mac 的内容拷贝到 mac_head.ether_dhost 中
memcpy(mac_head->ether_dhost, dst_mac, 6); // 目的 MAC
memcpy(mac_head->ether_shost, src_mac, 6); // 源 MAC
mac_head->ether_type = htons(0x0800); // 2 个字节的数据需要大小端转换 (0x0800: IP 协议)
/* ---------------------发送数据--------------------- */
// int send_len = sendto(sock_fd, udp_buf, 14 + 20 + 8 + msg_data_len, 0, (struct sockaddr *)&sll, sizeof(sll));
int send_len = send_datapacket(sock_fd, udp_buf, 14 + 20 + 8 + msg_data_len, "ens38");
printf("send_len = %d\n", send_len);
}
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;
}
// IP、UDP校验
// 数据报校验计算
// 接收传递的数据地址、长度
unsigned short checksum(unsigned short *buf, int len)
{
int nword = len / 2;
unsigned long sum;
if (len % 2 == 1)
nword++;
for (sum = 0; nword > 0; nword--)
{
sum += *buf;
buf++;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return ~sum;
}