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