Initial commit: simtrace2-pysniff v0.1.0
Python-based SIMtrace2 sniffer — drop-in replacement for simtrace2-sniff. Single dependency (pyusb), auto-reconnect, GSMTAP/PCAP output, library API. Modules: protocol.py — SIMtrace2 USB sniffing protocol parser device.py — USB discovery + SniffSession with exponential backoff reconnect gsmtap.py — GSMTAP UDP sender for Wireshark pcap.py — PCAP file writer (LINKTYPE_GSMTAP) dump.py — hex dump formatters + file output __main__.py — CLI entry point Startup script sniff-start.sh handles udev permission checks and shows install instructions. udev rule uses TAG+=uaccess (systemd-logind ACL).
This commit is contained in:
commit
73b0277ef7
13 files changed
+1289
No files matched your search
@@ -0,0 +1,106 @@
|
||||
"""Hex dump formatters for simtrace2 sniffing output."""
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def hexdump(data, sep=' '):
|
||||
"""Return hex string representation of bytes.
|
||||
|
||||
>>> hexdump(b'\\x3b\\x9e\\x12\\x34')
|
||||
'3b 9e 12 34'
|
||||
"""
|
||||
return data.hex(sep)
|
||||
|
||||
|
||||
def _flag_str(flags, flag_names):
|
||||
if not flags:
|
||||
return ''
|
||||
names = []
|
||||
for bit, name in sorted(flag_names.items()):
|
||||
if flags & bit:
|
||||
names.append(name)
|
||||
flags &= ~bit
|
||||
if flags:
|
||||
names.append(f'unknown(0x{flags:08x})')
|
||||
return ', '.join(names)
|
||||
|
||||
|
||||
def format_message(msg, *, show_flags=True):
|
||||
"""Format a SniffMessage as a human-readable line.
|
||||
|
||||
Returns a str suitable for stdout or file output.
|
||||
"""
|
||||
from .protocol import (
|
||||
CHANGE_FLAG_CARD_INSERT, CHANGE_FLAG_CARD_EJECT,
|
||||
CHANGE_FLAG_RESET_ASSERT, CHANGE_FLAG_RESET_DEASSERT,
|
||||
CHANGE_FLAG_TIMEOUT_WT,
|
||||
DATA_FLAG_ERROR_INCOMPLETE, DATA_FLAG_ERROR_MALFORMED,
|
||||
DATA_FLAG_ERROR_CHECKSUM, DATA_FLAG_ERROR_OVERRUN,
|
||||
DATA_FLAG_ERROR_FRAMING, DATA_FLAG_ERROR_PARITY,
|
||||
)
|
||||
|
||||
change_flag_names = {
|
||||
CHANGE_FLAG_CARD_INSERT: 'card inserted',
|
||||
CHANGE_FLAG_CARD_EJECT: 'card ejected',
|
||||
CHANGE_FLAG_RESET_ASSERT: 'reset asserted',
|
||||
CHANGE_FLAG_RESET_DEASSERT: 'reset de-asserted',
|
||||
CHANGE_FLAG_TIMEOUT_WT: 'waiting time timeout',
|
||||
}
|
||||
|
||||
data_flag_names = {
|
||||
DATA_FLAG_ERROR_INCOMPLETE: 'incomplete',
|
||||
DATA_FLAG_ERROR_MALFORMED: 'malformed',
|
||||
DATA_FLAG_ERROR_CHECKSUM: 'checksum error',
|
||||
DATA_FLAG_ERROR_OVERRUN: 'overrun',
|
||||
DATA_FLAG_ERROR_FRAMING: 'framing error',
|
||||
DATA_FLAG_ERROR_PARITY: 'parity error',
|
||||
}
|
||||
|
||||
type_label = msg.type.upper()
|
||||
|
||||
if msg.type == 'change':
|
||||
flags_str = _flag_str(msg.flags, change_flag_names)
|
||||
line = f'Card state change: {flags_str}' if flags_str else 'Card state change: no changes'
|
||||
elif msg.type == 'fidi':
|
||||
fidi = msg.data[0] if len(msg.data) >= 1 else 0
|
||||
fi = fidi >> 4
|
||||
di = fidi & 0x0f
|
||||
line = f'Fi/Di switched to {_fi_table.get(fi, fi)}/{_di_table.get(di, di)}'
|
||||
else:
|
||||
flags_str = ''
|
||||
if show_flags and msg.flags:
|
||||
flags_str = f' ({_flag_str(msg.flags, data_flag_names)})'
|
||||
line = f'{type_label}{flags_str}: {hexdump(msg.data)}'
|
||||
|
||||
return line
|
||||
|
||||
|
||||
class FileDumper:
|
||||
"""Write formatted sniff messages to a file."""
|
||||
|
||||
def __init__(self, path):
|
||||
self._f = open(path, 'a', buffering=1)
|
||||
|
||||
def write(self, msg):
|
||||
line = format_message(msg)
|
||||
self._f.write(line + '\n')
|
||||
|
||||
def close(self):
|
||||
self._f.close()
|
||||
|
||||
|
||||
# ISO 7816-3:2006 Table 7
|
||||
_fi_table = {
|
||||
0: 372, 1: 372, 2: 558, 3: 744,
|
||||
4: 1116, 5: 1488, 6: 1860, 7: 0,
|
||||
8: 0, 9: 512, 10: 768, 11: 1024,
|
||||
12: 1536, 13: 2048, 14: 0, 15: 0,
|
||||
}
|
||||
|
||||
# ISO 7816-3:2006 Table 8
|
||||
_di_table = {
|
||||
0: 0, 1: 1, 2: 2, 3: 4,
|
||||
4: 8, 5: 16, 6: 32, 7: 64,
|
||||
8: 12, 9: 20, 10: 2, 11: 4,
|
||||
12: 8, 13: 16, 14: 32, 15: 64,
|
||||
}
|
||||
Reference in new issue
Block a user