如今几乎我们身边所有的设备都已经支持互联网通信功能,都知道要在互联网中通信需要给设备分配IP地址,目前IP地址的设置方法主要有以下两种:
- 静态设置IP
- 动态设置IP
DHCP动态主机配置协议
DHCP(Dynamic Host Configuration Protocol)是工作在应用层的一个局域网协议,其作用是用来给内部网络分配IP地址。
DHCP工作流程
我们把一个网络中的某一台设备叫做DHCP服务器,它有一个地址池,里边存放着可以使用的IP地址;把需要IP地址的设备叫做DHCP客户端。
(1)DHCP客户端在局域网内广播一个DHCP Discover数据包(带着自己的MAC地址),目的是发现能够给它提供IP地址的DHCP服务器。
(2)DHCP服务器收到DHCP Discover数据包后向请求的DHCP服务端发送一个DHCP Offer数据包(包含分给请求者的IP地址)。
(3)DHCP客户端接收到DHCP Offer数据包后(同时接收到来自多个DHCP服务器的DHCP Offer只处理第一个),广播一个DHCP Request数据包,声明自己所用的DHCP服务端和IP地址是哪一个。
(4)DHCP服务器收到DHCP Request数据包后,判断数据包中的IP地址是不是和自己的IP地址相同,如果相同则发送一个DHCP ACK数据包。
关于DHCP的具体内容可以参考DHCP详解
用Python程序模拟DHCP客户端
DHCP协议数据包并不能单独在网络中传播,需要其他各层协议的配合,毕竟互联网协议是分工明确相互合作的嘛。其次,DHCP协议的前身是引导协议(BootStrap Protocol),BOOTP协议为连接道网络中的设备自动分配IP地址,但是后来被更加复杂、更加强大的DHCP取代,然而在一些工具中,比如Wireshark和Scapy中还是将DHCP和BOOTP拆成两部分来看。
如图:字段含义可以参考:
在构造数据包之前,我们需要了解一下DHCP数据包的相关知识:
- DHCP协议采用UDP协议
- DHCP客户端发送请求是到服务端的67端口,而响应消息是发给客户端的68端口。
DHCP Discovery数据包的构造
# DHCP Discover包
import binascii
from random import randint
from scapy.all import *
from scapy.layers.dhcp import BOOTP, DHCP
from scapy.layers.inet import *
client_mac = str(RandMAC()) # 本机已经有IP了,为了模拟,所以我们随机产生一个MAC地址,并转为str类型
client_mac_id = binascii.unhexlify(client_mac.replace(':', ''))
print("client_mac: " + client_mac)
client_xid = randint(1, 9999999) # DHCP发起一次请求是选择的一个随机数
DHCP_Discover = DHCP(options=[("message-type", "discover"), "end"])
BOOTP_Discover = BOOTP(chaddr=client_mac_id, xid=client_xid)
UDP_Discover = UDP(sport=68, dport=67)
IP_Discover = IP(src='0.0.0.0', dst='255.255.255.255') # 此时没有IP,故IP=‘0.0.0.0’
Ether_Discover = Ether(src=client_mac, dst='ff:ff:ff:ff:ff:ff')
Discover = Ether_Discover / IP_Discover / UDP_Discover / BOOTP_Discover / DHCP_Discover
ans = srp(Discover, iface='以太网')
print("\n\n\nSending DHCPDISCOVER on " + " 以太网")
print(ans.summary())
wireshark捕获到我们发出去的数据包如下![在这里插入图片描述](https://img-blog.csdnimg.cn/20210416181122223.png?x-oss-process=image/watermark,type_Zm
DHCP Request数据包的构造
Ether_Request = Ether(src=pkt[Ether].dst, dst='ff:ff:ff:ff:ff:ff')
IP_Request = IP(src='0:0:0:0', dst='255.255.255.255')
UDP_Request = UDP(sport=68, dport=67)
BOOTP_Request = BOOTP(chaddr=pkt[BOOTP].chaddr,xid=pkt[BOOTP].xid)
DHCP_Request = DHCP(options=[("message-type","request"),("server_id",pkt[DHCP].options[1][1]),("requested_addr",pkt[BOOTP].yiaddr),"end"])
Request = Ether_Request / IP_Request / UDP_Request / BOOTP_Request /DHCP_Request
完整代码如下(我这里用的是kali linux所以网卡为wlan0):
from scapy.all import *
def detect_dhcp(pkt):
if DHCP in pkt:
if pkt[DHCP].options[0][1] == 2:
Ether_Request = Ether(src=pkt[Ether].dst, dst='ff:ff:ff:ff:ff:ff')
IP_Request = IP(src='0:0:0:0', dst='255.255.255.255')
UDP_Request = UDP(sport=68, dport=67)
BOOTP_Request = BOOTP(chaddr=pkt[BOOTP].chaddr,xid=pkt[BOOTP].xid)
DHCP_Request = DHCP(options=[("message-type","request"),("server_id",pkt[DHCP].options[1][1]),("requested_addr",pkt[BOOTP].yiaddr),"end"])
Request = Ether_Request / IP_Request / UDP_Request / BOOTP_Request /DHCP_Request
sendp(Request,iface='wlan0')
print(pkt[BOOTP].yiaddr + "正在分配")
if pkt[DHCP].options[0][1] == 5:
print(pkt[BOOTP].yiaddr + "已经分配")
sniff(filter='src port 67',iface='wlan0',prn=detect_dhcp,count=10)
DHCP模拟
#!/usr/bin/python3
from scapy.all import *
import binascii
import _thread
import time
# 为线程定义一个函数
def sent_discover( threadName, delay):
while(1):
xid_random = random.randint(1, 900000000)
mac_random = str(RandMAC())
client_mac_id = binascii.unhexlify(mac_random.replace(':', ''))
dhcp_discover = Ether(src=mac_random, dst="ff:ff:ff:ff:ff:ff") / IP(src="0.0.0.0", dst="255.255.255.255") / UDP(sport=68, dport=67) / BOOTP(chaddr=client_mac_id, xid=xid_random) / DHCP(options=[("message-type", "discover"), "end"])
sendp(dhcp_discover, iface='wlan0')
time.sleep(delay)
def sniff_discover(threadName, delay):
def detect_dhcp(pkt):
if DHCP in pkt:
if pkt[DHCP].options[0][1] == 2:
Ether_Request = Ether(src=pkt[Ether].dst, dst="ff:ff:ff:ff:ff:ff")
IP_Request = IP(src="0.0.0.0", dst="255.255.255.255")
UDP_Request = UDP(sport=68, dport=67)
BOOTP_Request = BOOTP(chaddr=pkt[BOOTP].chaddr, xid=pkt[BOOTP].xid)
DHCP_Request = DHCP(options=[("message-type", 'request'), ("server_id", pkt[DHCP].options[1][1]),
("requested_addr", pkt[BOOTP].yiaddr), "end"])
Request = Ether_Request / IP_Request / UDP_Request / BOOTP_Request / DHCP_Request
sendp(Request, iface='wlan0')
print(pkt[BOOTP].yiaddr + "正在分配")
if pkt[DHCP].options[0][1] == 5:
print(pkt[BOOTP].yiaddr + "已经分配")
sniff(filter="src port 67", iface='wlan0', prn=detect_dhcp)
try:
_thread.start_new_thread( sniff_discover, ("Thread-1", 0, ) )
_thread.start_new_thread( sent_discover, ("Thread-2", 10, ) )
except:
print ("Error: 无法启动线程")
while 1:
pass