PcapComparator.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. import logging
  2. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  3. from scapy.all import rdpcap, wrpcap
  4. from scapy.all import IP, Ether, TCP, UDP
  5. import sys
  6. file_tel = "telnet-raw.pcap"
  7. from scapy.utils import PcapWriter
  8. def change_pcap(filepath):
  9. packets = rdpcap(filepath)
  10. split_filepath = filepath.split(".")
  11. new_filename = ".".join(split_filepath[:-1]) + "_new." + split_filepath[-1]
  12. #pktdump = PcapWriter(new_filename, append=True)
  13. for pkt in packets:
  14. """
  15. if pkt is packets[-1]:
  16. # test ethernet
  17. pkt[Ether].src = "AA:AA:AA:AA:AA:AA"
  18. pkt[Ether].dst = "BB:BB:BB:BB:BB:BB"
  19. #pkt[Ether].type = 0x1000
  20. pkt[IP].src = "255.255.255.255"
  21. pkt[IP].dst = "0.0.0.0"
  22. #pkt[IP].version = 0x1000
  23. #pkt[IP].ihl = 10
  24. pkt[IP].tos = 127
  25. #pkt[IP].len = 200
  26. # pkttwo = pkt
  27. #print(pkt.show)
  28. wrpcap(new_filename, pkt, append=True)
  29. #print("OK")
  30. #pktdump.write(pkt)
  31. """
  32. if pkt is packets[0]:
  33. wrpcap(new_filename, pkt)
  34. elif not pkt is packets[-1]:
  35. wrpcap(new_filename, pkt, append=True)
  36. ethernet = Ether(src="AA:AA:AA:AA:AA:AA", dst="BB:BB:BB:BB:BB:BB")
  37. ip = IP(src="255.255.255.255", dst="0.0.0.0", tos=127)
  38. tcp = TCP(sport=80, dport=50000, flags="SAF")
  39. pkt = ethernet/ip/tcp
  40. pkt.time = packets[-1].time
  41. wrpcap(new_filename, pkt, append=True)
  42. #print("OK")
  43. #pktdump.write(pkt)
  44. def check_l2_diff(idx, payload_one, payload_two):
  45. # assumes that the L2 protocol of both packets are the same
  46. # assumes Ethernet as L2 protocol
  47. err_msg = ""
  48. if payload_one.name == "Ethernet":
  49. if payload_one[Ether].src != payload_two[Ether].src:
  50. err_msg += "Reason: the packets at index %d have a different source MAC address.\n" % (idx+1)
  51. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (payload_one[Ether].src.upper(), payload_two[Ether].src.upper())
  52. if payload_one[Ether].dst != payload_two[Ether].dst:
  53. err_msg += "Reason: the packets at index %d have a different destination MAC address.\n" % (idx+1)
  54. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (payload_one[Ether].dst.upper(), payload_two[Ether].dst.upper())
  55. if payload_one[Ether].type != payload_two[Ether].type:
  56. err_msg += "Reason: the packets at index %d have a different Ethernet type.\n" % (idx+1)
  57. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (payload_one[Ether].type, payload_two[Ether].type)
  58. if err_msg == "":
  59. return False, err_msg
  60. return True, err_msg
  61. else:
  62. return False, ""
  63. def check_l3_diff(idx, payload_one, payload_two):
  64. # assumes that the L3 protocol of both packets are the same
  65. # assumes IPv4 as L3 protocol
  66. err_msg = ""
  67. if payload_one.name == "IP":
  68. ip_one, ip_two = payload_one[IP], payload_two[IP]
  69. if ip_one.src != ip_two.src:
  70. err_msg += "Reason: the packets at index %d have a different source IP address.\n" % (idx+1)
  71. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.src, ip_two.src)
  72. if ip_one.dst != ip_two.dst:
  73. err_msg += "Reason: the packets at index %d have a different destination IP address.\n" % (idx+1)
  74. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.dst, ip_two.dst)
  75. if ip_one.version != ip_two.version:
  76. err_msg += "Reason: the packets at index %d have a different IP version.\n" % (idx+1)
  77. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.version, ip_two.version)
  78. if ip_one.ihl != ip_two.ihl:
  79. err_msg += "Reason: the packets at index %d have a different IP IHL.\n" % (idx+1)
  80. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.ihl, ip_two.ihl)
  81. if ip_one.tos != ip_two.tos:
  82. err_msg += "Reason: the packets at index %d have a different IP TOS.\n" % (idx+1)
  83. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.tos, ip_two.tos)
  84. if ip_one.len != ip_two.len:
  85. err_msg += "Reason: the packets at index %d have a different length.\n" % (idx+1)
  86. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.len, ip_two.len)
  87. if ip_one.id != ip_two.id:
  88. err_msg += "Reason: the packets at index %d have a different IP ID.\n" % (idx+1)
  89. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.id, ip_two.id)
  90. if ip_one.flags != ip_two.flags:
  91. err_msg += "Reason: the packets at index %d have different IP flags.\n" % (idx+1)
  92. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.flags, ip_two.flags)
  93. if ip_one.frag != ip_two.frag:
  94. err_msg += "Reason: the packets at index %d have a different IP fragmentation offset.\n" % (idx+1)
  95. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.frag, ip_two.frag)
  96. if ip_one.ttl != ip_two.ttl:
  97. err_msg += "Reason: the packets at index %d have a different TTL.\n" % (idx+1)
  98. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.ttl, ip_two.ttl)
  99. if ip_one.proto != ip_two.proto:
  100. err_msg += "Reason: the packets at index %d have a different IP protocol field value.\n" % (idx+1)
  101. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.proto, ip_two.proto)
  102. if ip_one.chksum != ip_two.chksum:
  103. err_msg += "Reason: the packets at index %d have a different IP checksum.\n" % (idx+1)
  104. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.chksum, ip_two.chksum)
  105. if ip_one.options != ip_two.options:
  106. err_msg += "Reason: the packets at index %d have different IP options.\n" % (idx+1)
  107. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (ip_one.options, ip_two.options)
  108. if err_msg == "":
  109. return False, err_msg
  110. return True, err_msg
  111. else:
  112. return False, ""
  113. def check_l4_diff(idx, payload_one, payload_two):
  114. # assumes that the L4 protocol of both packets are the same
  115. # assumes UDP or TCP as L4 protocol
  116. err_msg = ""
  117. if payload_one.name == "UDP":
  118. udp_one, udp_two = payload_one[UDP], payload_two[UDP]
  119. if udp_one.sport != udp_two.sport:
  120. err_msg += "Reason: the packets at index %d have a different source port.\n" % (idx+1)
  121. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (udp_one.sport, udp_two.sport)
  122. if udp_one.dport != udp_two.dport:
  123. err_msg += "Reason: the packets at index %d have a different destination port.\n" % (idx+1)
  124. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (udp_one.dport, udp_two.dport)
  125. if udp_one.len != udp_two.len:
  126. err_msg += "Reason: the packets at index %d have a different length.\n" % (idx+1)
  127. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (udp_one.len, udp_two.len)
  128. if udp_one.chksum != udp_two.chksum:
  129. err_msg += "Reason: the packets at index %d have a different UDP checksum.\n" % (idx+1)
  130. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (udp_one.chksum, udp_two.chksum)
  131. if err_msg == "":
  132. return False, err_msg
  133. return True, err_msg
  134. elif payload_one.name == "TCP":
  135. tcp_one, tcp_two = payload_one[TCP], payload_two[TCP]
  136. if tcp_one.sport != tcp_two.sport:
  137. err_msg += "Reason: the packets at index %d have a different source port.\n" % (idx+1)
  138. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (tcp_one.sport, tcp_two.sport)
  139. if tcp_one.dport != tcp_two.dport:
  140. err_msg += "Reason: the packets at index %d have a different destination port.\n" % (idx+1)
  141. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (tcp_one.dport, tcp_two.dport)
  142. if tcp_one.seq != tcp_two.seq:
  143. err_msg += "Reason: the packets at index %d have a different TCP sequence number.\n" % (idx+1)
  144. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (tcp_one.seq, tcp_two.seq)
  145. if tcp_one.ack != tcp_two.ack:
  146. err_msg += "Reason: the packets at index %d have a different TCP acknowledge number.\n" % (idx+1)
  147. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (tcp_one.ack, tcp_two.ack)
  148. if tcp_one.dataofs != tcp_two.dataofs:
  149. err_msg += "Reason: the packets at index %d have a different TCP data offset.\n" % (idx+1)
  150. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (tcp_one.dataofs, tcp_two.dataofs)
  151. if tcp_one.reserved != tcp_two.reserved:
  152. err_msg += "Reason: the packets at index %d have a different TCP reserved value.\n" % (idx+1)
  153. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (tcp_one.reserved, tcp_two.reserved)
  154. if tcp_one.flags != tcp_two.flags:
  155. err_msg += "Reason: the packets at index %d have different TCP flags.\n" % (idx+1)
  156. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (tcp_one.flags, tcp_two.flags)
  157. if tcp_one.window != tcp_two.window:
  158. err_msg += "Reason: the packets at index %d have a different advertised window size.\n" % (idx+1)
  159. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (tcp_one.window, tcp_two.window)
  160. if tcp_one.chksum != tcp_two.chksum:
  161. err_msg += "Reason: the packets at index %d have a different TCP checksum.\n" % (idx+1)
  162. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (tcp_one.chksum, tcp_two.chksum)
  163. if tcp_one.urgptr != tcp_two.urgptr:
  164. err_msg += "Reason: the packets at index %d have a different TCP urgent pointer.\n" % (idx+1)
  165. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (tcp_one.urgptr, tcp_two.urgptr)
  166. if tcp_one.options != tcp_two.options:
  167. err_msg += "Reason: the packets at index %d have different TCP options.\n" % (idx+1)
  168. err_msg += "Packet 1: %s \t Packet 2: %s\n\n" % (tcp_one.options, tcp_two.options)
  169. if err_msg == "":
  170. return False, err_msg
  171. return True, err_msg
  172. else:
  173. return False, err_msg
  174. def check_payload_diff(idx, payload_one, payload_two):
  175. if payload_one != payload_two:
  176. err_msg = "Reason: the packets at index %d have different payloads.\n" % (idx+1)
  177. err_msg += "Packet 1:\n===================\n"
  178. if payload_one.load is not None:
  179. err_msg += str(payload_one.load)
  180. else:
  181. err_msg += "None"
  182. err_msg += "\n\n"
  183. err_msg += "Packet 2:\n===================\n"
  184. if payload_two.load is not None:
  185. err_msg += str(payload_two.load)
  186. else:
  187. err_msg += "None"
  188. return True, err_msg
  189. else:
  190. return False, ""
  191. def check_different_layers(idx, layer_num, payload_one, payload_two):
  192. if payload_one.name != payload_two.name:
  193. err_msg = "Reason: the packets at index %d have a different layer %d protocol.\n" % (idx+1, layer_num)
  194. err_msg += "Packet 1: %s \t Packet 2: %s\n" % (payload_one.name, payload_two.name)
  195. return True, err_msg
  196. else:
  197. return False, ""
  198. def find_detailed_diff(idx, pkt_one, pkt_two):
  199. def check_reason(check_func, check_same_layer=False):
  200. nonlocal printed_result
  201. nonlocal layer_num
  202. if not check_same_layer:
  203. status, msg = check_func(idx, payload_one, payload_two)
  204. else:
  205. status, msg = check_func(idx, layer_num, payload_one, payload_two)
  206. if status:
  207. if not printed_result:
  208. print("Result: the two PCAPs are not equal.")
  209. print("============================================")
  210. printed_result = True
  211. print("Layer %d:" % layer_num)
  212. print(msg)
  213. return status
  214. payload_one, payload_two = pkt_one, pkt_two
  215. printed_result = False
  216. layer_num = 2
  217. status = check_reason(check_different_layers, True)
  218. if not status:
  219. check_reason(check_l2_diff)
  220. while len(payload_one.payload) != 0:
  221. layer_num += 1
  222. payload_one = payload_one.payload
  223. payload_two = payload_two.payload
  224. if len(payload_two) == 0:
  225. if not printed_result:
  226. print("Result: the two PCAPs are not equal.")
  227. print("============================================")
  228. printed_result = True
  229. print("Reason: the packets at index %d have a different number of layers.\n" % (idx+1))
  230. return
  231. status = check_reason(check_different_layers, True)
  232. if not status:
  233. if layer_num == 3:
  234. check_reason(check_l3_diff)
  235. elif layer_num == 4:
  236. check_reason(check_l4_diff)
  237. elif layer_num > 4:
  238. check_reason(check_payload_diff)
  239. if printed_result:
  240. return
  241. if len(payload_one) == 0 and len(payload_two) != 0:
  242. if not printed_result:
  243. print("Result: the two PCAPs are not equal.")
  244. print("============================================")
  245. print("Reason: the packets at index %d have a different number of layers.\n" % (idx+1))
  246. return
  247. print("Result: the two PCAPs are not equal.")
  248. print("============================================")
  249. print("Reason: could not automatically find a detailed reason.")
  250. def do_rough_comparison(filepath_one, filepath_two):
  251. packets_one = rdpcap(filepath_one)
  252. packets_two = rdpcap(filepath_two)
  253. if len(packets_one) != len(packets_two):
  254. print("Result: the two PCAPs are not equal.")
  255. print("============================================")
  256. print("Reason: they contain a different number of packets.")
  257. return
  258. for i, pkt_one in enumerate(packets_one):
  259. pkt_two = packets_two[i]
  260. # print(pkt_one.payload.payload.payload.name)
  261. # print()
  262. # print(pkt_one.show())
  263. if pkt_one.time != pkt_two.time:
  264. print("Result: the two PCAPs are not equal.")
  265. print("============================================")
  266. print("Reason: the packets at index %d have a different timestamp." % (i+1))
  267. return
  268. if pkt_one != pkt_two:
  269. #print("Result: the two PCAPs are not equal.")
  270. #print("Reason: the packets at index %d have different contents." % (i+1))
  271. find_detailed_diff(i, pkt_one, pkt_two)
  272. return
  273. print("Success")
  274. print("There are no differences between %s and %s" % (filepath_one, filepath_two))
  275. def init_comparison():
  276. if len(sys.argv) != 3:
  277. print("Error: you need to specify two files to compare.\nCannot accept %d argument(s)" % (len(sys.argv)-1))
  278. filepath_one, filepath_two = sys.argv[1], sys.argv[2]
  279. #filepath_one, filepath_two = file_tel, file_tel
  280. #filepath_one, filepath_two = "shortcap.pcap", "shortcap.pcap"
  281. #filepath_one, filepath_two = "file1.pcap", "file3_new.pcap"
  282. print("Comparing %s and %s.\n" % (filepath_one, filepath_two))
  283. do_rough_comparison(filepath_one, filepath_two)
  284. init_comparison()
  285. #change_pcap("file3.pcap")