首页 > 代码库 > Python解析DNS数据包

Python解析DNS数据包

工作中有时需要对DNS数据包进行解析,抽取出其中的Qurey Name和Answer中的IP地址,今天写了一个简单的脚本分析PCAP包中的DNS,用到了dpkt模块。

我只抽取了关键的Query Name和Answer中的IP地址,没有解析授权和额外信息。

如果不想写脚本,可以使用tshark工具(wireshark的命令行版本),使用简单的命令行即可抽取想要的信息。但是tshark在抽取IP地址的时候,会将授权域和额外域中的IP地址也会一起抽取出来,难以区分。

  1 #!/usr/bin/python
  2 #coding = utf-8
  3 import struct
  4 import dpkt
  5 import sys
  6 import socket
  7 import binascii
  8 
  9 DNS_Q = 0
 10 DNS_R = 1
 11 
 12 # Opcodes
 13 DNS_QUERY = 0
 14 DNS_IQUERY = 1
 15 DNS_STATUS = 2
 16 DNS_NOTIFY = 4
 17 DNS_UPDATE = 5
 18 
 19 # Flags
 20 DNS_CD = 0x0010 # checking disabled
 21 DNS_AD = 0x0020 # authenticated data
 22 DNS_Z =  0x0040 # unused
 23 DNS_RA = 0x0080 # recursion available
 24 DNS_RD = 0x0100 # recursion desired
 25 DNS_TC = 0x0200 # truncated
 26 DNS_AA = 0x0400 # authoritative answer
 27 
 28 # Response codes
 29 DNS_RCODE_NOERR = 0
 30 DNS_RCODE_FORMERR = 1
 31 DNS_RCODE_SERVFAIL = 2
 32 DNS_RCODE_NXDOMAIN = 3
 33 DNS_RCODE_NOTIMP = 4
 34 DNS_RCODE_REFUSED = 5
 35 DNS_RCODE_YXDOMAIN = 6
 36 DNS_RCODE_YXRRSET = 7
 37 DNS_RCODE_NXRRSET = 8
 38 DNS_RCODE_NOTAUTH = 9
 39 DNS_RCODE_NOTZONE = 10
 40 
 41 # RR types
 42 DNS_A = 1
 43 DNS_NS = 2
 44 DNS_CNAME = 5
 45 DNS_SOA = 6
 46 DNS_PTR = 12
 47 DNS_HINFO = 13
 48 DNS_MX = 15
 49 DNS_TXT = 16
 50 DNS_AAAA = 28
 51 DNS_SRV = 33
 52 
 53 # RR classes
 54 DNS_IN = 1
 55 DNS_CHAOS = 3
 56 DNS_HESIOD = 4
 57 DNS_ANY = 255
 58 
 59 def addr2str(addrobj):
 60     if len(addrobj) != 4:
 61         return "addr error!"
 62     else:
 63         return str(ord(addrobj[0]))+"."+str(ord(addrobj[1]))+"."+str(ord(addrobj[2]))+"."+str(ord(addrobj[3]))
 64 
 65 def TCPorUDP(obj):
 66     if (ord(obj) == 0x01):
 67         return "ICMP"
 68     elif (ord(obj) == 0x02):
 69         return "IGMP"
 70     elif (ord(obj) == 0x06):
 71         return "TCP"
 72     elif (ord(obj) == 0x08):
 73         return "EGP"
 74     elif (ord(obj) == 0x09):
 75         return "IGP"
 76     elif (ord(obj) == 0x11):
 77         return "UDP"
 78     elif (ord(obj) == 41):
 79         return "IPv6"
 80     elif (ord(obj) == 89):
 81         return "OSPF"
 82     else:
 83         return "error"
 84 
 85 def dns_response_body_parse(body):        # parse the response message‘s body
 86     identification = body[0:2]
 87     flag = body[2:4]
 88     num_ques = body[4:6]
 89     num_ans_RR = body[6:8]
 90     num_auth_RR = body[8:10]
 91     num_addi_RR = body[10:12]
 92     query_name = ‘‘
 93     ans_ip = []
 94     flag = 12
 95     while(ord(body[flag])!=0x0):
 96         query_name = query_name + body[flag+1:flag+ord(body[flag])+1]
 97         flag = flag + ord(body[flag]) + 1
 98         try:
 99             if ord(body[flag]) != 0x0:
