blob: 33cb75a365308d7cdb84a0159637393320227127 [file] [log] [blame]
Harald Welte21caf322022-07-16 14:06:46 +02001#!/usr/bin/env python3
2
3import sys
4import logging, colorlog
5import argparse
6from pprint import pprint as pp
7
8from pySim.apdu import *
9from pySim.filesystem import RuntimeState
10
11from pySim.cards import UsimCard
12from pySim.commands import SimCardCommands
13from pySim.profile import CardProfile
14from pySim.ts_102_221 import CardProfileUICCSIM
15from pySim.ts_31_102 import CardApplicationUSIM
16from pySim.ts_31_103 import CardApplicationISIM
17from pySim.transport import LinkBase
18
19from pySim.apdu_source.gsmtap import GsmtapApduSource
20from pySim.apdu_source.pyshark_rspro import PysharkRsproPcap, PysharkRsproLive
21
22from pySim.apdu.ts_102_221 import UiccSelect, UiccStatus
23
24log_format='%(log_color)s%(levelname)-8s%(reset)s %(name)s: %(message)s'
25colorlog.basicConfig(level=logging.INFO, format = log_format)
26logger = colorlog.getLogger()
27
28# merge all of the command sets into one global set. This will override instructions,
29# the one from the 'last' set in the addition below will prevail.
30from pySim.apdu.ts_102_221 import ApduCommands as UiccApduCommands
31from pySim.apdu.ts_31_102 import ApduCommands as UsimApduCommands
32from pySim.apdu.global_platform import ApduCommands as GpApduCommands
33ApduCommands = UiccApduCommands + UsimApduCommands #+ GpApduCommands
34
35
36class DummySimLink(LinkBase):
37 """A dummy implementation of the LinkBase abstract base class. Currently required
38 as the UsimCard doesn't work without SimCardCommands, which in turn require
39 a LinkBase implementation talking to a card.
40
41 In the tracer, we don't actually talk to any card, so we simply drop everything
42 and claim it is successful.
43
44 The UsimCard / SimCardCommands should be refactored to make this obsolete later."""
45 def __init__(self, debug: bool = False, **kwargs):
46 super().__init__(**kwargs)
47 self._debug = debug
48 self._atr = h2i('3B9F96801F878031E073FE211B674A4C753034054BA9')
49
50 def _send_apdu_raw(self, pdu):
51 #print("DummySimLink-apdu: %s" % pdu)
52 return [], '9000'
53
54 def connect(self):
55 pass
56
57 def disconnect(self):
58 pass
59
60 def reset_card(self):
61 return 1
62
63 def get_atr(self):
64 return self._atr
65
66 def wait_for_card(self):
67 pass
68
69
70class Tracer:
71 def __init__(self, **kwargs):
72 # we assume a generic SIM + UICC + USIM + ISIM card
73 profile = CardProfileUICCSIM()
74 profile.add_application(CardApplicationUSIM())
75 profile.add_application(CardApplicationISIM())
76 scc = SimCardCommands(transport=DummySimLink())
77 card = UsimCard(scc)
78 self.rs = RuntimeState(card, profile)
79 # APDU Decoder
80 self.ad = ApduDecoder(ApduCommands)
81 # parameters
82 self.suppress_status = kwargs.get('suppress_status', True)
83 self.suppress_select = kwargs.get('suppress_select', True)
84 self.source = kwargs.get('source', None)
85
86 def format_capdu(self, inst: ApduCommand):
87 """Output a single decoded + processed ApduCommand."""
88 print("%02u %-16s %-35s %-8s %s %s" % (inst.lchan_nr, inst._name, inst.path_str, inst.col_id, inst.col_sw, inst.processed))
89 print("===============================")
90
91 def main(self):
92 """Main loop of tracer: Iterates over all Apdu received from source."""
93 while True:
94 # obtain the next APDU from the source (blocking read)
95 apdu = self.source.read()
96 #print(apdu)
97
98 if isinstance(apdu, CardReset):
99 self.rs.reset()
100 continue
101
102 # ask ApduDecoder to look-up (INS,CLA) + instantiate an ApduCommand derived
103 # class like 'UiccSelect'
104 inst = self.ad.input(apdu)
105 # process the APDU (may modify the RuntimeState)
106 inst.process(self.rs)
107
108 # Avoid cluttering the log with too much verbosity
109 if self.suppress_select and isinstance(inst, UiccSelect):
110 continue
111 if self.suppress_status and isinstance(inst, UiccStatus):
112 continue
113 #print(inst)
114 self.format_capdu(inst)
115
116option_parser = argparse.ArgumentParser(prog='pySim-trace', description='Osmocom pySim high-level SIM card trace decoder',
117 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
118
119global_group = option_parser.add_argument_group('General Options')
Harald Welte72c5b2d2022-07-24 10:21:41 +0200120global_group.add_argument('--no-suppress-select', action='store_false', dest='suppress_select',
121 help="Don't suppress displaying SELECT APDUs")
122global_group.add_argument('--no-suppress-status', action='store_false', dest='suppress_status',
123 help="Don't suppress displaying STATUS APDUs")
Harald Welte21caf322022-07-16 14:06:46 +0200124
125subparsers = option_parser.add_subparsers(help='APDU Source', dest='source', required=True)
126
127parser_gsmtap = subparsers.add_parser('gsmtap-udp', help='Live capture of GSMTAP-SIM on UDP port')
128parser_gsmtap.add_argument('-i', '--bind-ip', default='127.0.0.1',
129 help='Local IP address to which to bind the UDP port')
130parser_gsmtap.add_argument('-p', '--bind-port', default=4729,
131 help='Local UDP port')
132
133parser_rspro_pyshark_pcap = subparsers.add_parser('rspro-pyshark-pcap', help="""
134 PCAP file containing RSPRO (osmo-remsim) communication; processed via pyshark.
135 REQUIRES OSMOCOM PATCHED WIRESHARK!""")
136parser_rspro_pyshark_pcap.add_argument('-f', '--pcap-file', required=True,
137 help='Name of the PCAP[ng] file to be read')
138
139parser_rspro_pyshark_live = subparsers.add_parser('rspro-pyshark-live', help="""
140 Live capture of RSPRO (osmo-remsim) communication; processed via pyshark.
141 REQUIRES OSMOCOM PATCHED WIRESHARK!""")
142parser_rspro_pyshark_live.add_argument('-i', '--interface', required=True,
143 help='Name of the network interface to capture on')
144
145if __name__ == '__main__':
146
147 opts = option_parser.parse_args()
Harald Welte21caf322022-07-16 14:06:46 +0200148
149 logger.info('Opening source %s...' % opts.source)
150 if opts.source == 'gsmtap-udp':
151 s = GsmtapApduSource(opts.bind_ip, opts.bind_port)
152 elif opts.source == 'rspro-pyshark-pcap':
153 s = PysharkRsproPcap(opts.pcap_file)
154 elif opts.source == 'rspro-pyshark-live':
155 s = PysharkRsproLive(opts.interface)
156
Harald Welte72c5b2d2022-07-24 10:21:41 +0200157 tracer = Tracer(source=s, suppress_status=opts.suppress_status, suppress_select=opts.suppress_select)
Harald Welte21caf322022-07-16 14:06:46 +0200158 logger.info('Entering main loop...')
159 tracer.main()
160