def GMLAN_InitDiagnostics(sock, broadcast_socket=None, timeout=None, verbose=None, retry=0): # noqa: E501 # type: (ISOTPSocket, Optional[ISOTPSocket], Optional[int], Optional[bool], int) -> bool # noqa: E501 """ Send messages to put an ECU into diagnostic/programming state. :param sock: socket for communication. :param broadcast_socket: socket for broadcasting. If provided some message will be sent as broadcast. Recommended when used on a network with several ECUs. :param timeout: timeout for sending, receiving or sniffing packages. :param verbose: set verbosity level :param retry: number of retries in case of failure. :return: True on success else False """ # Helper function def _send_and_check_response(sock, req, timeout, verbose): # type: (ISOTPSocket, Packet, Optional[int], Optional[bool]) -> bool if verbose: print("Sending %s" % repr(req)) resp = sock.sr1(req, timeout=timeout, verbose=False) return _check_response(resp, verbose) if verbose is None: verbose = conf.verb > 0 retry = abs(retry) while retry >= 0: retry -= 1 # DisableNormalCommunication p = GMLAN(service="DisableNormalCommunication") if broadcast_socket is None: if not _send_and_check_response(sock, p, timeout, verbose): continue else: if verbose: print("Sending %s as broadcast" % repr(p)) broadcast_socket.send(p) time.sleep(0.05) # ReportProgrammedState p = GMLAN(service="ReportProgrammingState") if not _send_and_check_response(sock, p, timeout, verbose): continue # ProgrammingMode requestProgramming p = GMLAN() / GMLAN_PM(subfunction="requestProgrammingMode") if not _send_and_check_response(sock, p, timeout, verbose): continue time.sleep(0.05) # InitiateProgramming enableProgramming # No response expected p = GMLAN() / GMLAN_PM(subfunction="enableProgrammingMode") if verbose: print("Sending %s" % repr(p)) sock.send(p) time.sleep(0.05) return True return False
def GMLAN_InitDiagnostics(sock, broadcastsocket=None, timeout=None, verbose=None, retry=0): """Send messages to put an ECU into an diagnostic/programming state. Args: sock: socket to send the message on. broadcast: socket for broadcasting. If provided some message will be sent as broadcast. Recommended when used on a network with several ECUs. timeout: timeout for sending, receiving or sniffing packages. verbose: set verbosity level retry: number of retries in case of failure. Returns true on success. """ if verbose is None: verbose = conf.verb retry = abs(retry) while retry >= 0: retry -= 1 # DisableNormalCommunication p = GMLAN(service="DisableNormalCommunication") if broadcastsocket is None: if not _send_and_check_response(sock, p, timeout, verbose): continue else: if verbose: print("Sending %s as broadcast" % repr(p)) broadcastsocket.send(p) time.sleep(0.05) # ReportProgrammedState p = GMLAN(service="ReportProgrammingState") if not _send_and_check_response(sock, p, timeout, verbose): continue # ProgrammingMode requestProgramming p = GMLAN() / GMLAN_PM(subfunction="requestProgrammingMode") if not _send_and_check_response(sock, p, timeout, verbose): continue time.sleep(0.05) # InitiateProgramming enableProgramming # No response expected p = GMLAN() / GMLAN_PM(subfunction="enableProgrammingMode") if verbose: print("Sending %s" % repr(p)) sock.send(p) time.sleep(0.05) return True return False
def _get_initial_requests(self, **kwargs): # type: (Any) -> Iterable[Packet] scan_range = kwargs.pop("scan_range", range(0x100)) rdbi_enumerator = kwargs.pop("rdbi_enumerator", None) if rdbi_enumerator is None: return (GMLAN() / GMLAN_WDBI(dataIdentifier=x) for x in scan_range) elif isinstance(rdbi_enumerator, GMLAN_RDBIEnumerator): return (GMLAN() / GMLAN_WDBI(dataIdentifier=t.resp.dataIdentifier, dataRecord=bytes(t.resp)[2:]) for t in rdbi_enumerator.filtered_results if t.resp.service != 0x7f and len(bytes(t.resp)) >= 2) else: raise Scapy_Exception("rdbi_enumerator has to be an instance " "of GMLAN_RDBIEnumerator")
def execute(self, socket, state, timeout=1, execution_time=1200, **kwargs): # type: (_SocketUnion, EcuState, int, int, Any) -> None supported = GMLAN_InitDiagnostics(cast(SuperSocket, socket), timeout=20, verbose=kwargs.get("debug", False)) # TODO: Refactor result storage if supported: self._store_result(state, GMLAN() / GMLAN_PM(), GMLAN(service=0xE5)) else: self._store_result( state, GMLAN() / GMLAN_PM(), GMLAN() / GMLAN_NR(returnCode=0x11, requestServiceId=0xA5)) self._state_completed[state] = True
def GMLAN_TransferData(sock, addr, payload, maxmsglen=None, timeout=None, verbose=None, retry=0): # noqa: E501 # type: (ISOTPSocket, int, bytes, Optional[int], Optional[int], Optional[bool], int) -> bool # noqa: E501 """ Send TransferData message. Usually used after calling RequestDownload. :param sock: socket to send the message on. :param addr: destination memory address on the ECU. :param payload: data to be sent. :param maxmsglen: maximum length of a single iso-tp message. default: maximum length :param timeout: timeout for sending, receiving or sniffing packages. :param verbose: set verbosity level. :param retry: number of retries in case of failure. :return: True on success. """ if verbose is None: verbose = conf.verb > 0 retry = abs(retry) startretry = retry scheme = conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] if addr < 0 or addr >= 2**(8 * scheme): warning("Error: Invalid address %s for scheme %s", hex(addr), str(scheme)) return False # max size of dataRecord according to gmlan protocol if maxmsglen is None or maxmsglen <= 0 or maxmsglen > (4093 - scheme): maxmsglen = (4093 - scheme) maxmsglen = cast(int, maxmsglen) for i in range(0, len(payload), maxmsglen): retry = startretry while True: if len(payload[i:]) > maxmsglen: transdata = payload[i:i + maxmsglen] else: transdata = payload[i:] pkt = GMLAN() / GMLAN_TD(startingAddress=addr + i, dataRecord=transdata) resp = sock.sr1(pkt, timeout=timeout, verbose=0) if _check_response(resp, verbose): break retry -= 1 if retry >= 0: if verbose: print("Retrying..") else: return False return True
def GMLAN_RequestDownload(sock, length, timeout=None, verbose=None, retry=0): """Send RequestDownload message. Usually used before calling TransferData. Args: sock: socket to send the message on. length: value for the message's parameter 'unCompressedMemorySize'. timeout: timeout for sending, receiving or sniffing packages. verbose: set verbosity level. retry: number of retries in case of failure. Returns true on success. """ if verbose is None: verbose = conf.verb retry = abs(retry) while retry >= 0: # RequestDownload pkt = GMLAN() / GMLAN_RD(memorySize=length) resp = sock.sr1(pkt, timeout=timeout, verbose=0) if _check_response(resp, verbose): return True retry -= 1 if retry >= 0 and verbose: print("Retrying..") return False
def GMLAN_RequestDownload(sock, length, timeout=None, verbose=None, retry=0): # type: (SuperSocket, int, Optional[int], Optional[bool], int) -> bool """ Send RequestDownload message. Usually used before calling TransferData. :param sock: socket to send the message on. :param length: value for the message's parameter 'unCompressedMemorySize'. :param timeout: timeout for sending, receiving or sniffing packages. :param verbose: set verbosity level. :param retry: number of retries in case of failure. :return: True on success """ if verbose is None: verbose = conf.verb > 0 retry = abs(retry) while retry >= 0: # RequestDownload pkt = GMLAN() / GMLAN_RD(memorySize=length) resp = sock.sr1(pkt, timeout=timeout, verbose=0) if _check_response(resp, verbose): return True retry -= 1 if retry >= 0 and verbose: print("Retrying..") return False
def _get_initial_requests(self, **kwargs): # type: (Any) -> Iterable[Packet] services = set(x & ~0x40 for x in range(0x100)) services.remove(0x10) # Remove InitiateDiagnosticOperation service services.remove(0x3E) # Remove TesterPresent service services.remove(0xa5) # Remove ProgrammingMode service services.remove(0x34) # Remove RequestDownload return (GMLAN(service=x) for x in services)
def __init__(self, sock, pkt=GMLAN(service="TesterPresent"), interval=2): """ Thread to send TesterPresent messages packets periodically Args: sock: socket where packet is sent periodically pkt: packet to send interval: interval between two packets """ PeriodicSenderThread.__init__(self, sock, pkt, interval)
def get_key_pkt(seed, keyfunction, level=1): # type: (Packet, Callable[[int], int], int) -> Optional[Packet] try: s = seed.securitySeed except AttributeError: return None return cast(Packet, GMLAN() / GMLAN_SA(subfunction=level + 1, securityKey=keyfunction(s)))
def __init__(self, sock, pkt=GMLAN(service="TesterPresent"), interval=2): # type: (SuperSocket, Packet, int) -> None """ Thread to send GMLAN TesterPresent packets periodically :param sock: socket where packet is sent periodically :param pkt: packet to send :param interval: interval between two packets """ PeriodicSenderThread.__init__(self, sock, pkt, interval)
def enter_diagnostic_session(socket): # type: (_SocketUnion) -> bool ans = socket.sr1( GMLAN() / GMLAN_IDO(subfunction=2), timeout=5, verbose=False) if ans is not None and ans.service == 0x7f: log_interactive.debug( "[-] InitiateDiagnosticOperation received negative response!\n" "%s", repr(ans)) return ans is not None and ans.service != 0x7f
def _get_initial_requests(self, **kwargs): # type: (Any) -> Iterable[Packet] scan_range = kwargs.pop("scan_range", range(0x1ff)) temp = conf.contribs["GMLAN"]['GMLAN_ECU_AddressingScheme'] # Shift operations to eliminate addresses not aligned to 4 max_addr = (2**(temp * 8) - 1) >> 2 addresses = (random.randint(0, max_addr) << 2 for _ in scan_range) return (GMLAN() / GMLAN_TD(subfunction=0, startingAddress=x) for x in addresses)
def _get_initial_requests(self, **kwargs): # type: (Any) -> Iterable[Packet] self.probe_width = kwargs.pop("probe_width", self.probe_width) self.random_probes_len = \ kwargs.pop("random_probes_len", self.random_probes_len) self.sequential_probes_len = \ kwargs.pop("sequential_probes_len", self.sequential_probes_len) addresses = random.sample( range(0, self.highest_possible_addr, self.probe_width), self.random_probes_len) scan_range = kwargs.pop("scan_range", addresses) return (GMLAN() / GMLAN_RMBA(memoryAddress=x, memorySize=self.probe_width) for x in scan_range)
def enter(socket, # type: _SocketUnion configuration, # type: AutomotiveTestCaseExecutorConfiguration kwargs # type: Dict[str, Any] ): # type: (...) -> bool if configuration.unittest: configuration["tps"] = None socket.sr1(GMLAN(service=0x3E), timeout=0.1, verbose=False) return True GMLAN_TPEnumerator.cleanup(socket, configuration) configuration["tps"] = GMLAN_TesterPresentSender( cast(SuperSocket, socket)) configuration["tps"].start() return True
def GMLAN_ReadMemoryByAddress( sock, # type: SuperSocket addr, # type: int length, # type: int timeout=None, # type: Optional[int] verbose=None, # type: Optional[bool] retry=0 # type: int ): # type: (...) -> Optional[bytes] """ Read data from ECU memory. :param sock: socket to send the data on. :param addr: source memory address on the ECU. :param length: bytes to read. :param timeout: timeout for sending, receiving or sniffing packages. :param verbose: set verbosity level. :param retry: number of retries in case of failure. :return: bytes red or None """ if verbose is None: verbose = conf.verb > 0 retry = abs(retry) scheme = conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] if addr < 0 or addr >= 2**(8 * scheme): warning("Error: Invalid address %s for scheme %s", hex(addr), str(scheme)) return None # max size of dataRecord according to gmlan protocol if length <= 0 or length > (4094 - scheme): warning( "Error: Invalid length %s for scheme %s. " "Choose between 0x1 and %s", hex(length), str(scheme), hex(4094 - scheme)) return None while retry >= 0: # RequestDownload pkt = GMLAN() / GMLAN_RMBA(memoryAddress=addr, memorySize=length) resp = sock.sr1(pkt, timeout=timeout, verbose=0) if _check_response(resp, verbose): return cast(Packet, resp).dataRecord retry -= 1 if retry >= 0 and verbose: print("Retrying..") return None
def GMLAN_ReadMemoryByAddress(sock, addr, length, timeout=None, verbose=None, retry=0): """Read data from ECU memory. Args: sock: socket to send the data on. addr: source memory address on the ECU. length: bytes to read timeout: timeout for sending, receiving or sniffing packages. verbose: set verbosity level. retry: number of retries in case of failure. Returns the bytes read. """ if verbose is None: verbose = conf.verb retry = abs(retry) scheme = conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] if addr < 0 or addr >= 2**(8 * scheme): warning("Error: Invalid address %s for scheme %s", hex(addr), str(scheme)) return None # max size of dataRecord according to gmlan protocol if length <= 0 or length > (4094 - scheme): warning( "Error: Invalid length %s for scheme %s. " "Choose between 0x1 and %s", hex(length), str(scheme), hex(4094 - scheme)) return None while retry >= 0: # RequestDownload pkt = GMLAN() / GMLAN_RMBA(memoryAddress=addr, memorySize=length) resp = sock.sr1(pkt, timeout=timeout, verbose=0) if _check_response(resp, verbose): return resp.dataRecord retry -= 1 if retry >= 0 and verbose: print("Retrying..") return None
def get_seed_pkt(sock, level=1): # type: (_SocketUnion, int) -> Optional[Packet] req = GMLAN() / GMLAN_SA(subfunction=level) for _ in range(10): seed = sock.sr1(req, timeout=5, verbose=False) if seed is None: return None elif seed.service == 0x7f and \ GMLAN_Enumerator._get_negative_response_code(seed) != 0x37: log_interactive.info("Security access no seed! NR: %s", repr(seed)) return None elif seed.service == 0x7f and \ GMLAN_Enumerator._get_negative_response_code(seed) == 0x37: log_interactive.info("Security access retry to get seed") time.sleep(10) continue else: return seed return None
def GMLAN_GetSecurityAccess(sock, key_function, level=1, timeout=None, verbose=None, retry=0): # noqa: E501 # type: (ISOTPSocket, Callable[[int], int], int, Optional[int], Optional[bool], int) -> bool # noqa: E501 """ Authenticate on ECU. Implements Seey-Key procedure. :param sock: socket to send the message on. :param key_function: function implementing the key algorithm. :param level: level of access :param timeout: timeout for sending, receiving or sniffing packages. :param verbose: set verbosity level :param retry: number of retries in case of failure. :return: True on success. """ if verbose is None: verbose = conf.verb > 0 retry = abs(retry) if key_function is None: return False if level % 2 == 0: warning("Parameter Error: Level must be an odd number.") return False while retry >= 0: retry -= 1 request = GMLAN() / GMLAN_SA(subfunction=level) if verbose: print("Requesting seed..") resp = sock.sr1(request, timeout=timeout, verbose=0) if not _check_response(resp, verbose): if verbose: print("Negative Response.") continue seed = resp.securitySeed if seed == 0: if verbose: print("ECU security already unlocked. (seed is 0x0000)") return True keypkt = GMLAN() / GMLAN_SA(subfunction=level + 1, securityKey=key_function(seed)) if verbose: print("Responding with key..") resp = sock.sr1(keypkt, timeout=timeout, verbose=0) if resp is None: if verbose: print("Timeout.") continue if verbose: resp.show() if resp.sprintf("%GMLAN.service%") == "SecurityAccessPositiveResponse": # noqa: E501 if verbose: print("SecurityAccess granted.") return True # Invalid Key elif resp.sprintf("%GMLAN.service%") == "NegativeResponse" and \ resp.sprintf("%GMLAN.returnCode%") == "InvalidKey": if verbose: print("Key invalid") continue return False
def GMLAN_GetSecurityAccess( sock, # type: SuperSocket key_function, # type: Callable[[int], int] level=1, # type: int timeout=None, # type: Optional[int] verbose=None, # type: Optional[bool] retry=0 # type: int ): # type: (...) -> bool """ Authenticate on ECU. Implements Seey-Key procedure. :param sock: socket to send the message on. :param key_function: function implementing the key algorithm. :param level: level of access :param timeout: timeout for sending, receiving or sniffing packages. :param verbose: set verbosity level :param retry: number of retries in case of failure. :return: True on success. """ if verbose is None: verbose = conf.verb > 0 retry = abs(retry) if key_function is None: return False if level % 2 == 0: warning("Parameter Error: Level must be an odd number.") return False while retry >= 0: retry -= 1 request = GMLAN() / GMLAN_SA(subfunction=level) if verbose: print("Requesting seed..") resp = sock.sr1(request, timeout=timeout, verbose=0) if not _check_response(resp, verbose): if resp is not None and resp.returnCode == 0x37 and retry: if verbose: print("RequiredTimeDelayNotExpired. Wait 10s.") time.sleep(10) if verbose: print("Negative Response.") continue seed = cast(Packet, resp).securitySeed if seed == 0: if verbose: print("ECU security already unlocked. (seed is 0x0000)") return True keypkt = GMLAN() / GMLAN_SA(subfunction=level + 1, securityKey=key_function(seed)) if verbose: print("Responding with key..") resp = sock.sr1(keypkt, timeout=timeout, verbose=0) if resp is None: if verbose: print("Timeout.") continue if verbose: resp.show() if resp.service == 0x67: if verbose: print("SecurityAccess granted.") return True # Invalid Key elif resp.service == 0x7f and resp.returnCode == 0x35: if verbose: print("Key invalid") continue return False
def _get_initial_requests(self, **kwargs): # type: (Any) -> Iterable[Packet] return (GMLAN() / GMLAN_SA(subfunction=x) for x in range(1, 10, 2))
def _get_initial_requests(self, **kwargs): # type: (Any) -> Iterable[Packet] scan_range = kwargs.pop("scan_range", range(0x100)) return (GMLAN() / GMLAN_DC(CPIDNumber=x) for x in scan_range)
def _get_initial_requests(self, **kwargs): # type: (Any) -> Iterable[Packet] return [GMLAN() / GMLAN_IDO(subfunction=2)]
def post_execute(self, socket, state, global_configuration): # type: (_SocketUnion, EcuState, AutomotiveTestCaseExecutorConfiguration) -> None # noqa: E501 if not self._state_completed[state]: return if not self.random_probe_finished[state]: log_interactive.info("[i] Random memory probing finished") self.random_probe_finished[state] = True for tup in [ t for t in self.results_with_positive_response if t.state == state ]: self.points_of_interest[state].append( (tup.req.memoryAddress, True)) self.points_of_interest[state].append( (tup.req.memoryAddress, False)) if not len(self.points_of_interest[state]): return log_interactive.info( "[i] Create %d memory points for sequential probing" % len(self.points_of_interest[state])) tested_addrs = [tup.req.memoryAddress for tup in self.results] pos_addrs = [ tup.req.memoryAddress for tup in self.results_with_positive_response if tup.state == state ] new_requests = list() new_points_of_interest = list() for poi, upward in self.points_of_interest[state]: if poi not in pos_addrs: continue temp_new_requests = list() for i in range(self.probe_width, self.sequential_probes_len + self.probe_width, self.probe_width): if upward: new_addr = min(poi + i, self.highest_possible_addr) else: new_addr = max(poi - i, 0) if new_addr not in tested_addrs: pkt = GMLAN() / GMLAN_RMBA(memoryAddress=new_addr, memorySize=self.probe_width) temp_new_requests.append(pkt) if len(temp_new_requests): new_points_of_interest.append( (temp_new_requests[-1].memoryAddress, upward)) new_requests += temp_new_requests self.points_of_interest[state] = list() if len(new_requests): self._state_completed[state] = False self._request_iterators[state] = new_requests self.points_of_interest[state] = new_points_of_interest log_interactive.info("[i] Created %d pkts for sequential probing" % len(new_requests))
def _get_initial_requests(self, **kwargs): # type: (Any) -> Iterable[Packet] return [GMLAN(service=0x3E)]
def _get_initial_requests(self, **kwargs): # type: (Any) -> Iterable[Packet] scan_range = kwargs.pop("scan_range", range(0x10000)) return (GMLAN() / GMLAN_RDBPI(identifiers=[x]) for x in scan_range)
def _get_initial_requests(self, **kwargs): # type: (Any) -> Iterable[Packet] return [GMLAN() / GMLAN_RD(memorySize=0x10)]
def _get_initial_requests(self, **kwargs): # type: (Any) -> Iterable[Packet] scan_range = kwargs.pop("scan_range", range(1, 10, 2)) return (GMLAN() / GMLAN_SA(subfunction=x) for x in scan_range)