def _gen_high_data(self, msg): # type: (str) -> None ObjectPipe.send(self, (msg, True, False))
def _exhaust(self): # type: () -> None ObjectPipe.send(self, (None, None, True))
def _exhaust(self): ObjectPipe.send(self, (None, None, True))
def _gen_high_data(self, msg): ObjectPipe.send(self, (msg, True, False))
def _gen_data(self, msg): ObjectPipe.send(self, (msg, False, False))
class ISOTPSocketImplementation: """ Implementation of an ISOTP "state machine". Most of the ISOTP logic was taken from https://github.com/hartkopp/can-isotp/blob/master/net/can/isotp.c This class is separated from ISOTPSoftSocket to make sure the background thread can't hold a reference to ISOTPSoftSocket, allowing it to be collected by the GC. :param can_socket: a CANSocket instance, preferably filtering only can frames with identifier equal to did :param src_id: the CAN identifier of the sent CAN frames :param dst_id: the CAN identifier of the received CAN frames :param padding: If True, pads sending packets with 0x00 which not count to the payload. Does not affect receiving packets. :param extended_addr: Extended Address byte to be added at the beginning of every CAN frame _sent_ by this object. Can be None in order to disable extended addressing on sent frames. :param extended_rx_addr: Extended Address byte expected to be found at the beginning of every CAN frame _received_ by this object. Can be None in order to disable extended addressing on received frames. :param rx_block_size: Block Size byte to be included in every Control Flow Frame sent by this object. The default value of 0 means that all the data will be received in a single block. :param stmin: Time Minimum Separation byte to be included in every Control Flow Frame sent by this object. The default value of 0 indicates that the peer will not wait any time between sending frames. :param listen_only: Disables send of flow control frames """ def __init__(self, can_socket, # type: "CANSocket" src_id, # type: int dst_id, # type: int padding=False, # type: bool extended_addr=None, # type: Optional[int] extended_rx_addr=None, # type: Optional[int] rx_block_size=0, # type: int stmin=0, # type: int listen_only=False # type: bool ): # type: (...) -> None self.can_socket = can_socket self.dst_id = dst_id self.src_id = src_id self.padding = padding self.fc_timeout = 1 self.cf_timeout = 1 self.filter_warning_emitted = False self.extended_rx_addr = extended_rx_addr self.ea_hdr = b"" if extended_addr is not None: self.ea_hdr = struct.pack("B", extended_addr) self.listen_only = listen_only self.rxfc_bs = rx_block_size self.rxfc_stmin = stmin self.rx_queue = ObjectPipe() self.rx_len = -1 self.rx_buf = None # type: Optional[bytes] self.rx_sn = 0 self.rx_bs = 0 self.rx_idx = 0 self.rx_ts = 0.0 # type: Union[float, EDecimal] self.rx_state = ISOTP_IDLE self.txfc_bs = 0 self.txfc_stmin = 0 self.tx_gap = 0 self.tx_buf = None # type: Optional[bytes] self.tx_sn = 0 self.tx_bs = 0 self.tx_idx = 0 self.rx_ll_dl = 0 self.tx_state = ISOTP_IDLE self.tx_timeout_handle = None # type: Optional[TimeoutScheduler.Handle] # noqa: E501 self.rx_timeout_handle = None # type: Optional[TimeoutScheduler.Handle] # noqa: E501 self.rx_thread_started = Event() self.rx_thread = AsyncSniffer( store=False, opened_socket=can_socket, prn=self.on_can_recv, started_callback=self.rx_thread_started.set) self.tx_mutex = Lock() self.rx_mutex = Lock() self.send_mutex = Lock() self.tx_done = Event() self.tx_exception = None # type: Optional[str] self.tx_callbacks = [] # type: List[Callable[[], None]] self.rx_callbacks = [] # type: List[Callable[[bytes], None]] self.rx_thread.start() self.rx_thread_started.wait(5) def __del__(self): # type: () -> None self.close() def can_send(self, load): # type: (bytes) -> None if self.padding: load += b"\xCC" * (CAN_MAX_DLEN - len(load)) if self.src_id is None or self.src_id <= 0x7ff: self.can_socket.send(CAN(identifier=self.src_id, data=load)) else: self.can_socket.send(CAN(identifier=self.src_id, flags="extended", data=load)) def on_can_recv(self, p): # type: (Packet) -> None if not isinstance(p, CAN): raise Scapy_Exception("argument is not a CAN frame") if p.identifier != self.dst_id: if not self.filter_warning_emitted and conf.verb >= 2: warning("You should put a filter for identifier=%x on your " "CAN socket", self.dst_id) self.filter_warning_emitted = True else: self.on_recv(p) def close(self): # type: () -> None if self.rx_thread.thread and self.rx_thread.thread.is_alive(): self.rx_thread.stop(True) def _rx_timer_handler(self): # type: () -> None """Method called every time the rx_timer times out, due to the peer not sending a consecutive frame within the expected time window""" with self.rx_mutex: if self.rx_state == ISOTP_WAIT_DATA: # we did not get new data frames in time. # reset rx state self.rx_state = ISOTP_IDLE if conf.verb > 2: warning("RX state was reset due to timeout") def _tx_timer_handler(self): # type: () -> None """Method called every time the tx_timer times out, which can happen in two situations: either a Flow Control frame was not received in time, or the Separation Time Min is expired and a new frame must be sent.""" with self.tx_mutex: if (self.tx_state == ISOTP_WAIT_FC or self.tx_state == ISOTP_WAIT_FIRST_FC): # we did not get any flow control frame in time # reset tx state self.tx_state = ISOTP_IDLE self.tx_exception = "TX state was reset due to timeout" self.tx_done.set() raise Scapy_Exception(self.tx_exception) elif self.tx_state == ISOTP_SENDING: # push out the next segmented pdu src_off = len(self.ea_hdr) max_bytes = 7 - src_off if self.tx_buf is None: self.tx_exception = "TX buffer is not filled" raise Scapy_Exception(self.tx_exception) while 1: load = self.ea_hdr load += struct.pack("B", N_PCI_CF + self.tx_sn) load += self.tx_buf[self.tx_idx:self.tx_idx + max_bytes] self.can_send(load) self.tx_sn = (self.tx_sn + 1) % 16 self.tx_bs += 1 self.tx_idx += max_bytes if len(self.tx_buf) <= self.tx_idx: # we are done self.tx_state = ISOTP_IDLE self.tx_done.set() for cb in self.tx_callbacks: cb() return if self.txfc_bs != 0 and self.tx_bs >= self.txfc_bs: # stop and wait for FC self.tx_state = ISOTP_WAIT_FC self.tx_timeout_handle = TimeoutScheduler.schedule( self.fc_timeout, self._tx_timer_handler) return if self.tx_gap == 0: continue else: # stop and wait for tx gap self.tx_timeout_handle = TimeoutScheduler.schedule( self.tx_gap, self._tx_timer_handler) return def on_recv(self, cf): # type: (Packet) -> None """Function that must be called every time a CAN frame is received, to advance the state machine.""" data = bytes(cf.data) if len(data) < 2: return ae = 0 if self.extended_rx_addr is not None: ae = 1 if len(data) < 3: return if six.indexbytes(data, 0) != self.extended_rx_addr: return n_pci = six.indexbytes(data, ae) & 0xf0 if n_pci == N_PCI_FC: with self.tx_mutex: self._recv_fc(data[ae:]) elif n_pci == N_PCI_SF: with self.rx_mutex: self._recv_sf(data[ae:], cf.time) elif n_pci == N_PCI_FF: with self.rx_mutex: self._recv_ff(data[ae:], cf.time) elif n_pci == N_PCI_CF: with self.rx_mutex: self._recv_cf(data[ae:]) def _recv_fc(self, data): # type: (bytes) -> None """Process a received 'Flow Control' frame""" if (self.tx_state != ISOTP_WAIT_FC and self.tx_state != ISOTP_WAIT_FIRST_FC): return if self.tx_timeout_handle is not None: self.tx_timeout_handle.cancel() self.tx_timeout_handle = None if len(data) < 3: self.tx_state = ISOTP_IDLE self.tx_exception = "CF frame discarded because it was too short" self.tx_done.set() raise Scapy_Exception(self.tx_exception) # get communication parameters only from the first FC frame if self.tx_state == ISOTP_WAIT_FIRST_FC: self.txfc_bs = six.indexbytes(data, 1) self.txfc_stmin = six.indexbytes(data, 2) if ((self.txfc_stmin > 0x7F) and ((self.txfc_stmin < 0xF1) or (self.txfc_stmin > 0xF9))): self.txfc_stmin = 0x7F if six.indexbytes(data, 2) <= 127: tx_gap = six.indexbytes(data, 2) / 1000.0 elif 0xf1 <= six.indexbytes(data, 2) <= 0xf9: tx_gap = (six.indexbytes(data, 2) & 0x0f) / 10000.0 else: tx_gap = 0 self.tx_gap = tx_gap self.tx_state = ISOTP_WAIT_FC isotp_fc = six.indexbytes(data, 0) & 0x0f if isotp_fc == ISOTP_FC_CTS: self.tx_bs = 0 self.tx_state = ISOTP_SENDING # start cyclic timer for sending CF frame self.tx_timeout_handle = TimeoutScheduler.schedule( self.tx_gap, self._tx_timer_handler) elif isotp_fc == ISOTP_FC_WT: # start timer to wait for next FC frame self.tx_state = ISOTP_WAIT_FC self.tx_timeout_handle = TimeoutScheduler.schedule( self.fc_timeout, self._tx_timer_handler) elif isotp_fc == ISOTP_FC_OVFLW: # overflow in receiver side self.tx_state = ISOTP_IDLE self.tx_exception = "Overflow happened at the receiver side" self.tx_done.set() raise Scapy_Exception(self.tx_exception) else: self.tx_state = ISOTP_IDLE self.tx_exception = "Unknown FC frame type" self.tx_done.set() raise Scapy_Exception(self.tx_exception) def _recv_sf(self, data, ts): # type: (bytes, Union[float, EDecimal]) -> None """Process a received 'Single Frame' frame""" if self.rx_timeout_handle is not None: self.rx_timeout_handle.cancel() self.rx_timeout_handle = None if self.rx_state != ISOTP_IDLE: if conf.verb > 2: warning("RX state was reset because single frame was received") self.rx_state = ISOTP_IDLE length = six.indexbytes(data, 0) & 0xf if len(data) - 1 < length: return msg = data[1:1 + length] self.rx_queue.send((msg, ts)) for cb in self.rx_callbacks: cb(msg) def _recv_ff(self, data, ts): # type: (bytes, Union[float, EDecimal]) -> None """Process a received 'First Frame' frame""" if self.rx_timeout_handle is not None: self.rx_timeout_handle.cancel() self.rx_timeout_handle = None if self.rx_state != ISOTP_IDLE: if conf.verb > 2: warning("RX state was reset because first frame was received") self.rx_state = ISOTP_IDLE if len(data) < 7: return self.rx_ll_dl = len(data) # get the FF_DL self.rx_len = (six.indexbytes(data, 0) & 0x0f) * 256 + six.indexbytes( data, 1) ff_pci_sz = 2 # Check for FF_DL escape sequence supporting 32 bit PDU length if self.rx_len == 0: # FF_DL = 0 => get real length from next 4 bytes self.rx_len = six.indexbytes(data, 2) << 24 self.rx_len += six.indexbytes(data, 3) << 16 self.rx_len += six.indexbytes(data, 4) << 8 self.rx_len += six.indexbytes(data, 5) ff_pci_sz = 6 # copy the first received data bytes data_bytes = data[ff_pci_sz:] self.rx_idx = len(data_bytes) self.rx_buf = data_bytes self.rx_ts = ts # initial setup for this pdu reception self.rx_sn = 1 self.rx_state = ISOTP_WAIT_DATA # no creation of flow control frames if not self.listen_only: # send our first FC frame load = self.ea_hdr load += struct.pack("BBB", N_PCI_FC, self.rxfc_bs, self.rxfc_stmin) self.can_send(load) # wait for a CF self.rx_bs = 0 self.rx_timeout_handle = TimeoutScheduler.schedule( self.cf_timeout, self._rx_timer_handler) def _recv_cf(self, data): # type: (bytes) -> None """Process a received 'Consecutive Frame' frame""" if self.rx_state != ISOTP_WAIT_DATA: return if self.rx_timeout_handle is not None: self.rx_timeout_handle.cancel() self.rx_timeout_handle = None # CFs are never longer than the FF if len(data) > self.rx_ll_dl: return # CFs have usually the LL_DL length if len(data) < self.rx_ll_dl: # this is only allowed for the last CF if self.rx_len - self.rx_idx > self.rx_ll_dl: if conf.verb > 2: warning("Received a CF with insufficient length") return if six.indexbytes(data, 0) & 0x0f != self.rx_sn: # Wrong sequence number if conf.verb > 2: warning("RX state was reset because wrong sequence number was " "received") self.rx_state = ISOTP_IDLE return if self.rx_buf is None: raise Scapy_Exception("rx_buf not filled with data!") self.rx_sn = (self.rx_sn + 1) % 16 self.rx_buf += data[1:] self.rx_idx = len(self.rx_buf) if self.rx_idx >= self.rx_len: # we are done self.rx_buf = self.rx_buf[0:self.rx_len] self.rx_state = ISOTP_IDLE self.rx_queue.send((self.rx_buf, self.rx_ts)) for cb in self.rx_callbacks: cb(self.rx_buf) self.rx_buf = None return # perform blocksize handling, if enabled if self.rxfc_bs != 0: self.rx_bs += 1 # check if we reached the end of the block if self.rx_bs >= self.rxfc_bs and not self.listen_only: # send our FC frame load = self.ea_hdr load += struct.pack("BBB", N_PCI_FC, self.rxfc_bs, self.rxfc_stmin) self.can_send(load) # wait for another CF self.rx_timeout_handle = TimeoutScheduler.schedule( self.cf_timeout, self._rx_timer_handler) def begin_send(self, x): # type: (bytes) -> None """Begins sending an ISOTP message. This method does not block.""" with self.tx_mutex: if self.tx_state != ISOTP_IDLE: raise Scapy_Exception("Socket is already sending, retry later") self.tx_done.clear() self.tx_exception = None self.tx_state = ISOTP_SENDING length = len(x) if length > ISOTP_MAX_DLEN_2015: raise Scapy_Exception("Too much data for ISOTP message") if len(self.ea_hdr) + length <= 7: # send a single frame data = self.ea_hdr data += struct.pack("B", length) data += x self.tx_state = ISOTP_IDLE self.can_send(data) self.tx_done.set() for cb in self.tx_callbacks: cb() return # send the first frame data = self.ea_hdr if length > ISOTP_MAX_DLEN: data += struct.pack(">HI", 0x1000, length) else: data += struct.pack(">H", 0x1000 | length) load = x[0:8 - len(data)] data += load self.can_send(data) self.tx_buf = x self.tx_sn = 1 self.tx_bs = 0 self.tx_idx = len(load) self.tx_state = ISOTP_WAIT_FIRST_FC self.tx_timeout_handle = TimeoutScheduler.schedule( self.fc_timeout, self._tx_timer_handler) def send(self, p): # type: (bytes) -> None """Send an ISOTP frame and block until the message is sent or an error happens.""" with self.send_mutex: self.begin_send(p) # Wait until the tx callback is called send_done = self.tx_done.wait(30) if self.tx_exception is not None: raise Scapy_Exception(self.tx_exception) if not send_done: raise Scapy_Exception("ISOTP send not completed in 30s") return def recv(self, timeout=None): # type: (Optional[int]) -> Optional[Tuple[bytes, Union[float, EDecimal]]] # noqa: E501 """Receive an ISOTP frame, blocking if none is available in the buffer for at most 'timeout' seconds.""" try: return self.rx_queue.recv() except IndexError: return None
class _NTLM_Automaton(Automaton): def __init__(self, sock, **kwargs): # type: (StreamSocket, Any) -> None self.token_pipe = ObjectPipe() self.values = {} for key, dflt in [("DROP_MIC_v1", False), ("DROP_MIC_v2", False)]: setattr(self, key, kwargs.pop(key, dflt)) self.DROP_MIC = self.DROP_MIC_v1 or self.DROP_MIC_v2 super(_NTLM_Automaton, self).__init__(recvsock=lambda **kwargs: sock, ll=lambda **kwargs: sock, **kwargs) def _get_token(self, token): from scapy.layers.gssapi import (GSSAPI_BLOB, SPNEGO_negToken, SPNEGO_Token) negResult = None MIC = None if not token: return None, negResult, MIC if isinstance(token, bytes): ntlm = NTLM_Header(token) elif isinstance(token, conf.raw_layer): ntlm = NTLM_Header(token.load) else: if isinstance(token, GSSAPI_BLOB): token = token.innerContextToken if isinstance(token, SPNEGO_negToken): token = token.token if hasattr(token, "mechListMIC") and token.mechListMIC: MIC = token.mechListMIC.value if hasattr(token, "negResult"): negResult = token.negResult try: ntlm = token.mechToken except AttributeError: ntlm = token.responseToken if isinstance(ntlm, SPNEGO_Token): ntlm = ntlm.value if isinstance(ntlm, ASN1_STRING): ntlm = NTLM_Header(ntlm.val) if isinstance(ntlm, conf.raw_layer): ntlm = NTLM_Header(ntlm.load) if self.DROP_MIC_v1 or self.DROP_MIC_v2: if isinstance(ntlm, NTLM_AUTHENTICATE): ntlm.MIC = b"\0" * 16 ntlm.NtChallengeResponseLen = None ntlm.NtChallengeResponseMaxLen = None ntlm.EncryptedRandomSessionKeyBufferOffset = None if self.DROP_MIC_v2: ChallengeResponse = next(v[1] for v in ntlm.Payload if v[0] == 'NtChallengeResponse') i = next(i for i, k in enumerate(ChallengeResponse.AvPairs) if k.AvId == 0x0006) ChallengeResponse.AvPairs.insert( i + 1, AV_PAIR(AvId="MsvAvFlags", Value=0)) return ntlm, negResult, MIC def received_ntlm_token(self, ntlm): self.token_pipe.send(ntlm) def get(self, attr, default=None): if default is not None: return self.values.get(attr, default) return self.values[attr] def end(self): self.listen_sock.close() self.stop()