DNS(Domain Name System)协议是计算机网络中的一种基础协议,它用于将域名(如www.baidu.com)转换为IP地址(如192.168.0.1),从而实现计算机之间的通信。
DNS 分为查询请求和查询响应,请求和响应的报文结构基本相同。DNS 报文格式如图所示
上图中显示了 DNS 的报文格式。其中,事务 ID、标志、问题计数、回答资源记录数、权威名称服务器计数、附加资源记录数这 6 个字段是DNS的报文首部,共 12 个字节。
整个 DNS 格式主要分为 3 部分内容,即基础结构部分、问题部分、资源记录部分。下面将详细地介绍每部分的内容及含义。
DNS 报文的基础结构部分指的是报文首部,如图所示。
该部分中每个字段含义如下。
基础结构部分中的标志字段又分为若干个字段,如图所示。
标志字段中每个字段的含义如下:
问题部分指的是报文格式中查询问题区域(Queries)部分。该部分是用来显示 DNS 查询请求的问题,通常只有一个问题。该部分包含正在进行的查询信息,包含查询名(被查询主机名字)、查询类型、查询类。
问题部分格式如图所示。
该部分中每个字段含义如下:
资源记录部分是指 DNS 报文格式中的最后三个字段,包括回答问题区域字段、权威名称服务器区域字段、附加信息区域字段。这三个字段均采用一种称为资源记录的格式,格式如图所示。
资源记录格式中每个字段含义如下:
资源记录部分只有在 DNS 响应包中才会出现。下面通过 DNS 响应包来进一步了解资源记录部分的字段信息。
scapy具有强大的报文构造能力和修改能力,我们一般定制化DNS报文都是修改DNS请求里的域名和响应里的IP地址。最方便的方法就是基于一个现有的DNS请求和响应报文去修改我们目标字段。
我们可以先看看scapy解析dns的能力
scapy解析请求和响应
- ###[ DNS ]###
- id = 49586
- qr = 1
- opcode = QUERY
- aa = 0
- tc = 0
- rd = 1
- ra = 1
- z = 0
- ad = 0
- cd = 0
- rcode = ok
- qdcount = 1
- ancount = 23
- nscount = 2
- arcount = 0
- \qd \
- |###[ DNS Question Record ]###
- | qname = 'upload-z1.qbox.me.'
- | qtype = A
- | qclass = IN
- \an \
- |###[ DNS Resource Record ]###
- | rrname = 'upload-z1.qbox.me.'
- | type = CNAME
- | rclass = IN
- | ttl = 516
- | rdlen = None
- | rdata = 'upload-z1.clouddn.com.'
- |###[ DNS Resource Record ]###
- | rrname = 'upload-z1.clouddn.com.'
- | type = CNAME
- | rclass = IN
- | ttl = 80
- | rdlen = None
- | rdata = 'upload-z1-oss.clouddn.com.'
- |###[ DNS Resource Record ]###
- | rrname = 'upload-z1-oss.clouddn.com.'
- | type = CNAME
- | rclass = IN
- | ttl = 54
- | rdlen = None
- | rdata = 'bc-gate-up.qiniu.com.'
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.121
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.33
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.13.229.82
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.13.229.81
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.13.229.68
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.13.229.74
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.20
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.13.229.73
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.19
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.122
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.23
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.21
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.13.229.78
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.123
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.18
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.22
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.13.229.75
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.13.229.80
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.25
- |###[ DNS Resource Record ]###
- | rrname = 'bc-gate-up.qiniu.com.'
- | type = A
- | rclass = IN
- | ttl = 104
- | rdlen = None
- | rdata = 111.31.48.24
- \ns \
- |###[ DNS Resource Record ]###
- | rrname = 'qiniu.com.'
- | type = NS
- | rclass = IN
- | ttl = 78188
- | rdlen = None
- | rdata = 'ns3.dnsv5.com.'
- |###[ DNS Resource Record ]###
- | rrname = 'qiniu.com.'
- | type = NS
- | rclass = IN
- | ttl = 78188
- | rdlen = None
- | rdata = 'ns4.dnsv5.com.'
- ar = None
-
- [Finished in 2.3s]
可以看到scapy具备完整的DNS解析能力,那我们的目标就是利用scapy的解析能力去修改我们的目标字段,另外修改目标字段后报文的各种校验和也需要重新计算。
下面给出完整的构造DNS报文的代码
- def dnsPcapProc(self):
- savefile = 'dns_'+ datetime.now().strftime("%H%M%S") + '.pcap'
- srcpcap = rdpcap(self.dnsPcapFilePath)
- srcpcap[1][DNS].show2()
- oriDomainLen = len(srcpcap[0][DNS].fields['qd'].fields['qname'])
- diff = len(self.dnsDomainInputEntry)+1 - oriDomainLen
-
- oriIPLen = srcpcap[0][IP].len
- oriUDPLen = srcpcap[0][UDP].len
- srcpcap[0][IP].len = oriIPLen + diff
- srcpcap[0][UDP].len = oriUDPLen + diff
-
- srcpcap[0][DNS].fields['qd'].fields['qname'] = self.dnsDomainInputEntry.encode("utf-8")
-
- try:
- del srcpcap[0][IP].len
- del srcpcap[0][IP].chksum
- except:
- srcpcap[0][DNS].fields['qd'].fields['qtype'] = 28
- del srcpcap[0][IPv6].plen
- del srcpcap[0][IPv6].chksum
- flg =6
-
- del srcpcap[0][UDP].len
- del srcpcap[0][UDP].chksum
-
-
- srcpcap[0][IP].len = oriIPLen + diff
- srcpcap[0][UDP].len = oriUDPLen + diff
-
- ip_list = list()
-
- ip_list.append(self.dnsIPInputEntry)
-
- srcpcap[1][UDP].payload.ancount = len(ip_list)
- srcpcap[1][UDP].payload.qd.qname = self.dnsDomainInputEntry.encode("utf-8")
- dns_anser_as_ord = []
- for ip in ip_list:
- ipstr,flg = self.ipstr_trans2_hexlist(ip)
- if flg == 4:
- dns_anser_as_ord += [0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x20, 0x00, 0x04]
- else:
- dns_anser_as_ord += [0xc0, 0x0c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x1c, 0x20, 0x00, 0x10]
- dns_anser_as_ord += ipstr
-
- hexByteOri = "".join(["%02x" % c for c in dns_anser_as_ord]).encode("utf-8")
- hexByte = binascii.a2b_hex(hexByteOri)
-
- srcpcap[1][UDP].payload.an = hexByte
- try:
- del srcpcap[1][IP].len
- del srcpcap[1][IP].chksum
- except:
- del srcpcap[1][IPv6].plen
- del srcpcap[1][IPv6].chksum
-
- del srcpcap[1][UDP].len
- del srcpcap[1][UDP].chksum
- #srcpcap[0].show2()
- #wrpcap(savefile, srcpcap)
- pktdump = PcapWriter(savefile, append=True, sync=True)
- ether_layer = srcpcap[0]['Ether']
- newpcap = Ether(src=ether_layer.src, dst=ether_layer.dst) / srcpcap[0].getlayer('IP')
- newpcap2 = Ether(src=ether_layer.dst, dst=ether_layer.src) / srcpcap[1].getlayer('IP')
- pktdump.write(newpcap)
- pktdump.write(newpcap2)