我们知道了arp的作用,那么此时我们怎么可以用他来进行攻击呢?在一个局域网中,我们怎么实现呢?
原理:
这样B就可以做到中间人了,可以接受到两个主机的数据了。换句话来说,在同一个局域网内,我们要黑掉一个主机,只需要构建大量的arp应答,把自己的mac地址发送给要黑掉的主机,这样他发送的所有数据都会经过你的主机,如果你不管,也不转发,那么,恭喜你,成功让他的主机无法上网了。但是其实现在就算是黑掉也没什么用,因为https中经过加密,还有认证,一改数据就会被发现。所以没有什么意义。但是你可以黑掉同一个局域网中的另一台主机,让他无法上网。
说来说去,原理我知道了,但是我们应该怎么实现呢?此时我们就要回忆一下套接字类型了,有三种。流式套接字,数据报套接字,原始套接字。流式套接字对应的是tcp,数据包套接字是udp,那么答案已经出来了吧,不错,我们可以用原始套接字来实现伪造arp数据包。那么怎么实现呢?代码如下:
#include <iostream>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ether.h>
#include <cstring>
#include <unistd.h>
#define BUF_SIZE 1024
#define DST_SIZE 7
#define SRC_SIZE 7
int main()
{
int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sockfd < 0)
{
std::cout << "socket fail" << std::endl;
exit(1);
}
unsigned char buffer[1024];
memset(buffer, 0, 1024);
while (true)
{
ssize_t s = recvfrom(sockfd, buffer, 1024, 0, nullptr, nullptr);
if (s < 0)
{
std::cout << "recvfrom fail" << std::endl;
exit(2);
}
// 以太网数据包头
std::cout << "以太网数据包报头" << std::endl;
unsigned short type = 0;
unsigned char dst[DST_SIZE] = "";
unsigned char src[SRC_SIZE] = "";
type = ntohs(*(unsigned short *)(buffer + 12));
for (size_t i = 0; i < 6; i++)
dst[i] = buffer[i];
for (size_t i = 6; i < 12; i++)
src[i - 6] = buffer[i];
printf("目的mac:%x%x%x%x%x%x", dst[0], dst[1], dst[2], dst[3], dst[4], dst[5]);
printf("源mac:%x%x%x%x%x%x", src[0], src[1], src[2], src[3], src[4], src[5]);
printf("类型:%x", type);
std::cout << std::endl;
// arp协议数据报头
std::cout << "arp数据包报头" << std::endl;
// 硬件类型
unsigned short hardware;
// 协议类型
unsigned short pro;
// 硬件地址长度
unsigned char hardware_len;
// 协议长度
unsigned char pro_len;
// op
unsigned short op;
// 发送方mac地址
unsigned char src_mac[7] = "\0";
// 发送方ip地址
std::string src_ip;
// 目的mac地址
unsigned char dst_mac[7] = "\0";
// 目的ip地址
std::string dst_ip;
///
hardware = ntohs(*(unsigned short *)(buffer + 14));
pro = ntohs(*(unsigned short *)(buffer + 16));
hardware_len = buffer[18];
pro_len = buffer[19];
op = ntohs(*(unsigned short *)(buffer + 20));
for (size_t i = 22; i < 28; i++)
src_mac[i - 22] = buffer[i];
src_ip = inet_ntoa(*(in_addr *)(buffer + 28));
for (size_t i = 32; i < 38; i++)
dst_mac[i - 32] = buffer[i];
dst_ip = inet_ntoa(*(in_addr *)(buffer + 38));
/
printf("硬件类型:%x ", hardware);
printf("协议类型:%x ", pro);
printf("硬件长度:%x ", hardware_len);
printf("协议长度:%x ", pro_len);
printf("op:%x ", op);
printf("源mac:%x%x%x%x%x%x ", src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5]);
std::cout << "源ip" << src_ip << " ";
printf("目的mac:%x%x%x%x%x%x ", dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5]);
std::cout << "目的ip" << dst_ip << " ";
std::cout << std::endl;
}
// 完成之后关闭文件
close(sockfd);
return 0;
}
以上是我实现的一个类似于抓包工具(不是抓包工具,就是类似于)。他可以获取我们局域网中的所有数据包。并且可以解析。这个代码只有读取,没有发送,因为我用的是云服务器,这个云服务器所在局域网不是我所在的(害怕弄出麻烦来)。所以我就只进行了读取,但是如果你想测试的话,可以试试,用sendto就可以,但是在用sendto时,我们有几个地方与udp中使用的不一样,这个我就不在这里详细说了,想知道的小伙伴可以私信问或是上网查查。那么此时我们应该怎么用呢?测试如下:
我开了两个端口,一个是用来删除arp缓存中的默认网关的地址的,一个来运行。我们可以清楚的看见,这个我删除了之后,有一个arp的应答,所以我们可以得知,在这之前,肯定有一个请求。
所以这里也证明了网络中是有Arp请求和应答的,你可以等一会,你会看见另一个arp请求,这个就证明了他是有时间限制的。(时间可能有点长)
最后最后,小伙伴们千万不要胡乱试,这个如果出了差错的话,额......不过可以在虚拟机上进行试试。最后,如果大家看完本章内容有所收获的话,希望点一下赞吧!谢谢!!
如果有小伙伴对网络的数据包怎么发送与接收不清楚的,也不要太着急,下一次会发整个网络中的数据包是怎么发和怎么收的。