def parseBitmapCapability(self, data: bytes) -> BitmapCapability: """ https://msdn.microsoft.com/en-us/library/cc240554.aspx :param data: Raw data starting after lengthCapability """ stream = BytesIO(data) preferredBitsPerPixel = Uint16LE.unpack(stream.read(2)) receive1bitPerPixel = Uint16LE.unpack(stream.read(2)) receive4bitPerPixel = Uint16LE.unpack(stream.read(2)) receive8bitPerPixel = Uint16LE.unpack(stream.read(2)) desktopWidth = Uint16LE.unpack(stream.read(2)) desktopHeight = Uint16LE.unpack(stream.read(2)) stream.read(2) # pad2octets desktopResizeFlag = Uint16LE.unpack(stream.read(2)) bitmapCompressionFlag = Uint16LE.unpack(stream.read(2)) highColorFlags = Uint8.unpack(stream.read(1)) drawingFlags = Uint8.unpack(stream.read(1)) multipleRectangleSupport = Uint16LE.unpack(stream.read(2)) # ignoring pad2octetsB capability = BitmapCapability(preferredBitsPerPixel, receive1bitPerPixel, receive4bitPerPixel, receive8bitPerPixel, desktopWidth, desktopHeight, desktopResizeFlag, bitmapCompressionFlag, highColorFlags, drawingFlags, multipleRectangleSupport) capability.rawData = data return capability
def sendMessage(self, data: bytes, messageType: PlayerMessageType, timeStamp: int): stream = BytesIO() Uint8.pack(messageType, stream) Uint64LE.pack(timeStamp, stream) stream.write(data) self.previous.send(stream.getvalue())
def writeMouseEvent(self, event): rawData = BytesIO() Uint8.pack(event.rawHeaderByte, rawData) Uint16LE.pack(event.pointerFlags, rawData) Uint16LE.pack(event.mouseX, rawData) Uint16LE.pack(event.mouseY, rawData) return rawData.getvalue()
def parse(self, data: bytes) -> FastPathOutputEvent: """ Parse TS_FP_UPDATE. https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/a1c4caa8-00ed-45bb-a06e-5177473766d3 """ stream = BytesIO(data) header = Uint8.unpack(stream) # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/a1c4caa8-00ed-45bb-a06e-5177473766d3 updateCode = header & 0xf # fragmentation = (header & 0b00110000) >> 4 compressionFlags = Uint8.unpack(stream) if self.isCompressed( header) else None size = Uint16LE.unpack(stream) # Dispatch to the appropriate sub-parser. if updateCode == FastPathOutputType.FASTPATH_UPDATETYPE_BITMAP: return self.parseBitmapEventRaw(stream, header, compressionFlags, size) elif updateCode == FastPathOutputType.FASTPATH_UPDATETYPE_ORDERS: return self.parseOrdersEvent(stream, header, compressionFlags, size) read = stream.read(size) return FastPathOutputEvent(header, compressionFlags, read)
def parseSecondaryDrawingOrder(self, orderData): stream = BytesIO(orderData) controlFlags = Uint8.unpack(stream.read(1)) orderLength = Uint16LE.unpack(stream.read(2)) extraFlags = Uint16LE.unpack(stream.read(2)) orderType = Uint8.unpack(stream.read(1)) return SecondaryDrawingOrder(controlFlags, orderLength, extraFlags, orderType)
def writeFileDescription(self, description: PlayerFileDescription, stream: BytesIO): path = description.path.encode() Uint32LE.pack(len(path), stream) stream.write(path) Uint8.pack(int(description.isDirectory), stream)
def parseLength(self, stream: BytesIO) -> int: length = Uint8.unpack(stream) if length & 0x80 != 0: length = ((length & 0x7f) << 8) | Uint8.unpack(stream) return length
def parse(self, data: bytes) -> X224PDU: """ Read the byte stream and return a corresponding X224PDU """ length = Uint8.unpack(data[0]) header = Uint8.unpack(data[1]) >> 4 if header in list(X224PDUType): header = X224PDUType(header) if length < 2: raise ParsingError( "Invalid X224 length indicator: indicator = %d, expected at least 2 bytes" % length) if len(data) < length: raise ParsingError( "Invalid X224 length indicator: indicator = %d, length = %d" % (length, len(data))) if header not in self.parsers: raise UnknownPDUTypeError( "Trying to parse unknown X224 PDU type: %s" % (header if header in X224PDUType else hex(header)), header) return self.parsers[header](data, length)
def writeData(self, stream: BytesIO, pdu: X224DataPDU): """ Write a Data PDU onto the provided stream """ header = (pdu.header << 4) | int(pdu.roa) stream.write(Uint8.pack(header)) stream.write(Uint8.pack(int(pdu.eot) << 7))
def writeError(self, stream: BytesIO, pdu: X224ErrorPDU): """ Write an error PDU onto the provided stream """ stream.write(Uint8.pack(pdu.header)) stream.write(Uint16LE.pack(pdu.destination)) stream.write(Uint8.pack(pdu.cause))
def writeFileBothDirectoryInformation(self, information: List[FileBothDirectoryInformation], stream: BytesIO): dataList: [bytes] = [] for info in information: substream = BytesIO() fileName = info.fileName.encode("utf-16le") shortName = info.shortName.encode("utf-16le") Uint32LE.pack(info.fileIndex, substream) Uint64LE.pack(info.creationTime, substream) Uint64LE.pack(info.lastAccessTime, substream) Uint64LE.pack(info.lastWriteTime, substream) Uint64LE.pack(info.lastChangeTime, substream) Uint64LE.pack(info.endOfFilePosition, substream) Uint64LE.pack(info.allocationSize, substream) Uint32LE.pack(info.fileAttributes, substream) Uint32LE.pack(len(fileName), substream) Uint32LE.pack(info.eaSize, substream) Uint8.pack(len(shortName), substream) # stream.write(b"\x00") # reserved substream.write(shortName.ljust(24, b"\x00")[: 24]) substream.write(fileName) dataList.append(substream.getvalue()) self.writeFileInformationList(dataList, stream)
def parse(self, data: bytes) -> NegotiationRequestPDU: """ Parse a negotiation request. :param data: the request data. """ cookie = None if b"\r\n" in data: cookie = data[: data.index(b"\r\n")] data = data[data.index(b"\r\n") + 2 :] stream = BytesIO(data) if len(data) >= 8: type = Uint8.unpack(stream) requestFlags = Uint8.unpack(stream) requestLength = Uint16LE.unpack(stream) requestedProtocols = Uint32LE.unpack(stream) correlationFlags = None correlationID = None if requestFlags & NegotiationRequestFlags.CORRELATION_INFO_PRESENT != 0 and len(data) >= 36: type = Uint8.unpack(stream) correlationFlags = Uint8.unpack(stream) correlationLength = Uint16LE.unpack(stream) correlationID = stream.read(16) stream.read(16) return NegotiationRequestPDU(cookie, requestFlags, requestedProtocols, correlationFlags, correlationID) else: return NegotiationRequestPDU(cookie, None, None, None, None)
def writeMouseEvent(self, event: FastPathMouseEvent) -> bytes: stream = BytesIO() Uint8.pack(event.rawHeaderByte, stream) Uint16LE.pack(event.pointerFlags, stream) Uint16LE.pack(event.mouseX, stream) Uint16LE.pack(event.mouseY, stream) return stream.getvalue()
def parse(self, data: bytes) -> FastPathOutputEvent: stream = BytesIO(data) header = Uint8.unpack(stream) compressionFlags = None if self.isCompressed(header): compressionFlags = Uint8.unpack(stream) size = Uint16LE.unpack(stream) eventType = header & 0xf fragmentation = header & 0b00110000 != 0 if fragmentation: log.debug( "Fragmentation is present in output fastpath event packets." " Not parsing it and saving to FastPathOutputUpdateEvent.") return FastPathOutputEvent(header, compressionFlags, payload=stream.read(size)) if eventType == FastPathOutputType.FASTPATH_UPDATETYPE_BITMAP: return self.parseBitmapEventRaw(stream, header, compressionFlags, size) elif eventType == FastPathOutputType.FASTPATH_UPDATETYPE_ORDERS: return self.parseOrdersEvent(stream, header, compressionFlags, size) read = stream.read(size) return FastPathOutputEvent(header, compressionFlags, read)
def write(self, pdu: MCSPDU) -> bytes: """ Encode an MCS PDU into raw bytes :param pdu: the MCSPDU to encode :return: The raw bytes to send """ if pdu.header not in self.writers: raise UnknownPDUTypeError( "Trying to write unknown MCS PDU type %s" % pdu.header, pdu.header) stream = BytesIO() if pdu.header in [ MCSPDUType.CONNECT_INITIAL, MCSPDUType.CONNECT_RESPONSE ]: stream.write( Uint8.pack(ber.Class.BER_CLASS_APPL | ber.PC.BER_CONSTRUCT | ber.Tag.BER_TAG_MASK)) stream.write(Uint8.pack(pdu.header)) else: stream.write( Uint8.pack((pdu.header << 2) | self.headerOptions[pdu.header])) self.writers[pdu.header](stream, pdu) return stream.getvalue()
def writeSuppressOutput(self, stream: BytesIO, pdu): Uint8.pack(int(pdu.allowDisplayUpdates), stream) stream.write(b"\x00" * 3) Uint16LE.pack(pdu.left, stream) Uint16LE.pack(pdu.top, stream) Uint16LE.pack(pdu.right, stream) Uint16LE.pack(pdu.bottom, stream)
def writeConnectionPDU(self, stream: BytesIO, header: X224PDUType, destination: int, source: int, options: int): """ Write a connection PDU (connectionRequest/connectionConfirm/disconnectRequest) in the provided byte stream. """ stream.write(Uint8.pack(header)) stream.write(Uint16BE.pack(destination)) stream.write(Uint16BE.pack(source)) stream.write(Uint8.pack(options))
def writeBody(self, stream: BytesIO, pdu: FastPathPDU): bodyStream = BytesIO() SignedFastPathParser.writeBody(self, bodyStream, pdu) body = bodyStream.getvalue() Uint16LE.pack(0x10, stream) Uint8.pack(FIPSVersion.TSFIPS_VERSION1, stream) Uint8.pack(self.crypter.getPadLength(self.eventData), stream) stream.write(body)
def parseShareDataHeader(self, stream: BytesIO, controlHeader: ShareControlHeader): shareID = Uint32LE.unpack(stream) stream.read(1) streamID = Uint8.unpack(stream) uncompressedLength = Uint16LE.unpack(stream) pduSubtype = Uint8.unpack(stream) compressedType = Uint8.unpack(stream) compressedLength = Uint16LE.unpack(stream) return ShareDataHeader(controlHeader.pduType, controlHeader.version, controlHeader.source, shareID, streamID, uncompressedLength, SlowPathDataType(pduSubtype), compressedType, compressedLength)
def writeHeader(self, stream: BytesIO, pdu: FastPathPDU): header = (pdu.header & 0xc0) | self.getHeaderFlags() eventCount = len(pdu.events) if eventCount <= 15 and self.mode == ParserMode.CLIENT: header |= eventCount << 2 Uint8.pack(header, stream) self.writeLength(stream, pdu)
def write(self, pdu: DynamicChannelPDU) -> bytes: stream = BytesIO() header = pdu.cbid header |= pdu.sp << 2 header |= pdu.cmd << 4 Uint8.pack(header, stream) if isinstance(pdu, CreateResponsePDU): self.writeChannelId(stream, pdu.cbid, pdu.channelId) Uint32LE.pack(pdu.creationStatus, stream) else: raise NotImplementedError() return stream.getvalue()
def parse(self, data: bytes) -> FastPathPDU: stream = BytesIO(data) header = Uint8.unpack(stream) eventCount = self.parseEventCount(header) pduLength = self.parseLength(stream) if eventCount == 0: eventCount = Uint8.unpack(stream) data = stream.read(pduLength - stream.tell()) events = self.parseEvents(data) return FastPathPDU(header, events)
def writeUnicodeEvent(self, event: FastPathUnicodeEvent): stream = BytesIO() Uint8.pack( int(event.released) | (FastPathInputType.FASTPATH_INPUT_EVENT_UNICODE << 5), stream) if isinstance(event.text, bytes): stream.write(event.text[:2].ljust(2, b"\x00")) elif isinstance(event.text, str): stream.write(event.text[:1].ljust(1, "\x00").encode("utf-16le")) return stream.getvalue()
def writeShareDataHeader(self, stream: BytesIO, header, dataLength): substream = BytesIO() substream.write(Uint32LE.pack(header.shareID)) substream.write(b"\x00") substream.write(Uint8.pack(header.streamID)) substream.write(Uint16LE.pack(header.uncompressedLength)) substream.write(Uint8.pack(header.subtype)) substream.write(Uint8.pack(header.compressedType)) substream.write(Uint16LE.pack(header.compressedLength)) substream = substream.getvalue() self.writeShareControlHeader(stream, header, dataLength + len(substream)) stream.write(substream)
def write(self, pdu): """ Write a negotiation request. :param pdu: the request PDU. :type pdu: NegotiationRequestPDU :return: str """ stream = BytesIO() if pdu.cookie is not None: stream.write(pdu.cookie + b"\r\n") if pdu.flags is not None and pdu.requestedProtocols is not None: Uint8.pack(NegotiationType.TYPE_RDP_NEG_REQ, stream) Uint8.pack(pdu.flags, stream) Uint16LE.pack(8, stream) Uint32LE.pack(pdu.requestedProtocols, stream) if pdu.correlationFlags is not None and pdu.correlationID is not None: Uint8.pack(NegotiationType.TYPE_RDP_CORRELATION_INFO, stream) Uint8.pack(pdu.correlationFlags, stream) Uint16LE.pack(36, stream) stream.write(pdu.correlationID) stream.write(b"\x00" * 16) return stream.getvalue()
def parseLengthWithSocket(self, socket): """ Same as parseLength, but with a network socket. :type socket: socket.socket """ data = socket.recv(1) length = Uint8.unpack(data) if length & 0x80 != 0: data2 = socket.recv(1) data += data2 length = ((length & 0x7f) << 8) | Uint8.unpack(data2) return data, length
def writeDirectoryControlRequest(self, pdu: Union[DeviceIORequestPDU, DeviceQueryDirectoryRequestPDU], stream: BytesIO): self.minorFunctionsForParsingResponse[pdu.completionID] = pdu.minorFunction if pdu.minorFunction == MinorFunction.IRP_MN_NOTIFY_CHANGE_DIRECTORY: stream.write(pdu.payload) else: self.informationClassForParsingResponse[pdu.completionID] = pdu.informationClass path = (pdu.path + "\x00").encode("utf-16le") Uint32LE.pack(pdu.informationClass, stream) Uint8.pack(pdu.initialQuery, stream) Uint32LE.pack(len(path), stream) stream.write(b"\x00" * 23) stream.write(path)
def parse(self, data: bytes) -> TPKTPDU: """ Read the byte stream and return a TPKTPDU """ _version = Uint8.unpack(data[0:1]) _padding = Uint8.unpack(data[1:2]) length = Uint16BE.unpack(data[2:4]) payload = data[4:length] if len(payload) != length - 4: raise ParsingError("Payload is too short for TPKT length field") return TPKTPDU(payload)
def parse(self, data: bytes) -> NegotiationResponsePDU: """ Parse a negotiation response. :param data: the response data. """ stream = BytesIO(data) if len(data) == 8: type = Uint8.unpack(stream) flags = Uint8.unpack(stream) length = Uint16LE.unpack(stream) selectedProtocols = Uint32LE.unpack(stream) return NegotiationResponsePDU(type, flags, selectedProtocols) else: return NegotiationResponsePDU(None, None, None)
def write(self, pdu): """ Write a negotiation response. :param pdu: the response PDU. :type pdu: NegotiationResponsePDU :return: str """ stream = BytesIO() if pdu.flags is not None and pdu.selectedProtocols is not None: Uint8.pack(NegotiationType.TYPE_RDP_NEG_RSP, stream) Uint8.pack(pdu.flags, stream) Uint16LE.pack(8, stream) Uint32LE.pack(pdu.selectedProtocols, stream) return stream.getvalue()