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 parseDeviceCreateRequest(self, deviceID: int, fileID: int, completionID: int, minorFunction: int, stream: BytesIO) -> DeviceCreateRequestPDU: desiredAccess = Uint32LE.unpack(stream) allocationSize = Uint64LE.unpack(stream) fileAttributes = FileAttributes(Uint32LE.unpack(stream)) sharedAccess = FileShareAccess(Uint32LE.unpack(stream)) createDisposition = FileCreateDisposition(Uint32LE.unpack(stream)) createOptions = FileCreateOptions(Uint32LE.unpack(stream)) pathLength = Uint32LE.unpack(stream) path = stream.read(pathLength) path = decodeUTF16LE(path)[:-1] return DeviceCreateRequestPDU(deviceID, fileID, completionID, minorFunction, desiredAccess, allocationSize, fileAttributes, sharedAccess, createDisposition, createOptions, path)
def parseDeviceCreateRequest(self, deviceId: int, fileId: int, completionId: int, minorFunction: int, stream: BytesIO) -> DeviceCreateRequestPDU: """ Starting at desiredAccess. """ desiredAccess = Uint32LE.unpack(stream) allocationSize = Uint64LE.unpack(stream) fileAttributes = Uint32LE.unpack(stream) sharedAccess = Uint32LE.unpack(stream) createDisposition = Uint32LE.unpack(stream) createOptions = Uint32LE.unpack(stream) pathLength = Uint32LE.unpack(stream) path = stream.read(pathLength) return DeviceCreateRequestPDU(deviceId, fileId, completionId, minorFunction, desiredAccess, allocationSize, fileAttributes, sharedAccess, createDisposition, createOptions, path)
def parseClientClusterData(self, stream: BytesIO) -> ClientClusterData: flags = Uint32LE.unpack(stream) redirectedSessionID = Uint32LE.unpack(stream) return ClientClusterData(flags, redirectedSessionID)
def parseClientSecurityData(self, stream: BytesIO) -> ClientSecurityData: encryptionMethods = Uint32LE.unpack(stream) extEncryptionMethods = Uint32LE.unpack(stream) return ClientSecurityData(encryptionMethods, extEncryptionMethods)
def parsePlaySound(self, stream: BytesIO, header): duration = Uint32LE.unpack(stream) frequency = Uint32LE.unpack(stream) return PlaySoundPDU(header, duration, frequency)
def parseControl(self, stream: BytesIO, header): action = Uint16LE.unpack(stream) grantID = Uint16LE.unpack(stream) controlID = Uint32LE.unpack(stream) return ControlPDU(header, action, grantID, controlID)
def parseError(self, stream: BytesIO, header): errorInfo = Uint32LE.unpack(stream) return SetErrorInfoPDU(header, ErrorInfo(errorInfo))
def parse(s: BytesIO) -> 'FrameMarker': self = FrameMarker() self.action = Uint32LE.unpack(s) return self
def parseDeviceReadRequest(self, deviceID: int, fileID: int, completionID: int, minorFunction: int, stream: BytesIO) -> DeviceReadRequestPDU: length = Uint32LE.unpack(stream) offset = Uint64LE.unpack(stream) stream.read(20) # Padding return DeviceReadRequestPDU(deviceID, fileID, completionID, minorFunction, length, offset)
def parseDeviceReadResponse(self, deviceID: int, completionID: int, ioStatus: int, stream: BytesIO) -> DeviceReadResponsePDU: length = Uint32LE.unpack(stream) payload = stream.read(length) return DeviceReadResponsePDU(deviceID, completionID, ioStatus, payload)
def parseSynchronizeEvent(self, stream, eventTime): stream.read(2) flags = Uint32LE.unpack(stream) return SynchronizeEvent(eventTime, flags)
def parseDeviceListAnnounce(self, stream: BytesIO) -> DeviceListAnnounceRequest: deviceCount = Uint32LE.unpack(stream) deviceList = [self.parseDeviceAnnounce(stream) for _ in range(deviceCount)] return DeviceListAnnounceRequest(deviceList)
def doParse(self, data: bytes) -> NTLMSSPPDU: stream = BytesIO(data) signature = stream.read(8) messageType = Uint32LE.unpack(stream) return self.handlers[messageType](data, stream)
def doParse(self, data: bytes) -> VirtualChannelPDU: stream = BytesIO(data) length = Uint32LE.unpack(stream) flags = Uint32LE.unpack(stream) payload = stream.read(length) return VirtualChannelPDU(flags, payload)
def parseDeviceListAnnounce(self, stream: BytesIO) -> DeviceListAnnounceRequest: deviceCount = Uint32LE.unpack(stream) deviceList = [] for i in range(deviceCount): deviceList.append(self.parseSingleDeviceAnnounce(stream)) return DeviceListAnnounceRequest(deviceList)
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
def parseBitmap(self, stream: BytesIO, timestamp: int) -> PlayerBitmapPDU: width = Uint32LE.unpack(stream) height = Uint32LE.unpack(stream) pixels = stream.read(width * height * 4) return PlayerBitmapPDU(timestamp, width, height, pixels)
def parseSystemEvent(self, stream): pointerType = Uint32LE.unpack(stream) return PointerSystemEvent(pointerType)
def parseFormatDataRequest(self, payload, msgFlags): s = BytesIO(payload) out = FormatDataRequestPDU(Uint32LE.unpack(s)) self.req = out return out
def parseFileContentsResponse(self, payload, msgFlags): stream = BytesIO(payload) streamId = Uint32LE.unpack(stream) # FIXME: Need to grab the actual file size from the reply. data = stream.read() return FileContentsResponsePDU(payload, msgFlags, streamId, data)
def parseFileDescription(self, stream: BytesIO) -> PlayerFileDescription: length = Uint32LE.unpack(stream) path = stream.read(length).decode() isDirectory = bool(Uint8.unpack(stream)) return PlayerFileDescription(path, isDirectory)