• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Andreas Korb <andreas.korb@e-mundo.de>
5# Copyright (C) Friedrich Feigel <friedrich.feigel@e-mundo.de>
6# Copyright (C) Nils Weiss <nils@we155.de>
7
8
9import getopt
10import sys
11import signal
12import re
13import traceback
14
15from ast import literal_eval
16
17from scapy.config import conf
18from scapy.consts import LINUX
19
20if not LINUX or conf.use_pypy:
21    conf.contribs['CANSocket'] = {'use-python-can': True}
22
23from scapy.contrib.isotp import ISOTPSocket                    # noqa: E402
24from scapy.contrib.cansocket import CANSocket, PYTHON_CAN      # noqa: E402
25from scapy.contrib.automotive.obd.obd import OBD               # noqa: E402
26from scapy.contrib.automotive.obd.scanner import OBD_Scanner, \
27    OBD_S01_Enumerator, OBD_S02_Enumerator, OBD_S03_Enumerator, \
28    OBD_S06_Enumerator, OBD_S07_Enumerator, OBD_S08_Enumerator, \
29    OBD_S09_Enumerator, OBD_S0A_Enumerator  # noqa: E402
30
31
32def signal_handler(sig, frame):
33    print('Interrupting scan!')
34    sys.exit(0)
35
36
37def usage(is_error):
38    print('''usage:\tobdscanner [-i|--interface] [-c|--channel] [-b|--bitrate]
39                                [-a|--python-can_args] [-h|--help]
40                                [-s|--source] [-d|--destination]
41                                [-t|--timeout] [-f|--full]
42                                [-v|--verbose]\n
43    Scan for all possible obd service classes and their subfunctions.\n
44    optional arguments:
45    -c, --channel               python-can channel or Linux SocketCAN interface name\n
46    additional required arguments for WINDOWS or Python 2:
47    -i, --interface             python-can interface for the scan.
48                                Depends on used interpreter and system,
49                                see examples below. Any python-can interface can
50                                be provided. Please see:
51                                https://python-can.readthedocs.io for
52                                further interface examples.
53    optional arguments:
54    -a, --python-can_args       Additional arguments for a python-can Bus object.
55    -h, --help                  show this help message and exit
56    -s, --source                ISOTP-socket source id (hex)
57    -d, --destination           ISOTP-socket destination id (hex)
58    -t, --timeout               Timeout after which the scanner proceeds to next service [seconds]
59    -f, --full                  Full scan on id services
60    -v, --verbose               Display information during scan
61    -1                          Scan OBD Service 01
62    -2                          Scan OBD Service 02
63    -3                          Scan OBD Service 03
64    -6                          Scan OBD Service 06
65    -7                          Scan OBD Service 07
66    -8                          Scan OBD Service 08
67    -9                          Scan OBD Service 09
68    -A                          Scan OBD Service 0A\n
69    Example of use:\n
70    Python2 or Windows:
71    python2 -m scapy.tools.automotive.obdscanner --interface=pcan --channel=PCAN_USBBUS1 --source=0x070 --destination 0x034
72    python2 -m scapy.tools.automotive.obdscanner --interface vector --channel 0 --source 0x000 --destination 0x734
73    python2 -m scapy.tools.automotive.obdscanner --interface socketcan --channel=can0 --source 0x089 --destination 0x234
74    python2 -m scapy.tools.automotive.obdscanner --interface vector --channel 0 --python-can_args 'bitrate=500000, poll_interval=1' --source=0x070 --destination 0x034\n
75    Python3 on Linux:
76    python3 -m scapy.tools.automotive.obdscanner --channel can0 --source 0x123 --destination 0x456 \n''',  # noqa: E501
77          file=sys.stderr if is_error else sys.stdout)
78
79
80def get_can_socket(channel, interface, python_can_args):
81    if PYTHON_CAN:
82        if python_can_args:
83            arg_dict = dict((k, literal_eval(v)) for k, v in
84                            (pair.split('=') for pair in
85                             re.split(', | |,', python_can_args)))
86            return CANSocket(bustype=interface, channel=channel, **arg_dict)
87        else:
88            return CANSocket(bustype=interface, channel=channel)
89    else:
90        return CANSocket(channel=channel)
91
92
93def get_isotp_socket(csock, source, destination):
94    return ISOTPSocket(csock, source, destination, basecls=OBD, padding=True)
95
96
97def run_scan(isock, enumerators, full_scan, verbose, timeout):
98    s = OBD_Scanner(isock, test_cases=enumerators, full_scan=full_scan,
99                    debug=verbose,
100                    timeout=timeout)
101    print("Starting OBD-Scan...")
102    s.scan()
103    s.show_testcases()
104
105
106def main():
107
108    channel = None
109    interface = None
110    source = 0x7e0
111    destination = 0x7df
112    timeout = 0.1
113    full_scan = False
114    verbose = False
115    python_can_args = None
116    enumerators = []
117    conf.verb = -1
118
119    options = getopt.getopt(
120        sys.argv[1:],
121        'i:c:s:d:a:t:hfv1236789A',
122        ['interface=', 'channel=', 'source=', 'destination=',
123         'help', 'timeout=', 'python-can_args=', 'full',
124         'verbose'])
125
126    try:
127        for opt, arg in options[0]:
128            if opt in ('-i', '--interface'):
129                interface = arg
130            elif opt in ('-c', '--channel'):
131                channel = arg
132            elif opt in ('-a', '--python-can_args'):
133                python_can_args = arg
134            elif opt in ('-s', '--source'):
135                source = int(arg, 16)
136            elif opt in ('-d', '--destination'):
137                destination = int(arg, 16)
138            elif opt in ('-h', '--help'):
139                usage(False)
140                sys.exit(0)
141            elif opt in ('-t', '--timeout'):
142                timeout = float(arg)
143            elif opt in ('-f', '--full'):
144                full_scan = True
145            elif opt == '-1':
146                enumerators += [OBD_S01_Enumerator]
147            elif opt == '-2':
148                enumerators += [OBD_S02_Enumerator]
149            elif opt == '-3':
150                enumerators += [OBD_S03_Enumerator]
151            elif opt == '-6':
152                enumerators += [OBD_S06_Enumerator]
153            elif opt == '-7':
154                enumerators += [OBD_S07_Enumerator]
155            elif opt == '-8':
156                enumerators += [OBD_S08_Enumerator]
157            elif opt == '-9':
158                enumerators += [OBD_S09_Enumerator]
159            elif opt == '-A':
160                enumerators += [OBD_S0A_Enumerator]
161            elif opt in ('-v', '--verbose'):
162                verbose = True
163    except getopt.GetoptError as msg:
164        usage(True)
165        print("ERROR:", msg, file=sys.stderr)
166        raise SystemExit
167
168    if channel is None or \
169            (PYTHON_CAN and interface is None):
170        usage(True)
171        print("\nPlease provide all required arguments.\n",
172              file=sys.stderr)
173        sys.exit(1)
174
175    if 0 > source >= 0x800 or 0 > destination >= 0x800\
176            or source == destination:
177        print("The ids must be >= 0 and < 0x800 and not equal.",
178              file=sys.stderr)
179        sys.exit(1)
180
181    if 0 > timeout:
182        print("The timeout must be a positive value")
183        sys.exit(1)
184
185    csock = None
186    isock = None
187    try:
188        csock = get_can_socket(channel, interface, python_can_args)
189        isock = get_isotp_socket(csock, source, destination)
190
191        signal.signal(signal.SIGINT, signal_handler)
192        run_scan(isock, enumerators, full_scan, verbose, timeout)
193
194    except Exception as e:
195        usage(True)
196        print("\nSocket couldn't be created. Check your arguments.\n",
197              file=sys.stderr)
198        print(e, file=sys.stderr)
199        if verbose:
200            traceback.print_exc(file=sys.stderr)
201        sys.exit(1)
202
203    finally:
204        if isock:
205            isock.close()
206        if csock:
207            csock.close()
208
209
210if __name__ == '__main__':
211    main()
212