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