def __security_seed_wrapper(args): """Wrapper used to initiate security seed dump""" arb_id_request = args.src arb_id_response = args.dst reset_type = args.reset session_type = args.sess_type level = args.sec_level num_seeds = args.num reset_delay = args.delay seed_list = [] try: print("Security seed dump started. Press Ctrl+C to stop.\n") while num_seeds > len(seed_list) or num_seeds == 0: # Extended diagnostics response = extended_session(arb_id_request, arb_id_response, session_type) if not Iso14229_1.is_positive_response(response): print("Unable to enter extended session. Retrying...\n") continue # Request seed response = request_seed(arb_id_request, arb_id_response, level, None, None) if response is None: print("\nInvalid response") elif Iso14229_1.is_positive_response(response): seed_list.append(list_to_hex_str(response[2:])) print("Seed received: {}\t(Total captured: {})".format( list_to_hex_str(response[2:]), len(seed_list)), end="\r") stdout.flush() else: print_negative_response(response) break if reset_type: ecu_reset(arb_id_request, arb_id_response, reset_type, None) time.sleep(reset_delay) except KeyboardInterrupt: print("Interrupted by user.") except ValueError as e: print(e) return if len(seed_list) > 0: print("\n") print("Security Access Seeds captured:") for seed in seed_list: print(seed)
def __ecu_reset_wrapper(args): """Wrapper used to initiate ECU Reset""" arb_id_request = args.src arb_id_response = args.dst reset_type = args.reset_type timeout = args.timeout print( "Sending ECU reset, type 0x{0:02x} to arbitration ID {1} (0x{1:02x})". format(reset_type, arb_id_request)) try: response = ecu_reset(arb_id_request, arb_id_response, reset_type, timeout) except ValueError as e: print("ValueError: {0}".format(e)) return # Decode response if response is None: print("No response was received") else: response_length = len(response) if response_length == 0: # Empty response print("Received empty response") elif response_length == 1: # Invalid response length print( "Received response [{0:02x}] (1 byte), expected at least 2 bytes" .format(response[0], len(response))) elif Iso14229_1.is_positive_response(response): # Positive response handling response_service_id = response[0] subfunction = response[1] expected_response_id = Iso14229_1.get_service_response_id( Services.EcuReset.service_id) if response_service_id == expected_response_id and subfunction == reset_type: # Positive response print("Received positive response") if response_length > 2: # Additional data can be seconds left to reset (powerDownTime) or manufacturer specific additional_data = list_to_hex_str(response[2:], ",") print("Response contains additional data: [{0}]".format( additional_data)) else: # Service and/or subfunction mismatch print( "Response service ID 0x{0:02x} and subfunction 0x{1:02x} do not match expected values " "0x{2:02x} and 0x{3:02x}".format(response_service_id, subfunction, expected_response_id, reset_type)) else: # Negative response handling nrc = response[1] nrc_description = NRC_NAMES.get(nrc, "Unknown NRC value") print( "Received negative response code (NRC) 0x{0:02x}: {1}".format( nrc, nrc_description))
def directive_str(arb_id, data): """ Converts a directive to its string representation :param arb_id: message arbitration ID :param data: message data bytes :return: str representing directive """ data = list_to_hex_str(data, "") directive = "{0:03X}#{1}".format(arb_id, data) return directive
def handle_upload_reply(msg): global byte_counter, bytes_left, dump_complete, timeout_start, segment_counter if msg.arbitration_id != rcv_arb_id: return if msg.data[0] == 0xfe: decode_xcp_error(msg) return if msg.data[0] == 0xff: # Reset timeout timer timeout_start = datetime.now() # Calculate end index of data to handle end_index = min(8, bytes_left + 1) if dump_file: with open(dump_file, "ab") as outfile: outfile.write(bytearray(msg.data[1:end_index])) else: print(list_to_hex_str(msg.data[1:end_index], " ")) # Update counters byte_counter += max_segment_size bytes_left -= max_segment_size if bytes_left < 1: if dump_file: print("\rDumping segment {0} ({1} b, 0 b left)".format( segment_counter, length), end="") print("Dump complete!") dump_complete = True elif byte_counter > max_segment_size - 1: # Dump another segment segment_counter += 1 if dump_file: # Print progress print("\rDumping segment {0} ({1} b, {2} b left)".format( segment_counter, ((segment_counter + 1) * max_segment_size + byte_counter), bytes_left), end="") stdout.flush() byte_counter = 0 can_wrap.send_single_message_with_callback( [0xf5, min(max_segment_size, bytes_left)], handle_upload_reply)
def send_messages(messages, loop): """ Sends a list of messages separated by a given delay. :param loop: bool indicating whether the message sequence should be looped (re-sent over and over) :param messages: List of messages, where a message has the format (arb_id, [data_byte]) """ with CanActions(notifier_enabled=False) as can_wrap: loop_counter = 0 while True: for i in range(len(messages)): msg = messages[i] if i != 0 or loop_counter != 0: sleep(msg.delay) print(" Arb_id: 0x{0:08x}, data: {1}".format(msg.arb_id, list_to_hex_str(msg.data, "."))) can_wrap.send(msg.data, msg.arb_id, msg.is_extended, msg.is_error, msg.is_remote) if not loop: break loop_counter += 1
def do_stuff(my_arbitration_id): """ Performs some example operations, such as sending and receiving CAN messages. :param my_arbitration_id: The default arbitration id to use when sending messages :type my_arbitration_id: int """ # The notifier should only be enabled when handling incoming traffic using callbacks use_notifier = False # Setup CanActions wrapper to use for receiving and sending messages with CanActions(arb_id=my_arbitration_id, notifier_enabled=use_notifier) as can_wrap: # Define message contents my_message = [0x11, 0x22, 0x33, 0x44] # Send message using the default arbitration ID for can_wrap can_wrap.send(data=my_message) # Send the same message again, but on a custom arbitration ID this time my_custom_arbitration_id = 0x123ABC can_wrap.send(data=my_message, arb_id=my_custom_arbitration_id) # Listen for incoming traffic for a while duration_seconds = 1.0 start_time = time.time() end_time = start_time + duration_seconds while time.time() < end_time: # Check if a message is available msg = can_wrap.bus.recv(0) if msg is None: # No message was available right now - continue listening loop continue # If we reach here, a message was received. Let's print it! print("Received a message on channel", msg.channel) print(" Arb ID: 0x{0:x} ({0})".format(msg.arbitration_id)) data_string = list_to_hex_str(msg.data, ".") print(" Data: ", data_string) # Module logic for message handling goes here if msg.arbitration_id < 0x10: print(" That was a low arbitration ID!") # When we reach here, can_wrap has been closed print("\nDone!")
def mutate_fuzz(initial_arb_id, initial_data, arb_id_bitmap, data_bitmap, filename=None, show_status=True, show_responses=False, seed=None): """ Performs mutation based fuzzing of selected nibbles of a given arbitration ID and data. Nibble selection is controlled by bool lists 'arb_id_bitmap' and 'data_bitmap'. :param initial_arb_id: list of nibbles (ints in interval 0x0-0xF, inclusive) :param initial_data: list of nibbles (ints in interval 0x0-0xF, inclusive) :param arb_id_bitmap: list of bool values, representing which nibbles of 'initial_arb_id' to bruteforce :param data_bitmap: list of bool values, representing which nibbles of 'initial_data' to bruteforce :param filename: file to write cansend directives to :param show_status: bool indicating whether current message and counter should be printed to stdout :param show_responses: bool indicating whether responses should be printed to stdout :param seed: use given seed instead of random seed """ # Seed handling set_seed(seed) def response_handler(msg): # Callback handler for printing incoming messages if msg.arbitration_id != arb_id or list(msg.data) != data: response_directive = directive_str(msg.arbitration_id, msg.data) print(" Received {0}".format(response_directive)) number_of_nibbles_to_fuzz_arb_id = sum(arb_id_bitmap) number_of_nibbles_to_fuzz_data = sum(data_bitmap) file_logging_enabled = filename is not None output_file = None # Set initial values - needed in case they are static data = None arb_id = None if number_of_nibbles_to_fuzz_data == 0: data = apply_fuzzed_data(initial_data, [], data_bitmap) if number_of_nibbles_to_fuzz_arb_id == 0: arb_id = int_from_byte_list( apply_fuzzed_data(initial_arb_id, [], arb_id_bitmap)) try: if file_logging_enabled: output_file = open(filename, "a") with CanActions() as can_wrap: if show_responses: can_wrap.add_listener(response_handler) message_count = 0 while True: if number_of_nibbles_to_fuzz_arb_id > 0: # Mutate arbitration ID fuzzed_nibbles_arb_id = [ random.randint(0, 0xF) for _ in range(number_of_nibbles_to_fuzz_arb_id) ] arb_id_bytes = apply_fuzzed_data(initial_arb_id, fuzzed_nibbles_arb_id, arb_id_bitmap) arb_id = int_from_byte_list(arb_id_bytes) if number_of_nibbles_to_fuzz_data > 0: # Mutate data fuzzed_nibbles_data = [ random.randint(0, 0xF) for _ in range(number_of_nibbles_to_fuzz_data) ] data = apply_fuzzed_data(initial_data, fuzzed_nibbles_data, data_bitmap) if show_status: print("\rSending {0:04x} # {1} ({2})".format( arb_id, list_to_hex_str(data, " "), message_count), end="") stdout.flush() can_wrap.send(data, arb_id) message_count += 1 # Log to file if file_logging_enabled: write_directive_to_file_handle(output_file, arb_id, data) sleep(DELAY_BETWEEN_MESSAGES) except KeyboardInterrupt: if show_status: print() finally: if output_file is not None: output_file.close()
def bruteforce_fuzz(arb_id, initial_data, data_bitmap, filename=None, start_index=0, show_progress=True, show_responses=True): """ Performs a brute force of selected data nibbles for a given arbitration ID. Nibble selection is controlled by bool list 'data_bitmap'. Example: bruteforce_fuzz(0x123, [0x1, 0x2, 0xA, 0xB], [True, False, False, True]) will cause the following messages to be sent: 0x123#02A0 0x123#02A1 0x123#02A2 (...) 0x123#02AF 0x123#12A0 0x123#12A1 (...) 0x123#F2AF :param arb_id: int arbitration ID :param initial_data: list of nibbles (ints in interval 0x0-0xF, inclusive) :param data_bitmap: list of bool values, representing which nibbles of 'initial_data' to bruteforce :param filename: file to write cansend directives to :param start_index: int index to start at (can be used to resume interrupted session) :param show_progress: bool indicating whether progress should be printed to stdout :param show_responses: bool indicating whether responses should be printed to stdout """ # Sanity checks if not 2 <= len(initial_data) <= 16: raise ValueError( "Invalid initial data: must be between 2 and 16 nibbles") if not len(initial_data) % 2 == 0: raise ValueError("Invalid initial data: must have an even length") if not len(initial_data) == len(data_bitmap): raise ValueError( "Initial data ({0}) and data bitmap ({1}) must have the same length" .format(len(initial_data), len(data_bitmap))) number_of_nibbles_to_bruteforce = sum(data_bitmap) end_index = 16**number_of_nibbles_to_bruteforce if not 0 <= start_index <= end_index: raise ValueError( "Invalid start index '{0}', current range is [0-{1}]".format( start_index, end_index)) def response_handler(msg): # Callback handler for printing incoming messages if msg.arbitration_id != arb_id or list(msg.data) != output_data: response_directive = directive_str(msg.arbitration_id, msg.data) print(" Received {0}".format(response_directive)) # Initialize fuzzed nibble generator nibble_values = range(0xF + 1) fuzz_data = product(nibble_values, repeat=number_of_nibbles_to_bruteforce) file_logging_enabled = filename is not None output_file = None output_data = [] try: if file_logging_enabled: output_file = open(filename, "a") with CanActions(arb_id=arb_id) as can_wrap: if show_progress: print("Starting at index {0} of {1}\n".format( start_index, end_index)) if show_responses: can_wrap.add_listener(response_handler) message_count = 0 # Traverse all outputs from fuzz generator for current_fuzzed_nibbles in fuzz_data: # Skip handling until start_index is met if message_count < start_index: message_count += 1 continue # Apply fuzzed data output_data = apply_fuzzed_data(initial_data, current_fuzzed_nibbles, data_bitmap) # Send message can_wrap.send(output_data) message_count += 1 if show_progress: print("\rCurrent: {0} Index: {1}".format( list_to_hex_str(output_data, " "), message_count), end="") stdout.flush() # Log to file if file_logging_enabled: write_directive_to_file_handle(output_file, arb_id, output_data) sleep(DELAY_BETWEEN_MESSAGES) if show_progress: print() finally: if output_file is not None: output_file.close() if show_progress: print("Brute force finished")
def print_msg_as_text(msg): print(list_to_hex_str(msg.data[1:], ""))
def __str__(self): return "[{0}]".format(list_to_hex_str(self.message_data, ", "))