def parseGeneralCapability(self, version: int, payload: bytes) -> DeviceRedirectionGeneralCapability: stream = BytesIO(payload) osType = Uint32LE.unpack(stream) osVersion = Uint32LE.unpack(stream) protocolMajorVersion = Uint16LE.unpack(stream) protocolMinorVersion = Uint16LE.unpack(stream) ioCode1 = Uint32LE.unpack(stream) ioCode2 = Uint32LE.unpack(stream) extendedPDU = Uint32LE.unpack(stream) extraFlags1 = Uint32LE.unpack(stream) extraFlags2 = Uint32LE.unpack(stream) specialTypeDeviceCap = None if version == GeneralCapabilityVersion.GENERAL_CAPABILITY_VERSION_02: specialTypeDeviceCap = Uint32LE.unpack(stream) return DeviceRedirectionGeneralCapability( version, osType, osVersion, protocolMajorVersion, protocolMinorVersion, ioCode1, ioCode2, extendedPDU, extraFlags1, extraFlags2, specialTypeDeviceCap )
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 getEventLength(self, event: FastPathOutputUpdateEvent): if isinstance(event, bytes): header = Uint8.unpack(event[0]) if self.isCompressed(header): return Uint16LE.unpack(event[2: 4]) + 4 else: return Uint16LE.unpack(event[1: 3]) + 3 size = 3 if self.isCompressed(event.header): size += 1 if isinstance(event, FastPathOrdersEvent): size += 2 + len(event.orderData) elif isinstance(event, FastPathBitmapEvent): size += len(event.payload) elif isinstance(event, FastPathOutputUpdateEvent): length = len(event.payload) + 3 if event.compressionFlags is not None: length += 1 return length return size
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 parseSuppressOutput(self, stream: BytesIO, header): allowDisplayUpdates = Uint8.unpack(stream) stream.read(3) left = Uint16LE.unpack(stream) top = Uint16LE.unpack(stream) right = Uint16LE.unpack(stream) bottom = Uint16LE.unpack(stream) return SuppressOutputPDU(header, allowDisplayUpdates, left, top, right, bottom)
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 readChannelId(self, stream: BytesIO, cbid: int): if cbid == CbId.ONE_BYTE: return Uint8.unpack(stream) elif cbid == CbId.TWO_BYTE: return Uint16LE.unpack(stream) elif cbid == CbId.FOUR_BYTES: return Uint16LE.unpack(stream) else: raise ValueError(f"Invalid channel id length: {cbid}")
def doParse(self, data: bytes) -> DeviceRedirectionPDU: stream = BytesIO(data) component = DeviceRedirectionComponent(Uint16LE.unpack(stream)) packetID = DeviceRedirectionPacketID(Uint16LE.unpack(stream)) if component == DeviceRedirectionComponent.RDPDR_CTYP_CORE and packetID in self.parsers.keys(): return self.parsers[packetID](stream) else: return DeviceRedirectionPDU(component, packetID, payload=stream.read())
def parse(s: BytesIO) -> 'StreamBitmapNext': self = StreamBitmapNext() self.flags = Uint8.unpack(s) self.bitmapType = Uint16LE.unpack(s) blockSize = Uint16LE.unpack(s) self.data = s.read(blockSize) return self
def parseLicenseBlob(self, stream): """ Parse the provided byte stream and return the corresponding RDPLicenseBinaryBlob :type stream: BytesIO :return: RDPLicenseBinaryBlob """ type = LicenseBinaryBlobType(Uint16LE.unpack(stream)) length = Uint16LE.unpack(stream) data = stream.read(length) return LicenseBinaryBlob(type, data)
def parseCapability(self, stream: BytesIO) -> DeviceRedirectionCapability: capabilityType = RDPDRCapabilityType(Uint16LE.unpack(stream)) capabilityLength = Uint16LE.unpack(stream) version = Uint32LE.unpack(stream) payload = stream.read(capabilityLength - 8) if capabilityType in self.capabilityParsers: return self.capabilityParsers[capabilityType](version, payload) else: return DeviceRedirectionCapability(capabilityType, version, payload)
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 parseCapabilitySets(self, capabilitySetsRaw, numberCapabilities): stream = BytesIO(capabilitySetsRaw) capabilitySets = {} # Do minimum parsing for every capability for i in range(numberCapabilities): capabilitySetType = Uint16LE.unpack(stream.read(2)) lengthCapability = Uint16LE.unpack(stream.read(2)) capabilityData = stream.read(lengthCapability - 4) capability = Capability(capabilitySetType, capabilityData) capabilitySets[CapabilityType(capabilitySetType)] = capability # Fully parse the General capability set capabilitySets[CapabilityType.CAPSTYPE_GENERAL] = \ self.parseGeneralCapability(capabilitySets[CapabilityType.CAPSTYPE_GENERAL].rawData) # Fully parse the Glyph cache capability set if CapabilityType.CAPSTYPE_GLYPHCACHE in capabilitySets: capabilitySets[CapabilityType.CAPSTYPE_GLYPHCACHE] = \ self.parseGlyphCacheCapability(capabilitySets[CapabilityType.CAPSTYPE_GLYPHCACHE].rawData) # If present, fully parse the offscreen cache capability set if CapabilityType.CAPSTYPE_OFFSCREENCACHE in capabilitySets: capabilitySets[CapabilityType.CAPSTYPE_OFFSCREENCACHE] = \ self.parseOffscreenCacheCapability(capabilitySets[CapabilityType.CAPSTYPE_OFFSCREENCACHE].rawData) # If present, fully parse the surface commands cache capability set if CapabilityType.CAPSETTYPE_SURFACE_COMMANDS in capabilitySets: capabilitySets[CapabilityType.CAPSETTYPE_SURFACE_COMMANDS] = \ self.parseSurfaceCommandsCapability(capabilitySets[CapabilityType.CAPSETTYPE_SURFACE_COMMANDS].rawData) # Fully parse the Bitmap capability set capabilitySets[CapabilityType.CAPSTYPE_BITMAP] = \ self.parseBitmapCapability(capabilitySets[CapabilityType.CAPSTYPE_BITMAP].rawData) # Fully parse the Order capability set capabilitySets[ CapabilityType.CAPSTYPE_ORDER] = self.parseOrderCapability( capabilitySets[CapabilityType.CAPSTYPE_ORDER].rawData) # Fully parse the VirtualChannel capability set if CapabilityType.CAPSTYPE_VIRTUALCHANNEL in capabilitySets: capabilitySets[ CapabilityType. CAPSTYPE_VIRTUALCHANNEL] = self.parseVirtualChannelCapability( capabilitySets[ CapabilityType.CAPSTYPE_VIRTUALCHANNEL].rawData) # Fully parse the Pointer capability set if CapabilityType.CAPSTYPE_POINTER in capabilitySets: capabilitySets[ CapabilityType.CAPSTYPE_POINTER] = self.parsePointerCapability( capabilitySets[CapabilityType.CAPSTYPE_POINTER].rawData) return capabilitySets
def parse(s: BytesIO) -> 'GdiPlusCacheNext': self = GdiPlusCacheNext() self.flags = Uint8.unpack(s) self.cacheType = Uint16LE.unpack(s) self.cacheIdx = Uint16LE.unpack(s) cbSize = Uint16LE.unpack(s) self.data = s.read(cbSize) return self
def parsePointerCapability(self, data: bytes) -> PointerCapability: """ https://msdn.microsoft.com/en-us/library/cc240562.aspx :param data: Raw data starting after lengthCapability """ stream = BytesIO(data) colorPointerFlag = Uint16LE.unpack(stream) colorPointerCacheSize = Uint16LE.unpack(stream) pointerCacheSize = Uint16LE.unpack(stream) return PointerCapability(colorPointerFlag, colorPointerCacheSize, pointerCacheSize)
def parse(s: BytesIO) -> 'GdiPlusCacheEnd': self = GdiPlusCacheEnd() self.flags = Uint8.unpack(s) self.cacheType = Uint16LE.unpack(s) self.cacheIndex = Uint16LE.unpack(s) cbSize = Uint16LE.unpack(s) self.totalSize = Uint32LE.unpack(s) self.data = s.read(cbSize) return self
def parseBitmapEvent( self, fastPathBitmapEvent: FastPathOutputEvent) -> FastPathBitmapEvent: rawBitmapUpdateData = fastPathBitmapEvent.payload stream = BytesIO(rawBitmapUpdateData) Uint16LE.unpack(stream.read(2)) # updateType (unused) bitmapData = self.bitmapParser.parseBitmapUpdateData(stream.read()) return FastPathBitmapEvent(fastPathBitmapEvent.header, fastPathBitmapEvent.compressionFlags, bitmapData, rawBitmapUpdateData)
def parseColorEvent(self, stream): cacheIndex = Uint16LE.unpack(stream) hotSpot = Uint32LE.unpack(stream) width = Uint16LE.unpack(stream) height = Uint16LE.unpack(stream) andMaskLength = Uint16LE.unpack(stream) xorMaskLength = Uint16LE.unpack(stream) xorMask = stream.read(xorMaskLength) andMask = stream.read(andMaskLength) stream.read(1) return PointerColorEvent(cacheIndex, hotSpot, width, height, andMask, xorMask)
def parseDemandActive(self, stream: BytesIO, header): shareID = Uint32LE.unpack(stream) lengthSourceDescriptor = Uint16LE.unpack(stream) lengthCombinedCapabilities = Uint16LE.unpack(stream) sourceDescriptor = stream.read(lengthSourceDescriptor) numberCapabilities = Uint16LE.unpack(stream) pad2Octets = stream.read(2) capabilitySets = stream.read(lengthCombinedCapabilities - 4) sessionID = Uint32LE.unpack(stream) parsedCapabilitySets = self.parseCapabilitySets(capabilitySets, numberCapabilities) return DemandActivePDU(header, shareID, sourceDescriptor, numberCapabilities, capabilitySets, sessionID, parsedCapabilitySets)
def parseSingleCapability(self, stream: BytesIO) -> Tuple[CapabilityType, DeviceRedirectionCapability]: """ https://msdn.microsoft.com/en-us/library/cc241325.aspx """ capabilityType = CapabilityType(Uint16LE.unpack(stream)) capabilityLength = Uint16LE.unpack(stream) version = Uint32LE.unpack(stream) payload = stream.read(capabilityLength - 8) if capabilityType == CapabilityType.CAP_GENERAL_TYPE: return capabilityType, self.parseGeneralCapability(version, payload) else: return capabilityType, DeviceRedirectionCapability(capabilityType, version, payload=payload)
def parse(self, data): stream = BytesIO(data) msgType = Uint16LE.unpack(stream) msgFlags = Uint16LE.unpack(stream) dataLen = Uint32LE.unpack(stream) payload = stream.read(dataLen) if msgType in self.dispatch: clipboardPDU = self.dispatch[msgType](payload, msgFlags) else: clipboardPDU = ClipboardPDU(ClipboardMessageType(msgType), msgFlags, payload) return clipboardPDU
def parseOffscreenCacheCapability(self, data) -> OffscreenBitmapCacheCapability: """ https://msdn.microsoft.com/en-us/library/cc240550.aspx :param data: Raw data starting after lengthCapability """ stream = BytesIO(data) offscreenSupportLevel = Uint32LE.unpack(stream.read(4)) offscreenCacheSize = Uint16LE.unpack(stream.read(2)) offscreenCacheEntries = Uint16LE.unpack(stream.read(2)) capability = OffscreenBitmapCacheCapability(offscreenSupportLevel, offscreenCacheSize, offscreenCacheEntries) capability.rawData = data return capability
def parse(self, data: bytes) -> DeviceRedirectionPDU: stream = BytesIO(data) unpack = Uint16LE.unpack(stream) component = DeviceRedirectionComponent(unpack) packetId = DeviceRedirectionPacketId(Uint16LE.unpack(stream)) if component == DeviceRedirectionComponent.RDPDR_CTYP_PRN: log.warning("Received Printing component packets, which are not handled. Might cause a crash.") if packetId in self.parsers.keys(): return self.parsers[packetId](stream) else: return DeviceRedirectionPDU(component, packetId, payload=stream.read())
def parseConfirmActive(self, stream: BytesIO, header): shareID = Uint32LE.unpack(stream) originatorID = Uint16LE.unpack(stream) lengthSourceDescriptor = Uint16LE.unpack(stream) lengthCombinedCapabilities = Uint16LE.unpack(stream) sourceDescriptor = stream.read(lengthSourceDescriptor) numberCapabilities = Uint16LE.unpack(stream) stream.read(2) capabilitySetsRaw = stream.read(lengthCombinedCapabilities - 4) capabilitySets = self.parseCapabilitySets(capabilitySetsRaw, numberCapabilities) return ConfirmActivePDU(header, shareID, originatorID, sourceDescriptor, numberCapabilities, capabilitySets, capabilitySetsRaw)
def parse(self, data): stream = BytesIO(data) msgType = Uint16LE.unpack(stream) msgFlags = Uint16LE.unpack(stream) dataLen = Uint32LE.unpack(stream) payload = stream.read(dataLen) if msgType == ClipboardMessageType.CB_FORMAT_DATA_RESPONSE: clipboardPDU = self.parseFormatDataResponse(payload, msgFlags) elif msgType == ClipboardMessageType.CB_FORMAT_LIST: clipboardPDU = self.parseFormatList(payload, msgFlags) else: clipboardPDU = ClipboardPDU(ClipboardMessageType(msgType), msgFlags, payload) return clipboardPDU
def parseStructure(self, stream: BytesIO) -> typing.Union[ClientCoreData, ClientNetworkData, ClientSecurityData, ClientClusterData]: header = Uint16LE.unpack(stream) length = Uint16LE.unpack(stream) - 4 data = stream.read(length) if len(data) != length: raise ParsingError("Client Data length field does not match actual size") substream = BytesIO(data) if header not in self.parsers: raise UnknownPDUTypeError("Trying to parse unknown client data structure %s" % header, header) return self.parsers[header](substream)
def parseProprietaryCertificate(self, stream: BytesIO) -> ProprietaryCertificate: signatureAlgorithmID = Uint32LE.unpack(stream) keyAlgorithmID = Uint32LE.unpack(stream) publicKeyType = Uint16LE.unpack(stream) keyLength = Uint16LE.unpack(stream) publicKey = stream.read(keyLength) signatureType = Uint16LE.unpack(stream) signatureLength = Uint16LE.unpack(stream) signature = stream.read(signatureLength - 8) padding = stream.read() publicKey = self.parsePublicKey(publicKey) return ProprietaryCertificate(signatureAlgorithmID, keyAlgorithmID, publicKeyType, publicKey, signatureType, signature, padding)
def parseStructure(self, stream): header = Uint16LE.unpack(stream) length = Uint16LE.unpack(stream) - 4 data = stream.read(length) if len(data) != length: raise ParsingError("Client Data length field does not match actual size") substream = BytesIO(data) if header not in self.parsers: raise UnknownPDUTypeError("Trying to parse unknown client data structure %s" % header, header) return self.parsers[header](substream)
def parseOrderCapability(self, data) -> OrderCapability: """ https://msdn.microsoft.com/en-us/library/cc240556.aspx :param data: Raw data starting after lengthCapability """ stream = BytesIO(data) terminalDescriptor = stream.read(16) stream.read(4) # pad4octetsA desktopSaveXGranularity = Uint16LE.unpack(stream.read(2)) desktopSaveYGranularity = Uint16LE.unpack(stream.read(2)) stream.read(2) # pad2octetsA maximumOrderLevel = Uint16LE.unpack(stream.read(2)) numberFonts = Uint16LE.unpack(stream.read(2)) orderFlags = Uint16LE.unpack(stream.read(2)) orderSupport = stream.read(32) textFlags = Uint16LE.unpack(stream.read(2)) orderSupportExFlags = Uint16LE.unpack(stream.read(2)) stream.read(4) # pad4octetsB desktopSaveSize = Uint32LE.unpack(stream.read(4)) stream.read(4) # pad2octetsC, pad2octetsD textANSICodePage = Uint16LE.unpack(stream.read(2)) # ignoring pad2octetsE capability = OrderCapability( terminalDescriptor, desktopSaveXGranularity, desktopSaveYGranularity, maximumOrderLevel, numberFonts, orderFlags, orderSupport, textFlags, orderSupportExFlags, desktopSaveSize, textANSICodePage) capability.rawData = data return capability
def parseClientCoreData(self, stream: BytesIO) -> ClientCoreData: stream = StrictStream(stream) # 128 bytes minimum (excluding header) version = RDPVersion(Uint32LE.unpack(stream)) desktopWidth = Uint16LE.unpack(stream) desktopHeight = Uint16LE.unpack(stream) colorDepth = ColorDepth(Uint16LE.unpack(stream)) sasSequence = Uint16LE.unpack(stream) keyboardLayout = Uint32LE.unpack(stream) clientBuild = Uint32LE.unpack(stream) clientName = decodeUTF16LE(stream.read(32)) keyboardType = Uint32LE.unpack(stream) keyboardSubType = Uint32LE.unpack(stream) keyboardFunctionKey = Uint32LE.unpack(stream) imeFileName = stream.read(64) core = ClientCoreData(version, desktopWidth, desktopHeight, colorDepth, sasSequence, keyboardLayout, clientBuild, clientName, keyboardType, keyboardSubType, keyboardFunctionKey, imeFileName) # Optional data # The optional fields are read in order. If one of them is not present, then all subsequent fields are also not present. try: core.postBeta2ColorDepth = Uint16LE.unpack(stream) core.clientProductId = Uint16LE.unpack(stream) core.serialNumber = Uint32LE.unpack(stream) # Should match HighColorDepth enum most of the time, but in order to support scanners and we script, we have to loosely accept this one # Anyway, the server will reject it and enforce another one core.highColorDepth = Uint16LE.unpack(stream) core.supportedColorDepths = Uint16LE.unpack(stream) core.earlyCapabilityFlags = Uint16LE.unpack(stream) core.clientDigProductId = decodeUTF16LE(stream.read(64)) core.connectionType = ConnectionType(Uint8.unpack(stream)) stream.read(1) core.serverSelectedProtocol = Uint32LE.unpack(stream) core.desktopPhysicalWidth = Uint32LE.unpack(stream) core.desktopPhysicalHeight = Uint32LE.unpack(stream) core.desktopOrientation = DesktopOrientation( Uint16LE.unpack(stream)) core.desktopScaleFactor = Uint32LE.unpack(stream) core.deviceScaleFactor = Uint32LE.unpack(stream) except EOFError: # The stream has reached the end, we don't have any more optional fields. This exception can be ignored. pass return core