读取 python 中的 DNS 数据包
Reading DNS packet in python
This question had been asked before 但这个问题从未得到完全解决,是从 2013 年开始的。我正在使用 python 套接字来观察 DNS 数据包,它们看起来像这样:
b'\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x10googletagmanager\x03com\x00\x00\x01\x00\x01'
在研究 DNS 数据包的基础知识后,我发现它们的结构如下:
QR | OpCode | AA | TC | RD | RA | Z | AD | CD | RCODE
然后我将数据包解码为 ASCII:
>> str = b'\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03www\x10googletagmanager\x03com\x00\x00\x01\x00\x01'
>> print(str.decode("ascii"))
wwwgoogletagmanagercom
这只是 returns 带有地址名称的单个字符串,而不是上面指定的其他信息。其余数据在哪里,例如 QR 和 OpCode?我是不是解码错了?
明确地说,我不想使用外部库,我的目的是了解 DNS 数据包的结构以及如何对其进行解码;我知道 dnslib
和 scapy
.
等库
我不是套接字专家。来自参考 - DNS header 由位而不是字节组成...因此您需要将其解析为位。使用字节和掩码位。请参见下面的示例。不确定header hdr[12:]的内容是什么?
下面是一些基于上述规范的示例代码:
def DNStoDict(hdr):
'''
Parse QNAME by using length (byte) +data sequence -- final length=0 signifies end of QNAME
Refer to https://whosebug.com/questions/34841206/why-is-the-content-of-qname-field-not-the-original-domain-in-a-dns-message
1) DNS knows nothing of URLs. DNS is older than the concept of a URL.
2) Because that's how DNS's wire format works. What you see is the
domain name www.mydomain.com, encoded in the DNS binary format.
Length+data is a very common way of storing strings in general.
'''
# Build DNS dictionary of values... include QNAME
l = len(hdr)
argSize = hdr[10]*256+hdr[11]
dnsDict = dict(ID = hdr[0]*256+hdr[1],
QR = bool(hdr[2] & int('10000000', 2)),
Opcode = (hdr[2] & int('01111000', 2))>>3,
AA = bool(hdr[2] & int('00000100', 2)),
TC = bool(hdr[2] & int('00000010', 2)),
RD = bool(hdr[2] & int('00000001', 2)),
RA = bool(hdr[3] & int('10000000', 2)),
Z = bool(hdr[3] & int('01000000', 2)),
AD = bool(hdr[3] & int('00100000', 2)),
CD = bool(hdr[3] & int('00010000', 2)),
RCode = bool(hdr[3] & int('00001111', 2)),
QDCOUNT = hdr[4]*256+hdr[5],
ANCOUNT = hdr[6]*256+hdr[7],
NSCOUNT = hdr[8]*256+hdr[9],
ARCOUNT = argSize,
QTYPE = hdr[l-4]*256+hdr[l-3],
QCLASS = hdr[l-2]*256+hdr[l-2])
# Parse QNAME
n = 12
mx = len(hdr)
qname = ''
while n < mx:
try:
qname += hdr[n:n+argSize].decode() + '.'
n += argSize
argSize = int(hdr[n])
n += 1
if argSize == 0 :
break
except Exception as err:
print("Parse Error", err, n, qname)
break
dnsDict['QNAME'] = qname[:-1]
return dnsDict
# Sample DNS Packet Data
hdr = b'\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03www\x10googletagmanager\x03com\x00\x00\x01\x00\x01'
# Parse out the QNAME
dnsDict = DNStoDict(hdr)
print("\n DNS PACKET dictionary")
print(dnsDict)
输出:
DNS 数据包字典
{'ID': 257, 'QR': 假, 'Opcode': 0, 'AA': 假, 'TC': 假, 'RD': 假, 'RA':错误,'Z':错误,'AD':错误,'CD':错误,'RCode':错误,'QDCOUNT':0,'ANCOUNT': 0, 'NSCOUNT': 0, 'ARCOUNT': 3, 'QTYPE': 1, 'QCLASS': 0, 'QNAME': 'www.googletagmanager.com'}
Pyhon 位操作
参考
- https://wiki.python.org/moin/BitManipulation
- http://www.java2s.com/Tutorials/Python/Data_Types/How_to_create_integer_in_Python_octal_binary_hexadecimal_and_long_integer.htm
一个字节(b'xxxx'
)代表4个字节。每个字节由8位组成
0000 0000 - 0
0000 0001 - 1
0000 0010 - 2
0000 0100 - 4
0000 1000 - 8
0001 0000 - 16
0010 0000 - 32
0100 0000 - 64
1000 0000 - 128
1111 1111 - 255 (128+64+32+16+8+4+2+1)
在 python 中,格式 int('00000111', 2) 是使用模 2(位)转换字符串数组 ['0'/'1']。这个 returns 值 7 模 10.
参考 DNS Header:
https://www2.cs.duke.edu/courses/fall16/compsci356/DNS/DNS-primer.pdf
http://www.networksorcery.com/enp/protocol/dns.htm
This question had been asked before 但这个问题从未得到完全解决,是从 2013 年开始的。我正在使用 python 套接字来观察 DNS 数据包,它们看起来像这样:
b'\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x10googletagmanager\x03com\x00\x00\x01\x00\x01'
在研究 DNS 数据包的基础知识后,我发现它们的结构如下:
QR | OpCode | AA | TC | RD | RA | Z | AD | CD | RCODE
然后我将数据包解码为 ASCII:
>> str = b'\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03www\x10googletagmanager\x03com\x00\x00\x01\x00\x01'
>> print(str.decode("ascii"))
wwwgoogletagmanagercom
这只是 returns 带有地址名称的单个字符串,而不是上面指定的其他信息。其余数据在哪里,例如 QR 和 OpCode?我是不是解码错了?
明确地说,我不想使用外部库,我的目的是了解 DNS 数据包的结构以及如何对其进行解码;我知道 dnslib
和 scapy
.
我不是套接字专家。来自参考 - DNS header 由位而不是字节组成...因此您需要将其解析为位。使用字节和掩码位。请参见下面的示例。不确定header hdr[12:]的内容是什么?
下面是一些基于上述规范的示例代码:
def DNStoDict(hdr):
'''
Parse QNAME by using length (byte) +data sequence -- final length=0 signifies end of QNAME
Refer to https://whosebug.com/questions/34841206/why-is-the-content-of-qname-field-not-the-original-domain-in-a-dns-message
1) DNS knows nothing of URLs. DNS is older than the concept of a URL.
2) Because that's how DNS's wire format works. What you see is the
domain name www.mydomain.com, encoded in the DNS binary format.
Length+data is a very common way of storing strings in general.
'''
# Build DNS dictionary of values... include QNAME
l = len(hdr)
argSize = hdr[10]*256+hdr[11]
dnsDict = dict(ID = hdr[0]*256+hdr[1],
QR = bool(hdr[2] & int('10000000', 2)),
Opcode = (hdr[2] & int('01111000', 2))>>3,
AA = bool(hdr[2] & int('00000100', 2)),
TC = bool(hdr[2] & int('00000010', 2)),
RD = bool(hdr[2] & int('00000001', 2)),
RA = bool(hdr[3] & int('10000000', 2)),
Z = bool(hdr[3] & int('01000000', 2)),
AD = bool(hdr[3] & int('00100000', 2)),
CD = bool(hdr[3] & int('00010000', 2)),
RCode = bool(hdr[3] & int('00001111', 2)),
QDCOUNT = hdr[4]*256+hdr[5],
ANCOUNT = hdr[6]*256+hdr[7],
NSCOUNT = hdr[8]*256+hdr[9],
ARCOUNT = argSize,
QTYPE = hdr[l-4]*256+hdr[l-3],
QCLASS = hdr[l-2]*256+hdr[l-2])
# Parse QNAME
n = 12
mx = len(hdr)
qname = ''
while n < mx:
try:
qname += hdr[n:n+argSize].decode() + '.'
n += argSize
argSize = int(hdr[n])
n += 1
if argSize == 0 :
break
except Exception as err:
print("Parse Error", err, n, qname)
break
dnsDict['QNAME'] = qname[:-1]
return dnsDict
# Sample DNS Packet Data
hdr = b'\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03www\x10googletagmanager\x03com\x00\x00\x01\x00\x01'
# Parse out the QNAME
dnsDict = DNStoDict(hdr)
print("\n DNS PACKET dictionary")
print(dnsDict)
输出:
DNS 数据包字典 {'ID': 257, 'QR': 假, 'Opcode': 0, 'AA': 假, 'TC': 假, 'RD': 假, 'RA':错误,'Z':错误,'AD':错误,'CD':错误,'RCode':错误,'QDCOUNT':0,'ANCOUNT': 0, 'NSCOUNT': 0, 'ARCOUNT': 3, 'QTYPE': 1, 'QCLASS': 0, 'QNAME': 'www.googletagmanager.com'}
Pyhon 位操作
参考
- https://wiki.python.org/moin/BitManipulation
- http://www.java2s.com/Tutorials/Python/Data_Types/How_to_create_integer_in_Python_octal_binary_hexadecimal_and_long_integer.htm
一个字节(b'xxxx'
)代表4个字节。每个字节由8位组成
0000 0000 - 0 0000 0001 - 1 0000 0010 - 2 0000 0100 - 4 0000 1000 - 8 0001 0000 - 16 0010 0000 - 32 0100 0000 - 64 1000 0000 - 128 1111 1111 - 255 (128+64+32+16+8+4+2+1)
在 python 中,格式 int('00000111', 2) 是使用模 2(位)转换字符串数组 ['0'/'1']。这个 returns 值 7 模 10.
参考 DNS Header: https://www2.cs.duke.edu/courses/fall16/compsci356/DNS/DNS-primer.pdf http://www.networksorcery.com/enp/protocol/dns.htm