- Hardware Type:2 bytes。
- Protocol Type:2 bytes。
- Hardware Length:1 byte。
- Protocol Length:1 byte。
- Operation Code:2 bytes。
- Sender Hardware Address:硬體地址長度 bytes。
- Sender Protocol Address:協定地址長度 bytes。
- Target Hardware Address:硬體地址長度 bytes。
- Target Protocol Address:協定地址長度 bytes。
- 長度:8 + 硬體地址長度×2 + 協定地址長度×2 bytes。
表頭橘色部分長度是依照Hardware Length以及Protocol Length決定的,那麼以Ethernet來講,硬體地址長度是6(MAC Address),協定地址長度是4(IP Address),所以Ethernet的ARP封包長度為:8+6*2+4*2 = 28 bytes。
ARP協定並不是只有Ethernet使用而已。
要多include的header:
#include <net/if_arp.h>
一樣開啟預設的device。
char errbuf[PCAP_ERRBUF_SIZE]; char *device = NULL; //get default interface name device = pcap_lookupdev(errbuf); if(!device) { fprintf(stderr, "pcap_lookupdev(): %s\n", errbuf); exit(1); }//end if printf("Sniffing: %s\n", device); pcap_t *handle = pcap_open_live(device, 65535, 1, 1, errbuf); if(!handle) { fprintf(stderr, "pcap_open_live(): %s\n", errbuf); exit(1); }//end if
為了方便解析,這次加入了過濾器(BPF filter)的方式,只對arp封包有興趣。
宣告會用到的變數。
//generate bpf filter bpf_u_int32 net, mask; struct bpf_program fcode;
產生過濾器的時候,會需要netmask,所以先呼叫函數
pcap_lookupnet()
來取得netmask。//get network and mask if(-1 == pcap_lookupnet(device, &net, &mask, errbuf)) { fprintf(stderr, "pcap_lookupnet(): %s\n", errbuf); mask = PCAP_NETMASK_UNKNOWN; }//end if
但是netmask不是一定需要的,所以如果當取得失敗的時候,使用marcos
PCAP_NETMASK_UNKNOWN
當作netmask使用就好了。巨集
PCAP_NETMASK_UNKNOWN
宣告:/* * Value to pass to pcap_compile() as the netmask if you don't know what * the netmask is. */ #define PCAP_NETMASK_UNKNOWN 0xffffffff
接著來產生過濾器。
//compile filter if(-1 == pcap_compile(handle, &fcode, "arp", 1, mask)) { fprintf(stderr, "pcap_compile(): %s\n", pcap_geterr(handle)); pcap_close(handle); exit(1); }//end if
函數
pcap_compile()
能夠將filter expression轉成電腦使用的結構體,這邊使用"arp"表示只要arp封包。過濾器表達式(filter expression)教學。
函數
pcap_compile()
原型:int pcap_compile(pcap_t *p, struct bpf_program *program, char *buf, int optimize, bpf_u_int32 mask);
- 返回值:成功傳回0,失敗傳回-1,錯誤訊息從pcap_geterr()取得。
- 參數:p一個libpcap handle。program最後過濾器結果儲存的結構指標。buf過濾器表達式。optimize是否要最佳化過濾器表達式。mask要設定的那個device的遮罩netmask,可以使用PCAP_NETMASK_UNKNOWN代替。
- 功能:將過濾器表達式轉成核心能夠使用過濾器結構。
接著將產生的結構體設定在handle上。
//set filter if(-1 == pcap_setfilter(handle, &fcode)) { fprintf(stderr, "pcap_pcap_setfilter(): %s\n", pcap_geterr(handle)); pcap_freecode(&fcode); pcap_close(handle); exit(1); }//end if
函數
pcap_setfilter()
原型:int pcap_setfilter(pcap_t *p, struct bpf_program *program);
- 返回值:成功傳回0,失敗傳回-1,錯誤訊息從pcap_geterr()取得。
- 參數:p一個libpcap handle。program過濾器結構指標。
- 功能:設定過濾器到libpcap handle上。
使用完過濾器的結構體之後,不需要的時候要釋放掉。
//free bpf code pcap_freecode(&fcode);
函數
pcap_freecode()
原型:void pcap_freecode(struct bpf_program *program);
- 參數:program要釋放的過濾器結構。
- 功能:釋放從pcap_compile()產生的過濾器結構。
接著只抓兩個封包。
//start capture pcap_loop(handle, 2, pcap_callback, NULL);
解析ethernet過程中,當下一層的類型是0x0806(或marcos
ETHERTYPE_ARP
),就呼叫函數dump_arp()
來解析arp封包。printf("Next protocol is "); switch (type) { case ETHERTYPE_ARP: printf("ARP\n"); dump_arp(length, content); break; case ETHERTYPE_IP: printf("IP\n"); break; case ETHERTYPE_REVARP: printf("RARP\n"); break; case ETHERTYPE_IPV6: printf("IPv6\n"); break; default: printf("%#06x\n", type); break; }//end switch
函數
dump_arp()
是用來解析arp封包的函數,一開始先宣告一些封包表頭欄位。u_short hdr_type; u_short pro_type; u_char hdr_len; u_char pro_len; u_short op; char sender_mac[MAC_ADDRSTRLEN] = {0}; char sender_ip[INET_ADDRSTRLEN] = {0}; char target_mac[MAC_ADDRSTRLEN] = {0}; char target_ip[INET_ADDRSTRLEN] = {0};
struct ether_arp *arp = (struct ether_arp *)(content + ETHER_HDR_LEN);
結構
struct ether_arp
是ethernet常用的arp封包表頭結構體;起始位置當然就是ethernet表頭後的位置,所以是content + 14(或marcos ETHER_HDR_LEN
)。結構
struct ether_arp
宣告:/* * Ethernet Address Resolution Protocol. * * See RFC 826 for protocol description. Structure below is adapted * to resolving internet addresses. Field names used correspond to * RFC 826. */ struct ether_arp { struct arphdr ea_hdr; /* fixed-size header */ u_char arp_sha[ETHER_ADDR_LEN]; /* sender hardware address */ u_char arp_spa[4]; /* sender protocol address */ u_char arp_tha[ETHER_ADDR_LEN]; /* target hardware address */ u_char arp_tpa[4]; /* target protocol address */ }; #define arp_hrd ea_hdr.ar_hrd #define arp_pro ea_hdr.ar_pro #define arp_hln ea_hdr.ar_hln #define arp_pln ea_hdr.ar_pln #define arp_op ea_hdr.ar_op
結構
struct arphdr
宣告:/* * Address Resolution Protocol. * * See RFC 826 for protocol description. ARP packets are variable * in size; the arphdr structure defines the fixed-length portion. * Protocol type values are the same as those for 10 Mb/s Ethernet. * It is followed by the variable-sized fields ar_sha, arp_spa, * arp_tha and arp_tpa in that order, according to the lengths * specified. Field names used correspond to RFC 826. */ struct arphdr { u_short ar_hrd; /* format of hardware address */ #define ARPHRD_ETHER 1 /* ethernet hardware format */ #define ARPHRD_IEEE802 6 /* token-ring hardware format */ #define ARPHRD_FRELAY 15 /* frame relay hardware format */ #define ARPHRD_IEEE1394 24 /* IEEE1394 hardware address */ #define ARPHRD_IEEE1394_EUI64 27 /* IEEE1394 EUI-64 */ u_short ar_pro; /* format of protocol address */ u_char ar_hln; /* length of hardware address */ u_char ar_pln; /* length of protocol address */ u_short ar_op; /* one of: */ #define ARPOP_REQUEST 1 /* request to resolve address */ #define ARPOP_REPLY 2 /* response to previous request */ #define ARPOP_REVREQUEST 3 /* request protocol address given hardware */ #define ARPOP_REVREPLY 4 /* response giving protocol address */ #define ARPOP_INVREQUEST 8 /* request to identify peer */ #define ARPOP_INVREPLY 9 /* response identifying peer */ /* * The remaining fields are variable in size, * according to the sizes above. */ #ifdef COMMENT_ONLY u_char ar_sha[]; /* sender hardware address */ u_char ar_spa[]; /* sender protocol address */ u_char ar_tha[]; /* target hardware address */ u_char ar_tpa[]; /* target protocol address */ #endif };
這邊有個要注意的地方:結構
struct ether_arp
的arp_spa和arp_tpa被宣告成u_char [4]
,這兩個是指ip地址,常理應該會宣告成無號的32 bit整數,雖然都是4個byte,但是這樣隱含的問題就是結構的對齊(Struct Member Alignment)。這兩個成員前面都是u_char,預設情況會結構的成員只要資料型態不同就會試著對齊到4的倍數,所以結果會有問題。
當然也可以取消掉對齊,在
gcc
編譯器使用__attribute__((packed))
宣告。像是:
struct my_ether_arp { struct arphdr ea_hdr; /* fixed-size header */ u_char arp_sha[ETHER_ADDR_LEN]; /* sender hardware address */ u_int32_t arp_spa; /* sender protocol address */ u_char arp_tha[ETHER_ADDR_LEN]; /* target hardware address */ u_int32_t arp_tpa[4]; /* target protocol address */ } __attribute__((packed));
Windows的
cl
編譯器(VC++
)則是:#pragma pack(push, 1) struct my_ether_arp { struct arphdr ea_hdr; /* fixed-size header */ u_char arp_sha[ETHER_ADDR_LEN]; /* sender hardware address */ u_int32_t arp_spa; /* sender protocol address */ u_char arp_tha[ETHER_ADDR_LEN]; /* target hardware address */ u_int32_t arp_tpa[4]; /* target protocol address */ }; #pragma pack(pop)
通用解法:
#ifdef _WIN32 #define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop) ) #else #define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) #endif
使用方法:
PACK ( struct my_ether_arp { struct arphdr ea_hdr; /* fixed-size header */ u_char arp_sha[ETHER_ADDR_LEN]; /* sender hardware address */ u_int32_t arp_spa; /* sender protocol address */ u_char arp_tha[ETHER_ADDR_LEN]; /* target hardware address */ u_int32_t arp_tpa[4]; /* target protocol address */ });
這樣就不需要特別判斷使用哪個編譯器了。
接著就將標頭欄位複製到變數內。
//copy header hdr_type = ntohs(arp->arp_hrd); pro_type = ntohs(arp->arp_pro); hdr_len = arp->arp_hln; pro_len = arp->arp_pln; op = ntohs(arp->arp_op); snprintf(sender_mac, sizeof(sender_mac), "%s", mac_ntoa(arp->arp_sha)); snprintf(sender_ip, sizeof(sender_ip), "%s", ip_ntoa(arp->arp_spa)); snprintf(target_mac, sizeof(target_mac), "%s", mac_ntoa(arp->arp_tha)); snprintf(target_ip, sizeof(target_ip), "%s", ip_ntoa(arp->arp_tpa));
其中函數
ip_ntoa()
是將網路地址格式轉成字串格式。其中做法就跟函數
mac_ntoa()
一樣。static const char *ip_ntoa(void *i) { static char ip[STR_BUF][INET_ADDRSTRLEN]; static int which = -1; which = (which + 1 == STR_BUF ? 0 : which + 1); memset(ip[which], 0, INET_ADDRSTRLEN); inet_ntop(AF_INET, i, ip[which], sizeof(ip[which])); return ip[which]; }//end ip_ntoa
只是用函數
inet_ntop()
來將網路地址轉成字串格式;就不檢查回傳值了。static char *arp_op_name[] = { "Undefine", "(ARP Request)", "(ARP Reply)", "(RARP Request)", "(RARP Reply)" }; //arp option type if(op < 0 || sizeof(arp_op_name)/sizeof(arp_op_name[0]) < op) op = 0;
宣告變數arp_op_name是根據每個arp的operation code的數值來宣告對應的index位置,例如arp request的operation code為1,arp reply的operation code為2,接來的if判斷只是確保不會超過index範圍。
最後就將封包列印出來。
//print printf("Protocol: ARP\n"); printf("+-------------------------+-------------------------+\n"); printf("| Hard Type: %2u%-11s| Protocol: %#06x%-8s|\n", hdr_type, (hdr_type == ARPHRD_ETHER) ? "(Ethernet)" : "(Not Ether)", pro_type, (pro_type == ETHERTYPE_IP) ? "(IP)" : "(Not IP)"); printf("+------------+------------+-------------------------+\n"); printf("| Hard Len:%2u| Addr Len:%2u| OP: %4d%16s|\n", hdr_len, pro_len, op, arp_op_name[op]); printf("+------------+------------+-------------------------+-------------------------+\n"); printf("| Sender MAC Address: %17s|\n", sender_mac); printf("+---------------------------------------------------+-------------------------+\n"); printf("| Sender IP Address: %15s|\n", sender_ip); printf("+---------------------------------------------------+-------------------------+\n"); printf("| Target MAC Address: %17s|\n", target_mac); printf("+---------------------------------------------------+-------------------------+\n"); printf("| Target IP Address: %15s|\n", target_ip); printf("+---------------------------------------------------+\n");
編譯:
libpcap % gcc -I/usr/local/opt/libpcap/include -Wall -std=gnu99 -L/usr/local/opt/libpcap/lib -lpcap dump-arp.c -o dump-arp
執行結果(Mac OS X):
libpcap % ./dump-arp Sniffing: en0 No. 1 Time: 16:31:08.240019 Length: 58 bytes Capture length: 58 bytes Ethernet Frame: +-------------------------+-------------------------+-------------------------+ | Destination MAC Address: ff:ff:ff:ff:ff:ff| +-------------------------+-------------------------+-------------------------+ | Source MAC Address: 6c:40:08:bc:ae:98| +-------------------------+-------------------------+-------------------------+ | Ethernet Type: 0x0806| +-------------------------+ Next protocol is ARP Protocol: ARP +-------------------------+-------------------------+ | Hard Type: 1(Ethernet) | Protocol: 0x0800(IP) | +------------+------------+-------------------------+ | Hard Len: 6| Addr Len: 4| OP: 1 (ARP Request)| +------------+------------+-------------------------+-------------------------+ | Sender MAC Address: 6c:40:08:bc:ae:98| +---------------------------------------------------+-------------------------+ | Sender IP Address: 192.168.1.50| +---------------------------------------------------+-------------------------+ | Target MAC Address: 00:00:00:00:00:00| +---------------------------------------------------+-------------------------+ | Target IP Address: 192.168.1.1| +---------------------------------------------------+ No. 2 Time: 16:31:08.246209 Length: 42 bytes Capture length: 42 bytes Ethernet Frame: +-------------------------+-------------------------+-------------------------+ | Destination MAC Address: 6c:40:08:bc:ae:98| +-------------------------+-------------------------+-------------------------+ | Source MAC Address: d8:fe:e3:a4:d3:78| +-------------------------+-------------------------+-------------------------+ | Ethernet Type: 0x0806| +-------------------------+ Next protocol is ARP Protocol: ARP +-------------------------+-------------------------+ | Hard Type: 1(Ethernet) | Protocol: 0x0800(IP) | +------------+------------+-------------------------+ | Hard Len: 6| Addr Len: 4| OP: 2 (ARP Reply)| +------------+------------+-------------------------+-------------------------+ | Sender MAC Address: d8:fe:e3:a4:d3:78| +---------------------------------------------------+-------------------------+ | Sender IP Address: 192.168.1.1| +---------------------------------------------------+-------------------------+ | Target MAC Address: 6c:40:08:bc:ae:98| +---------------------------------------------------+-------------------------+ | Target IP Address: 192.168.1.50| +---------------------------------------------------+
執行結果(CentOS):
[root@tutu libpcap]# ./dump-arp Sniffing: eth0 No. 1 Time: 15:04:15.011590 Length: 42 bytes Capture length: 42 bytes Ethernet Frame: +-------------------------+-------------------------+-------------------------+ | Destination MAC Address: ff:ff:ff:ff:ff:ff| +-------------------------+-------------------------+-------------------------+ | Source MAC Address: 00:0c:29:b0:81:f7| +-------------------------+-------------------------+-------------------------+ | Ethernet Type: 0x0806| +-------------------------+ Next protocol is ARP Protocol: ARP +-------------------------+-------------------------+ | Hard Type: 1(Ethernet) | Protocol:0x0800(IP) | +------------+------------+-------------------------+ | HardLen: 6| Addr Len: 4| OP: 1 (ARP Request)| +------------+------------+-------------------------+-------------------------+ | Sender MAC Address: 00:0c:29:b0:81:f7| +---------------------------------------------------+-------------------------+ | Sender IP Address: 192.168.1.67| +---------------------------------------------------+-------------------------+ | Target MAC Address: ff:ff:ff:ff:ff:ff| +---------------------------------------------------+-------------------------+ | Target IP Address: 192.168.1.1| +---------------------------------------------------+ No. 2 Time: 15:04:15.018333 Length: 60 bytes Capture length: 60 bytes Ethernet Frame: +-------------------------+-------------------------+-------------------------+ | Destination MAC Address: 00:0c:29:b0:81:f7| +-------------------------+-------------------------+-------------------------+ | Source MAC Address: d8:fe:e3:a4:d3:78| +-------------------------+-------------------------+-------------------------+ | Ethernet Type: 0x0806| +-------------------------+ Next protocol is ARP Protocol: ARP +-------------------------+-------------------------+ | Hard Type: 1(Ethernet) | Protocol:0x0800(IP) | +------------+------------+-------------------------+ | HardLen: 6| Addr Len: 4| OP: 2 (ARP Reply)| +------------+------------+-------------------------+-------------------------+ | Sender MAC Address: d8:fe:e3:a4:d3:78| +---------------------------------------------------+-------------------------+ | Sender IP Address: 192.168.1.1| +---------------------------------------------------+-------------------------+ | Target MAC Address: 00:0c:29:b0:81:f7| +---------------------------------------------------+-------------------------+ | Target IP Address: 192.168.1.67| +---------------------------------------------------+
Source code on Github
沒有留言:
張貼留言