100                 query_name = query_name+.
101         except Exception, e:
102             print "error when parse query domain name"
103     #print query_name
104     flag = flag + 1
105     query_type = ord(body[flag])*256 + ord(body[flag+1])
106     if query_type == 0x01:            # use domain query IP addr
107         flag = flag + 4
108         i = 1
109         answer_num = ord(num_ans_RR[0])*256 + ord(num_ans_RR[1])
110         while(i<=answer_num):
111             if ord(body[flag]) == 0xc0:
112                 flag = flag + 2
113             else:
114                 while(ord(body[flag])!=0x0):
115                     flag = flag + ord(body[flag]) + 1
116                 flag = flag + 1
117             if (    ord(body[flag])*256+ord(body[flag+1]) == DNS_A 
118                 and ord(body[flag+2])*256+ord(body[flag+3]) == DNS_IN):
119                 flag = flag + 8
120                 RR_data_len = ord(body[flag])*256 + ord(body[flag+1])
121                 if RR_data_len == 4:
122                     ans_ip.append(addr2str(body[flag+2:flag+6]))
123                 flag = flag + ord(body[flag])*256 + ord(body[flag+1]) + 2
124             else:
125                 flag = flag + 8
126                 flag = flag + ord(body[flag])*256 + ord(body[flag+1]) + 2
127             i = i + 1
128     else:
129         print "query type is PTR not A"
130         return
131     return "%s\t%s"%(query_name,ans_ip)
132 
133 def main():
134     paralen = len(sys.argv)
135     if paralen != 3:
136         print ("there is only %d parameter, %s"%(paralen,sys.argv))
137         print "no enough parameter!"
138         print "command should be: python *.py result.txt src_pcap_file.pcap"
139         return
140 
141     print "parse result will write to:"+sys.argv[1]
142     print "strat parse the pcap file:"+sys.argv[2]
143 
144     fw = open(sys.argv[1],"w")
145     f = file(sys.argv[2],"rb")
146     pcap = dpkt.pcap.Reader(f)
147     for ts,buf in pcap:
148         #fw.writelines("timestamp:"+str(ts)+"\tpacket len:"+str(len(buf))+"\n")
149         ethheader = buf[0:14]
150         dstmac = ethheader[0:6]
151         srcmac = ethheader[6:12]
152         netlayer_type = ethheader[12:14]
153         #fw.writelines("dstMAC:"+str(binascii.b2a_hex(dstmac))+"\tsrcMAC:"+str(binascii.b2a_hex(srcmac))+"\n")
154 
155         pktheader = buf[14:34]
156         trans_type = pktheader[9]
157         srcip = pktheader[12:16]
158         dstip = pktheader[16:20]
159 
160         #fw.writelines("dstIP:"+addr2str(dstip)+"\tsrcIP:"+addr2str(srcip)+"\n")
161         #fw.writelines("packet type:"+TCPorUDP(trans_type)+"\n")
162 
163         if (ord(trans_type) == 0x11):     #UDP
164             udpheader = buf[34:42]
165             srcport = udpheader[0:2]
166             dstport = udpheader[2:4]
167             udplen = udpheader[4:6]
168             #fw.writelines("srcport:"+str(ord(srcport[1])+ord(srcport[0])*16*16)+"\tdstport:"+str(ord(dstport[1])+ord(dstport[0])*16*16)+"\n\n")
169             bodylen = ord(udplen[0])*256+ord(udplen[1])-8
170             print "\ndns body length is "+str(bodylen)
171             dnsbody = buf[42:(42+bodylen)]
172             if (ord(dstport[0]) == 0x00 and ord(dstport[1]) == 0x35):
173                 print "this is a DNS Request"
174             elif (ord(srcport[0]) == 0x00 and ord(srcport[1]) == 0x35):
175                 print "this is a DNS Response"
176                 fw.writelines(dns_response_body_parse(dnsbody)+"\n")    # wirte result to file
177             else:
178                 print ord(srcport[0]),ord(srcport[0])
179         elif (ord(trans_type) == 0x06):     #TCP
180             tcpheader = buf[34:54]
181             srcport = tcpheader[0:2]
182             dstport = tcpheader[2:4]
183             #fw.writelines("srcport:"+str(ord(srcport[1])+ord(srcport[0])*16*16)+"\tdstport:"+str(ord(dstport[1])+ord(dstport[0])*16*16)+"\n\n")
184     f.close()
185     print ("process %s has finished, the result was in file %s"%(sys.argv[2],sys.argv[1]))
186 if __name__ == "__main__":
187     main()