工作中有时需要对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 %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)+" packet len:"+str(len(buf))+" ") 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))+" srcMAC:"+str(binascii.b2a_hex(srcmac))+" ") 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)+" srcIP:"+addr2str(srcip)+" ") 161 #fw.writelines("packet type:"+TCPorUDP(trans_type)+" ") 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)+" dstport:"+str(ord(dstport[1])+ord(dstport[0])*16*16)+" ") 169 bodylen = ord(udplen[0])*256+ord(udplen[1])-8 170 print " dns 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)+" ") # 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)+" dstport:"+str(ord(dstport[1])+ord(dstport[0])*16*16)+" ") 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()