7c1270fda73c82dc19aa4aeec704c532255cfa3d
[pingpong.git] / extract_from_tshark.py
1 #!/usr/bin/python\r
2 \r
3 """\r
4 Script used to extract only the needed information from JSON packet traces generated by\r
5 tshark from PCAPNG format\r
6 """\r
7 \r
8 import os, sys\r
9 import json\r
10 import uuid\r
11 \r
12 from collections import OrderedDict\r
13 \r
14 json_key_source = "_source"\r
15 json_key_layers = "layers"\r
16 \r
17 json_key_ip = "ip"\r
18 json_key_tcp = "tcp"\r
19 \r
20 json_key_http = "http"\r
21 json_key_method = "method"\r
22 json_key_uri = "uri"\r
23 json_key_headers = "headers"\r
24 json_key_host = "host"\r
25 \r
26 json_key_http_req = json_key_http + ".request."\r
27 json_key_http_req_method = json_key_http_req + json_key_method\r
28 json_key_http_req_uri = json_key_http_req + json_key_uri\r
29 json_key_http_req_line = json_key_http_req + "line"\r
30 \r
31 json_key_pkt_comment = "pkt_comment"\r
32 \r
33 json_key_frame = "frame"\r
34 json_key_frame_num = json_key_frame + ".number"\r
35 json_key_frame_comment = json_key_frame + ".comment"\r
36 json_key_frame_ts = json_key_frame + ".time_epoch"\r
37 \r
38 \r
39 def make_unique(key, dct):\r
40     counter = 0\r
41     unique_key = key\r
42 \r
43     while unique_key in dct:\r
44         counter += 1\r
45         unique_key = '{}_{}'.format(key, counter)\r
46     return unique_key\r
47 \r
48 \r
49 def parse_object_pairs(pairs):\r
50     dct = OrderedDict()\r
51     for key, value in pairs:\r
52         if key in dct:\r
53             key = make_unique(key, dct)\r
54         dct[key] = value\r
55 \r
56     return dct\r
57 \r
58 def change_file(fpath):\r
59     for fn in os.listdir(fpath):\r
60         full_path = fpath + '/' + fn\r
61 \r
62         # Recursively go through all directories\r
63         if os.path.isdir(full_path):\r
64             change_file(full_path)\r
65             continue\r
66 \r
67         print full_path\r
68         with open(full_path, "r+") as jf:\r
69             # Since certain json 'keys' appear multiple times in our data, we have to make them\r
70             # unique first (we can't use regular json.load() or we lose some data points). From:\r
71             # https://stackoverflow.com/questions/29321677/python-json-parser-allow-duplicate-keys\r
72             decoder = json.JSONDecoder(object_pairs_hook=parse_object_pairs)\r
73             pcap_data = decoder.decode(jf.read())\r
74 \r
75             # Prepare new data structure for re-formatted JSON storage\r
76             data = {}\r
77             for packet in pcap_data:\r
78                 layers = packet[json_key_source][json_key_layers]\r
79 \r
80                 # All captured traffic should have a frame + frame number, but check anyway\r
81                 frame_num = " Frame: "\r
82                 if json_key_frame not in layers or json_key_frame_num not in layers[json_key_frame]:\r
83                     print "WARNING: could not find frame number! Using -1..."\r
84                     frame_num = frame_num + "-1"\r
85                 else:\r
86                     # Save frame number for error-reporting\r
87                     frame_num = frame_num + layers[json_key_frame][json_key_frame_num]\r
88 \r
89                 # All captured traffic should be IP, but check anyway\r
90                 if not json_key_ip in layers:\r
91                     print "WARNING: Non-IP traffic detected!" + frame_num\r
92                     continue\r
93 \r
94                 # For now, focus on HTTP only\r
95                 if json_key_tcp not in layers or json_key_http not in layers:\r
96                     continue\r
97 \r
98                 # Fill our new JSON packet with TCP/IP info\r
99                 new_packet = {}\r
100                 new_packet["dst_ip"] = layers[json_key_ip][json_key_ip + ".dst"]\r
101                 new_packet["dst_port"] = int(layers[json_key_tcp][json_key_tcp + ".dstport"])\r
102 \r
103                 # JV: Also include src so we can see what device initiates the traffic\r
104                 new_packet["src_ip"] = layers[json_key_ip][json_key_ip + ".src"]\r
105                 new_packet["src_port"] = int(layers[json_key_tcp][json_key_tcp + ".srcport"])\r
106 \r
107                 # Go through all HTTP fields and extract the ones that are needed\r
108                 http_data = layers[json_key_http]\r
109                 for http_key in http_data:\r
110                     http_value = http_data[http_key]\r
111 \r
112                     if http_key.startswith(json_key_http_req_line):\r
113                         header_line = http_value.split(":", 1)\r
114                         if len(header_line) != 2:\r
115                             print ("WARNING: could not parse header '" + str(header_line) + "'"\r
116                                    + frame_num)\r
117                             continue\r
118 \r
119                         # Prepare container for HTTP headers\r
120                         if json_key_headers not in new_packet:\r
121                             new_packet[json_key_headers] = {}\r
122 \r
123                         # Use lower case for header keys to stay consistent with our other data\r
124                         header_key = header_line[0].lower()\r
125 \r
126                         # Remove the trailing carriage return\r
127                         header_val = header_line[1].strip()\r
128 \r
129                         # Save the header key-value pair\r
130                         new_packet[json_key_headers][header_key] = header_val\r
131 \r
132                         # If this is the host header, we also save it to the main object\r
133                         if header_key == json_key_host:\r
134                             new_packet[json_key_host] = header_val\r
135 \r
136                     if json_key_http_req_method in http_value:\r
137                         new_packet[json_key_method] = http_value[json_key_http_req_method]\r
138                     if json_key_http_req_uri in http_value:\r
139                         new_packet[json_key_uri] = http_value[json_key_http_req_uri]\r
140 \r
141                 # End of HTTP parsing\r
142 \r
143                 # Check that we found the minimum needed HTTP headers\r
144                 if (json_key_uri not in new_packet or json_key_method not in new_packet or\r
145                         json_key_host not in new_packet):\r
146                     print "Missing some HTTP Headers!" + frame_num\r
147                     continue\r
148 \r
149                 # Extract timestamp\r
150                 if json_key_frame_ts not in layers[json_key_frame]:\r
151                     print "WARNING: could not find timestamp!" + frame_num\r
152                     continue\r
153 \r
154                 new_packet["ts"] = layers[json_key_frame][json_key_frame_ts]\r
155 \r
156                 # Create a unique key for each packet to keep consistent with ReCon\r
157                 # Also good in case packets end up in different files\r
158                 data[str(uuid.uuid4())] = new_packet\r
159 \r
160             # Write the new data\r
161             #print json.dumps(data, sort_keys=True, indent=4)\r
162             jf.seek(0)\r
163             jf.write(json.dumps(data, sort_keys=True, indent=4))\r
164             jf.truncate()\r
165 \r
166 if __name__ == '__main__':\r
167     # Needed to re-use some JSON keys\r
168     change_file(sys.argv[1])