Python的网络编程[3] -> BOOTP 协议[0] -> BOOTP 的基本理论
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