libnet
libnet目前官方網站"似乎"已經停止維護了(libnet-0.10.11),所以到這裡使用新版本libnet-1.2-rc3。
雖然libnet很方便撰寫封包,但是個人發現容易讓初學者混淆一些觀念(而且在Mac OS X上有bug,暈...),但要跨平台最好使用Library,所以就見仁見智囉,個人則是喜歡直接寫Raw socket。
Bug Fix
在Mac OS X上使用libnet有個bug,就是無法修改Source MAC Address,問題出在原始碼src/libnet_link_bpf.c
:
...略
100 #if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT) && !(__APPLE__)
101 uint spoof_eth_src = 1;
102 #endif
...略
162 /*
163 * NetBSD and FreeBSD BPF have an ioctl for enabling/disabling
164 * automatic filling of the link level source address.
165 */
166 #if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT) && !(__APPLE__)
167 if (ioctl(l->fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1)
168 {
169 snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, "%s(): BIOCSHDRCMPLT: %s",
170 __func__, strerror(errno));
171 goto bad;
172 }
173 #endif
使用BSD
核心的系統中(包含Mac OS X啦),預設會自動填寫Source MAC Address,如果要關掉該選項,要使用ioctl()
的BIOCSHDRCMPLT
參數關閉,可是這裡的判斷是:#if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT) && !(__APPLE__)
,它把__APPLE__
給否定了,所以改成:
...略
100 #if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT) && (__APPLE__)
101 uint spoof_eth_src = 1;
102 #endif
...略
162 /*
163 * NetBSD and FreeBSD BPF have an ioctl for enabling/disabling
164 * automatic filling of the link level source address.
165 */
166 #if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT) && (__APPLE__)
167 if (ioctl(l->fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1)
168 {
169 snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, "%s(): BIOCSHDRCMPLT: %s",
170 __func__, strerror(errno));
171 goto bad;
172 }
173 #endif
把!
拿掉,就可以正確判斷並關掉自動填寫Source MAC Address這個選項了。
編譯
一樣記得指定--prefix
參數。
先建立安裝路徑。
~ % mkdir /usr/local/TU/libnet
然後切到libnet原始碼目錄使用configure
腳本。
libnet-1.2-rc3 % ./configure --prefix=/usr/local/TU/libnet
beginning autoconfiguration process for libnet-1.2-rc3 ...
checking build system type... x86_64-apple-darwin15.2.0
...略
config.status: executing depfiles commands
config.status: executing libtool commands
然後make
編譯。
libnet-1.2-rc3 % make
Making all in include
/Applications/Xcode.app/Contents/Developer/usr/bin/make all-recursive
...略
make[1]: Nothing to be done for `all'.
make[1]: Nothing to be done for `all-am'.
最後make install
安裝。
libnet-1.2-rc3 % make install
Making install in include
Making install in libnet
...略
./install-sh -c -d '/usr/local/bin'
/usr/bin/install -c libnet-config '/usr/local/bin'
make[2]: Nothing to be done for `install-data-am'.
安裝完成~
Sample
在libnet的原始碼內有個資料夾sample在安裝完後有不少範例可以使用。
sample % ls
Makefile fddi_tcp1 icmp_timestamp ospf_lsa.o tcp1.o
Makefile.am fddi_tcp1.c icmp_timestamp.c ping_of_death tcp2
Makefile.in fddi_tcp1.o icmp_timestamp.o ping_of_death.c tcp2.c
arp fddi_tcp2 icmp_unreach ping_of_death.o tcp2.o
arp.c fddi_tcp2.c icmp_unreach.c rpc_tcp test_ipv4
arp.o fddi_tcp2.o icmp_unreach.o rpc_tcp.c test_ipv4.c
bgp4_hdr get_addr ieee rpc_tcp.o test_ipv4.o
bgp4_hdr.c get_addr.c ieee.c rpc_udp test_ipv4_options
...略
我們就來挑一個來試用看看。
sample % sudo ./ospf_hello 192.168.1.100 192.168.1.1
libnet 1.1 OSPF Hello packet shaping[raw]
Wrote 68 byte OSPF packet; check the wire.
結果:
用範例就可以簡單送出一個OSPF Hello
封包了,這個sample資料夾內有不少範例可以參考。
Xcode and Command Line Environment Settings
這邊在libpcap教學已經教學過了,只有路徑稍微不同,所以就不再多些不必要的文字簡介。
Xcode
Header的Search Paths
給/usr/local/TU/libnet/include
,lib的Library Search Paths
則是/usr/local/TU/libnet/lib
。
找到Linking
的Other Linker Flags
裡新增-lnet
。
有時候需要開啟Network Layer
的Raw socket,而開啟Network Layer
的Raw socket一定要root權限,所以來讓它能夠以root權限執行程式。
左上角Scheme->Edit Scheme...
。
中間的Debug Process As
選root
就可以了。
第一次編譯的時候會需要輸入密碼。
Command Line
在指令列上在呼叫指令前加上sudo
就好了(如果在有/etc/sudoers
內,預設有)。
~ % gcc main.c -o hello -I /usr/local/TU/libnet/include -L /usr/local/TU/libnet/lib -lnet
~ % sudo ./hello
Password:
Hello TUTU
一樣的要用-I
增加include搜尋路徑,-L
增加library搜尋路徑,-l
要連結library,第一次一樣需要輸入密碼。
在Windows上使用libnet需要搭配Winpcap才能夠使用。
Raw socket
這邊就要來介紹什麼是Raw socket啦,首先來講Socket。
所謂的Socket指的是兩個程序(Process)互相溝通的一個管道,而這兩個程序不限制在同一台電腦上可以在不同台電腦上,而網路用的Socket設計方式分成七層。
在OSI七層模型中,TCP/IP分成五層:
Application Layer |
Transport Layer |
Network Layer |
Data-link Layer |
Physical Layer |
而在乙太網路中Data-link Layer
和Physical Layer
可以直接當成同一層看待。
一般Socket只能夠存取Application Layer
的資料,不能存取其他層資訊。
Application Layer(∨) |
Transport Layer(×) |
Network Layer(×) |
Data-link Layer(×) |
那只要能夠存取Transport Layer
以下的Socket通稱Raw socket,如果要存取Network Layer
以下需要有root權限(函數setsockopt()
其實能夠稍微修改一些Network Layer
的欄位資料,不過這不是我們要講的)。
也就是說:
- Socket只能填寫
Application Layer
,以下自動填。 Transport Layer
的Raw socket,Transport Layer
以上需要手動填寫,以下(不包含Transport Layer
)系統填寫。Network Layer
的Raw socket,Network Layer
以上需要手動填寫,以下(不包含Network Layer
)系統填寫。Data-link Layer
的Raw socket,Data-link Layer
以上需要手動填寫,以下(不包含Data-link Layer
)系統填寫(當然啦,表尾CRC系統自己算)。Data-link Layer
的Raw socket可以拿來當作Sniffer,因為要從網卡讀到的封包才有意義,從Network Layer
讀取封包有問題,有些系統無法透過Network Layer
的Raw scoket抓取TCP封包,因為有Port binding的問題。
那什麼時候該選擇怎樣的Socket呢?
左邊是一般Socket情況,包含了Socket、Transport Layer
的Raw socket以及Network Layer
的Raw socket,這三種Socket在送出去後,會交由給核心的Route table決定要往哪個Interface送出,如果要送往乙太介面(en0
和en1
)會再經過ARP table填寫最後的MAC Address,最後才會交給NIC(網卡)送至網路上。
右邊是Data-link Layer
的Raw socket,送出去會直接交給NIC(網卡)送至網路上,所以MAC Address必須自己填寫。
假如寫ARP封包,就開啟Data-link Layer
的Raw socket,其他要寫IPv4或IPv6的封包,就看需求開啟哪層的Raw socket。
如果開啟Network Layer
以上的Raw socket,目的IP地址假如是在同一個區域網路下但並不存在,會造成封包送到ARP table時無法填入目的MAC Address,所以封包就無法送出,如果一定要送出這個假封包,就必須開啟Data-link Layer
的Raw socket並填寫Ethernet的表頭欄位才能夠送出。
libnet Support Protocol
Application Layer
- Border Gateway Protocol
- Remote Procedure Call
- Domain Name System
- Network Time Protocol
- Bootstrap Protocol
- Dynamic Host Configuration Protocol
Transport Layer
- Transmission Control Protocol
- User Datagram Protocol
Network Layer
- Routing Information Protocol
- Open Shortest Path First
- Virtual Router Redundancy Protocol
- Internet Control Message Protocol
- Internet Group Management Protocol
- Internet Protocol version 4
- Internet Protocol version 6
- Generic Routing Encapsulation
- Encapsulating Security Payloads
- Authentication Headers
Data-link Layer
- Cisco Discovery Protocol
- Fiber Distributed Data Interface
- IEEE 802.1x
- Ethernet
- IEEE 802.1Q
- IEEE 802.3
- Multi-Protocol Label Switching
- IEEE 802.5 LAN Protocol(Token Ring)
- IEEE 802.2
- Address Resolution Protocol
- Reverse Address Resolution Protocol
- SubNetwork Access Protocol
libnet不只有這些協定,以防誤人子弟我只列出我能夠理解的協定。
Source Code
- libnet/address_conversion.c
- libnet/send_an_arp_request.c
- libnet/arpoison.c
- libnet/icmp_echo_request_list.c
- libnet/udp_port_chain.c
- libnet/tcp_syn_flood.c
- libnet/ssdp.c
Compile Source Code
一樣下載我所提供的原始碼並且libnet和libpcap安裝路徑為/usr/local/TU/libnet
和/usr/local/TU/libpcap
,也可以直接下make
指令一次編譯所有檔案。
libnet % make
/usr/bin/gcc -lnet address_conversion.c -o address_conversion
/usr/bin/gcc -lnet send_an_arp_request.c -o send_an_arp_request
/usr/bin/gcc -lnet arpoison.c -o arpoison
/usr/bin/gcc -lnet icmp_echo_request_list.c -o icmp_echo_request_list
/usr/bin/gcc -lnet udp_port_chain.c -o udp_port_chain
/usr/bin/gcc -lnet tcp_syn_flood.c -o tcp_syn_flood
/usr/bin/gcc -lnet -lpcap ssdp.c -o ssdp
同樣如果不是照我的路徑安裝,直接修改檔案Makefile
內的變數ROOT_DIR
。
如果只要編譯單一程式,指令make 程式
就可以了,例如要編譯ssdp.c
。
libnet % make ssdp
/usr/bin/gcc -lnet -lpcap ssdp.c -o ssdp
指令make clean
可以清除所有編譯完成的執行檔。
libnet % make clean
rm -f address_conversion send_an_arp_request arpoison icmp_echo_request_list udp_port_chain tcp_syn_flood ssdp
小結
libnet只介紹一小部分的封包,其他封包協定等有需要時再來回頭翻文件學習就好囉。
可能會有人想使用libnet來寫TCP連線的程式,但實際上用Raw socket是無法達成的,在後期Raw socket篇時才會講解如何完成TCP連線並傳送資料。
所以libnet用來撰寫Connectionless
的封包很方便(像是ARP、ICMP、IP、UDP、DNS...等),但在Connection-Oriented
的封包(像是TCP、HTTP、SSH)就非常鱉腳。
如果要關鍵字的話,使用函數
connect()
。