Harald Welte | c95f6e2 | 2022-12-02 22:50:35 +0100 | [diff] [blame] | 1 | # coding=utf-8 |
| 2 | |
| 3 | # (C) 2022 by Harald Welte <laforge@osmocom.org> |
| 4 | # |
| 5 | # This program is free software: you can redistribute it and/or modify |
| 6 | # it under the terms of the GNU General Public License as published by |
| 7 | # the Free Software Foundation, either version 2 of the License, or |
| 8 | # (at your option) any later version. |
| 9 | # |
| 10 | # This program is distributed in the hope that it will be useful, |
| 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | # GNU General Public License for more details. |
| 14 | # |
| 15 | # You should have received a copy of the GNU General Public License |
| 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 17 | |
| 18 | |
| 19 | import sys |
| 20 | import logging |
| 21 | from pprint import pprint as pp |
| 22 | from typing import Tuple |
| 23 | import pyshark |
| 24 | |
| 25 | from pySim.utils import h2b, b2h |
| 26 | from pySim.apdu import Tpdu |
| 27 | from pySim.gsmtap import GsmtapMessage |
| 28 | from . import ApduSource, PacketType, CardReset |
| 29 | |
| 30 | from pySim.apdu.ts_102_221 import ApduCommands as UiccApduCommands |
| 31 | from pySim.apdu.ts_31_102 import ApduCommands as UsimApduCommands |
| 32 | from pySim.apdu.global_platform import ApduCommands as GpApduCommands |
| 33 | ApduCommands = UiccApduCommands + UsimApduCommands + GpApduCommands |
| 34 | |
| 35 | logger = logging.getLogger(__name__) |
| 36 | |
| 37 | class _PysharkGsmtap(ApduSource): |
| 38 | """APDU Source [provider] base class for reading GSMTAP SIM APDU via tshark.""" |
| 39 | |
| 40 | def __init__(self, pyshark_inst): |
| 41 | self.pyshark = pyshark_inst |
| 42 | self.bank_id = None |
| 43 | self.bank_slot = None |
| 44 | self.cmd_tpdu = None |
| 45 | super().__init__() |
| 46 | |
| 47 | def read_packet(self) -> PacketType: |
| 48 | p = self.pyshark.next() |
| 49 | return self._parse_packet(p) |
| 50 | |
| 51 | def _set_or_verify_bank_slot(self, bsl: Tuple[int, int]): |
| 52 | """Keep track of the bank:slot to make sure we don't mix traces of multiple cards""" |
| 53 | if not self.bank_id: |
| 54 | self.bank_id = bsl[0] |
| 55 | self.bank_slot = bsl[1] |
| 56 | else: |
| 57 | if self.bank_id != bsl[0] or self.bank_slot != bsl[1]: |
| 58 | raise ValueError('Received data for unexpected B(%u:%u)' % (bsl[0], bsl[1])) |
| 59 | |
| 60 | def _parse_packet(self, p) -> PacketType: |
| 61 | udp_layer = p['udp'] |
| 62 | udp_payload_hex = udp_layer.get_field('payload').replace(':','') |
| 63 | gsmtap = GsmtapMessage(h2b(udp_payload_hex)) |
| 64 | gsmtap_msg = gsmtap.decode() |
| 65 | if gsmtap_msg['type'] != 'sim': |
| 66 | raise ValueError('Unsupported GSMTAP type %s' % gsmtap_msg['type']) |
| 67 | sub_type = gsmtap_msg['sub_type'] |
| 68 | if sub_type == 'apdu': |
| 69 | return ApduCommands.parse_cmd_bytes(gsmtap_msg['body']) |
| 70 | elif sub_type == 'atr': |
| 71 | # card has been reset |
| 72 | return CardReset() |
| 73 | elif sub_type in ['pps_req', 'pps_rsp']: |
| 74 | # simply ignore for now |
| 75 | pass |
| 76 | else: |
| 77 | raise ValueError('Unsupported GSMTAP-SIM sub-type %s' % sub_type) |
| 78 | |
| 79 | class PysharkGsmtapPcap(_PysharkGsmtap): |
| 80 | """APDU Source [provider] class for reading GSMTAP from a PCAP |
| 81 | file via pyshark, which in turn uses tshark (part of wireshark). |
| 82 | """ |
| 83 | def __init__(self, pcap_filename): |
| 84 | """ |
| 85 | Args: |
| 86 | pcap_filename: File name of the pcap file to be opened |
| 87 | """ |
| 88 | pyshark_inst = pyshark.FileCapture(pcap_filename, display_filter='gsm_sim', use_json=True, keep_packets=False) |
| 89 | super().__init__(pyshark_inst) |
| 90 | |