mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-22 21:36:12 +03:00
119 lines
4.3 KiB
Python
Executable File
119 lines
4.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
import yaml
|
|
|
|
|
|
class TlsStreamParser:
|
|
STATE_CONTENT_TYPE = 0
|
|
STATE_VERSION_BYTE0 = 1
|
|
STATE_VERSION_BYTE1 = 2
|
|
STATE_LENGTH_BYTE0 = 3
|
|
STATE_LENGTH_BYTE1 = 4
|
|
STATE_DATA = 5
|
|
|
|
TLS_HEADER_SIZE = 5
|
|
|
|
def __init__(self):
|
|
self.state = self.STATE_CONTENT_TYPE
|
|
self.current_length = None
|
|
self.current_remaining = None
|
|
|
|
def read(self, data):
|
|
record_parts = []
|
|
i = 0
|
|
tls_consumed_bytes = 0
|
|
while i < len(data):
|
|
if self.state == self.STATE_CONTENT_TYPE:
|
|
# TODO: add content type description
|
|
content_type = data[i]
|
|
self.state = self.STATE_VERSION_BYTE0
|
|
i += 1
|
|
tls_consumed_bytes += 1
|
|
elif self.state == self.STATE_VERSION_BYTE0:
|
|
self.state = self.STATE_VERSION_BYTE1
|
|
i += 1
|
|
tls_consumed_bytes += 1
|
|
elif self.state == self.STATE_VERSION_BYTE1:
|
|
self.state = self.STATE_LENGTH_BYTE0
|
|
i += 1
|
|
tls_consumed_bytes += 1
|
|
elif self.state == self.STATE_LENGTH_BYTE0:
|
|
self.current_length = data[i]
|
|
self.state = self.STATE_LENGTH_BYTE1
|
|
i += 1
|
|
tls_consumed_bytes += 1
|
|
elif self.state == self.STATE_LENGTH_BYTE1:
|
|
self.current_length = self.current_length * 256 + data[i]
|
|
self.current_remaining = self.current_length
|
|
self.state = self.STATE_DATA
|
|
i += 1
|
|
tls_consumed_bytes += 1
|
|
elif self.state == self.STATE_DATA:
|
|
consume_data = min(self.current_remaining, len(data) - i)
|
|
self.current_remaining -= consume_data
|
|
i += consume_data
|
|
tls_consumed_bytes += consume_data
|
|
if self.current_remaining == 0:
|
|
record_parts.append(
|
|
(tls_consumed_bytes, self.TLS_HEADER_SIZE + self.current_length))
|
|
tls_consumed_bytes = 0
|
|
self.current_length = None
|
|
self.state = self.STATE_CONTENT_TYPE
|
|
if tls_consumed_bytes:
|
|
if self.current_length is None:
|
|
record_parts.append((tls_consumed_bytes, '?'))
|
|
else:
|
|
record_parts.append(
|
|
(tls_consumed_bytes, self.TLS_HEADER_SIZE + self.current_length))
|
|
return record_parts
|
|
|
|
|
|
if len(sys.argv) != 3:
|
|
print(f'Usage: {sys.argv[0]} PCAP_FILE STREAM_ID')
|
|
os.exit(1)
|
|
|
|
file = sys.argv[1]
|
|
stream_id = sys.argv[2]
|
|
result = subprocess.run(['tshark', '-2', '-r', file, '-q', '-z',
|
|
f'follow,tcp,yaml,{stream_id}'], capture_output=True, check=True, text=True)
|
|
|
|
follow_result = yaml.safe_load(result.stdout)
|
|
LOCAL_PEER = 0
|
|
REMOTE_PEER = 1
|
|
assert follow_result['peers'][REMOTE_PEER][
|
|
'port'] == 443, f"assuming the remote peer is the TLS server: {follow_result['peers']}"
|
|
packets = follow_result['packets']
|
|
|
|
upload_stream = TlsStreamParser()
|
|
download_stream = TlsStreamParser()
|
|
rtt = packets[1]['timestamp'] - packets[0]['timestamp']
|
|
time_unit = rtt / 2
|
|
local_timestamp_first = packets[0]['timestamp']
|
|
mitm_timestamp_first = local_timestamp_first + rtt / 4
|
|
min_mitm_timestamp_up = packets[0]['timestamp']
|
|
min_mitm_timestamp_down = packets[0]['timestamp']
|
|
for packet in packets:
|
|
local_timestamp = packet['timestamp']
|
|
|
|
data = packet['data']
|
|
if packet['peer'] == LOCAL_PEER:
|
|
mitm_timestamp = local_timestamp + time_unit / 2
|
|
mitm_timestamp = max(mitm_timestamp, min_mitm_timestamp_up)
|
|
min_mitm_timestamp_up = mitm_timestamp
|
|
|
|
timestamp = (mitm_timestamp - mitm_timestamp_first) / time_unit
|
|
record_parts = upload_stream.read(data)
|
|
print('%.3f' % timestamp, len(data), ','.join(
|
|
f'{i}/{j}' for i, j in record_parts))
|
|
elif packet['peer'] == REMOTE_PEER:
|
|
mitm_timestamp = local_timestamp - time_unit / 2
|
|
mitm_timestamp = max(mitm_timestamp, min_mitm_timestamp_down)
|
|
min_mitm_timestamp_down = mitm_timestamp
|
|
|
|
timestamp = (mitm_timestamp - mitm_timestamp_first) / time_unit
|
|
record_parts = download_stream.read(data)
|
|
print('%.3f' % timestamp, -len(data),
|
|
','.join(f'{i}/{j}' for i, j in record_parts))
|