BOOTP協議 / BOOTP Protocol
目錄
BOOTP(Bootstrap Protocol)是一種引導協議,基於RFC951協議,基於UDP協議,也稱為自舉協議,是DHCP協議的前身。BOOTP用於無盤工作站(類似網吧無盤結構)的局域網中,可以讓無盤工作站從一個中心服務器上獲得IP地址。通過BOOTP協議可以為局域網中的無盤工作站分配動態IP地址,這樣就不需要管理員去為每個用戶去設置靜態IP地址。
BOOTP使用UDP報文傳輸,並使用保留端口號67(BOOTP服務器,客戶端使用此端口作為目標端口發送請求,通常是廣播)和68(BOOTP客戶端,服務器使用此端口作為目標端口發送應答)工作。使用BOOTP協議的時候,一般包括Bootstrap Protocol Server(自舉協議服務端)和Bootstrap Protocol Client(自舉協議客戶端)兩部分。
2. BOOTP 與 DHCP / BOOTP and DHCP
BOOTP協議(BOOTstrap Protocol): 引導程序協議,是一種C/S協議,克服了RARP協議的兩個缺陷,具體表現為:(1)BOOTP協議是一個C/S程序,BOOTP服務器可以位於Internet的任何地方;(2)BOOT協議除了返回IP地址外,還提供其他配置信息(如子網掩碼等),這些信息可以記錄在BOOTP報文的選項部分。但是BOOTP協議的缺陷也很明顯:BOOTP協議是一個靜態配置協議。也就是說當客戶請求自己的IP地址時,BOOTP服務器就會查找一張(MAC-->IP)的映射表,這種映射關系必須是事先設定好的。也就是說:BOOTP協議中,MAC地址和IP地址之間的綁定關系是靜態的,是固定存放在一張表中,除非管理員更改這張表。所以后來就提出了現在耳熟能詳的DHCP協議。
DHCP協議(Dynamic Host Configuration Protocal):動態主機配置協議。同BOOTP協議一樣,DHCP協議也是基於C/S方式的,DHCP是BOOTP的繼承者,並且能夠兼容BOOTP。DHCP不僅能夠處理靜態配置(此時等同於BOOTP協議),而且能夠處理動態配置。也就是說,在客戶在查找其自己的IP地址時,DHCP服務器中(MAC-->IP)地址可以事先存在,也可以臨時分配。換句話說:當客戶發送DHCP請求報文時,DHCP服務器服務器先在其數據庫中查找該計算機的配置信息。若找到,則返回找到的信息。若找不到,則從服務器的IP地址池中取一個地址分配給該計算機。
芯片中的BOOTP啟動代碼啟動客戶端,此時客戶端還沒有地址,因此客戶端以0.0.0.0為本機地址,向255.255.255.255:67廣播一個請求報文,服務端接收到請求報文后進行檢驗,並將配置的offer_ip及需要下載啟動的文件名插入返回報文,廣播回客戶端,客戶端接收后根據信息啟動TFTP下載啟動文件。傳輸過程中包含了重傳策略,服務器接收檢驗及轉發處理等(RFC951)。
此處包含一個雞和蛋的問題,即如果客戶端不知道自己IP地址,服務器怎么發送IP報文到客戶端。
無論何時一條引導應答被發送,發送設備執行下列操作:
1.如果客戶端知道自己的IP地址('ciaddr'字段非零),因為客戶端能夠回應ARPs (Address Resolution Protocol, 6.4.1.2),那么IP能夠正常發送。
2.如果客戶端還不知道自己的IP地址(ciaddr是零),客戶端就不能回應引導應答發送程序回的ARPs。這時有兩種選擇:
a.如果發送程序有必需的核心或驅動鈎子程序來人工建立ARP地址緩沖條目,就可以使用'chaddr'和'yiaddr'字段填入一個條目。當然,這個條目象正常ARP建立的其它條目一樣有一個生命時間,引導應答的發送程序就能夠簡單地發送引導應答到客戶端的IP地址了。UNIX (4.2BSD)有這種功能。
b.如果發送程序缺少這些核心鈎子程序,就只能簡單發送引導應答到相應接口的廣播地址。這只是在前面情況外的額外的廣播。
通信流程大致如下,
(0.0.0.0) Client --(<broadcast>, 67)--> Server
TFTP <---- Ip and file <--(<broadcast>, 68)-- (offer ip and boot file name)
Note: ARP(Address Resolution Protocol),是根據IP地址獲取物理(mac)地址的一個TCP/IP協議。主機發送信息時將包含目標IP地址的ARP請求廣播到網絡上的所有主機,並接收返回消息,以此確定目標的物理地址;收到返回消息后將該IP地址和物理地址存入本機ARP緩存中並保留一定時間,下次請求時直接查詢ARP緩存以節約資源。地址解析協議是建立在網絡中各個主機互相信任的基礎上的,網絡上的主機可以自主發送ARP應答消息,其他主機收到應答報文時不會檢測該報文的真實性就會將其記入本機ARP緩存;由此攻擊者就可以向某一主機發送偽ARP應答報文,使其發送的信息無法到達預期的主機或到達錯誤的主機,這就構成了一個ARP欺騙。ARP命令可用於查詢本機ARP緩存中IP地址和MAC地址的對應關系、添加或刪除靜態對應關系等。相關協議有RARP、代理ARP。NDP用於在IPv6中代替地址解析協議。
4 數據報文格式 / Data Message Format
字段 字節數 位號 描述
------ --------- ------ ------
Op 1 1 Packet op code / Message type.包操作碼/消息類型,
1 = BOOTREQUEST(引導請求), 2 = BOOTREPLY(引導應答)
Htype 1 2 Hardware address type, 硬件地址類型
'1' = 10mb ethernet 10M以太網
Hlen 1 3 Hardware address length,硬件地址長度
(eg '6' for 10mb ethernet). 例如'6'是10M以太網
Hops 1 4 client sets to zero, 客戶端設置成0
optionally used by gateways,在跨越網關引導時網關可選擇使用
in cross-gateway booting.
Xid 4 5-8 Transaction ID, a random number, used to match this boot request
with the responses it generates.
事務ID,一個隨機數,用來匹配引用請求和應答
Secs 2 9-10 Seconds elapsed since client started trying to boot,filled in by client
由客戶端填寫,客戶端引導開始后的過去的秒數
--(Flags) 2 11-12 unused未使用
Ciaddr 4 13-16 Client IP address,filled in by client in bootrequest if known.
客戶端IP地址,如果客戶端知道就在引導請求中填入
Yiaddr 4 17-20 'Your' (client) IP address,'你的'(客戶端)IP地址
filled by server if client doesn't know its own address (ciaddr was 0)
如果客戶端不知道它的地址(ciaddr是0),服務器填入
Siaddr 4 21-24 Server IP address,服務器IP地址
returned in bootreply by server,由服務器在引導應答返回
Giaddr 4 25-28 Gateway IP address,used in optional cross-gateway booting
網關IP地址,在跨越網關引導中可以選擇使用
Chaddr 16 29-44 Client hardware address,客戶端硬件地址
filled in by client.由客戶端填寫,前6位mac_id,后10位填充0
Sname 64 45-108 Optional server host name, null terminated string.
可選的服務器主機名,空結束的字符串
File 128 109-236 Boot file name, null terminated string;
引導文件名,空結束的字符串
'generic' name or null in bootrequest,
fully qualified directory-path name in bootreply
在引導請求中使用'通用'名稱或空
是引導應答中使用確切的目錄路徑名稱
Vend 64 237-300 Optional vendor-specific area,可選的賣主指定的區域,
e.g. could be hardware type/serial on request,
例如,可以是請求硬件類型/序列,
or 'capability' / remote file system handleon reply.
或應答的性能/遠端文件系統句柄。
This info may be set aside for use by a third phase bootstrap or kernel.
這些信息留給第三方分析引導或核心(程序)使用。
分別對服務端和客戶端的加碼利用 Python 進行實現,解碼部分將在客戶端和服務器的實現代碼中完成,
其中使用到了 binascii 模塊,wmi 模塊,struct 模塊。
1 import binascii 2 import socket 3 import struct 4 import wmi 5 import random 6 w = wmi.WMI() 7 8 class ServerCodeC: 9 10 @staticmethod 11 def offer(transaction_id, client_ip_offer, server_ip, client_mac_id, file_path): 12 SERVER_NAME = 'bootpserver' 13 VENDOR = '' 14 client_mac_id = binascii.unhexlify(client_mac_id.replace(':', '')) 15 transaction_id = binascii.unhexlify(transaction_id) 16 packet = b'' 17 packet += b'\x02' # op 18 packet += b'\x01' # htype 19 packet += b'\x06' # hlen 20 packet += b'\x00' # hops 21 packet += transaction_id 22 packet += b'\x00\x00' # secs 23 packet += b'\x80\x00' # flags (broadcast) 24 packet += b'\x00\x00\x00\x00' # current client ip 25 packet += socket.inet_aton(client_ip_offer) # next current client ip offer 26 packet += socket.inet_aton(server_ip) # server ip 27 packet += b'\x00\x00\x00\x00' # gateway ip 28 packet += client_mac_id # Client mac id #TODO: Change it 29 packet += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # client mac id padding 30 packet += SERVER_NAME.encode('utf-8') 31 packet += b"\x00"*(64-len(SERVER_NAME)) 32 packet += file_path.encode('utf-8') 33 packet += b"\x00"*(128-len(file_path)) 34 packet += VENDOR.encode('utf-8') 35 packet += b"\x00"*(64-len(VENDOR)) 36 return packet 37 38 @staticmethod 39 def collect(msg): 40 msgBody = {} 41 m = list(struct.unpack('%dc' % len(msg), msg)) 42 msgBody['op'] = m[0] 43 msgBody['htype'] = m[1] 44 msgBody['hlen'] = m[2] 45 msgBody['hops'] = m[3] 46 msgBody['xid'] = ''.join(['%02x' % ord(x) for x in m[4:8]]) # transaction_id 47 msgBody['secs'] = m[8:10] 48 msgBody['flags'] = m[10:12] 49 msgBody['ciaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[12:16]), msg[12:16]))).rstrip('.') 50 msgBody['yiaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[16:20]), msg[16:20]))).rstrip('.') 51 msgBody['siaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[20:24]), msg[20:24]))).rstrip('.') 52 msgBody['giaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[24:28]), msg[24:28]))).rstrip('.') 53 msgBody['chaddr'] = ''.join(['%02x:' % ord(x) for x in m[28:34]])[:-1] # Client_mac_id (Delete last one ':') 54 msgBody['sname'] = msg[44:108].decode('utf-8').strip('\x00') 55 msgBody['file'] = msg[108:236].decode('utf-8').strip('\x00') 56 msgBody['vend'] = msg[236:300] 57 return msgBody 58 59 class ClientCodeC(): 60 transaction_id = None 61 client_mac_id = None 62 63 @classmethod 64 def get_xid_macid(cls): 65 xid = '' 66 for i in range(8): 67 xid += hex(random.randint(0, 15))[-1] 68 cls.transaction_id = xid 69 70 mac_id = [] 71 for network in w.Win32_NetworkAdapterConfiguration(IPEnabled=1): 72 mac_id.append(network.MACAddress) 73 cls.client_mac_id = mac_id[0].lower() 74 75 @staticmethod 76 def request(): 77 SERVER_NAME = '' 78 VENDER = '' 79 transaction_id = binascii.unhexlify(ClientCodeC.transaction_id) 80 client_mac_id = binascii.unhexlify(ClientCodeC.client_mac_id.replace(':', '')) 81 82 packet = b'' 83 packet += b'\x01' # request op 1 1 84 packet += b'\x01' # htype 2 1 85 packet += b'\x06' # hlen 3 1 86 packet += b'\x00' # hops 4 1 87 packet += transaction_id # transaction_id 5-8 4 88 packet += b'\x00\x00' # secs 9-10 2 89 # TODO: Add resend time count 90 packet += b'\x80\x00' # flags(broadcast) 11-12 2 91 packet += b'\x00\x00\x00\x00' # client ip 13-16 4 92 packet += b'\x00\x00\x00\x00' # your client ip 17-20 4 93 packet += b'\x00\x00\x00\x00' # server ip 21-24 4 94 packet += b'\x00\x00\x00\x00' # gateway ip 25-28 4 95 packet += client_mac_id # mac id 29-34 6 96 packet += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # mac id placeholder 35-44 10 97 packet += b'\x00' * 64 # server name 45-108 64 98 packet += b'\x00' * 128 # file name 109-236 128 99 packet += VENDER.encode('utf-8') # vender info 237-300 100 packet += b"\x00"*(64-len(VENDER)) 101 return packet 102 103 @staticmethod 104 def collect(msg): 105 msgBody = {} 106 m = list(struct.unpack('%dc' % len(msg), msg)) 107 msgBody['op'] = m[0] 108 msgBody['htype'] = m[1] 109 msgBody['hlen'] = m[2] 110 msgBody['hops'] = m[3] 111 msgBody['xid'] = ''.join(['%02x' % ord(x) for x in m[4:8]]) # transaction_id 112 msgBody['secs'] = m[8:10] 113 msgBody['flags'] = m[10:12] 114 msgBody['ciaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[12:16]), msg[12:16]))).rstrip('.') 115 msgBody['yiaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[16:20]), msg[16:20]))).rstrip('.') 116 msgBody['siaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[20:24]), msg[20:24]))).rstrip('.') 117 msgBody['giaddr'] = ''.join(str(ord(i)) + '.' for i in list(struct.unpack('%dc' % len(msg[24:28]), msg[24:28]))).rstrip('.') 118 msgBody['chaddr'] = ''.join(['%02x:' % ord(x) for x in m[28:34]])[:-1] # Client_mac_id (Delete last one ':') 119 msgBody['sname'] = msg[44:108].decode('utf-8').strip('\x00') 120 msgBody['file'] = msg[108:236].decode('utf-8').strip('\x00') 121 msgBody['vend'] = msg[236:300] 122 return msgBody
相關閱讀
1. binascii 模塊
2. wmi 模塊
3. struct 模塊
4. DHCP
參考鏈接
http://www.360doc.com/content/07/0822/15/39230_688439.shtml