class FastPathOutputParser(Parser): def __init__(self): super().__init__() self.bitmapParser = BitmapParser() def getEventLength(self, event: FastPathOutputEvent) -> int: 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, FastPathOutputEvent): length = len(event.payload) + 3 if event.compressionFlags is not None: length += 1 return length return size def isCompressed(self, header: int) -> bool: return ( header >> 6 ) & FastPathOutputCompressionType.FASTPATH_OUTPUT_COMPRESSION_USED != 0 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 parseBitmapEventRaw(self, stream: BytesIO, header: int, compressionFlags: int, size: int) -> FastPathBitmapEvent: return FastPathBitmapEvent(header, compressionFlags, [], stream.read(size)) def parseBitmapEvent( self, fastPathBitmapEvent: FastPathOutputEvent) -> FastPathBitmapEvent: rawBitmapUpdateData = fastPathBitmapEvent.payload stream = BytesIO(rawBitmapUpdateData) updateType = Uint16LE.unpack(stream.read(2)) bitmapData = self.bitmapParser.parseBitmapUpdateData(stream.read()) return FastPathBitmapEvent(fastPathBitmapEvent.header, fastPathBitmapEvent.compressionFlags, bitmapData, rawBitmapUpdateData) def writeBitmapEvent(self, stream: BytesIO, event: FastPathBitmapEvent): stream.write(event.payload) def parseOrdersEvent(self, stream: BytesIO, header: int, compressionFlags: int, size: int) -> FastPathOrdersEvent: orderCount = Uint16LE.unpack(stream) orderData = stream.read(size - 2) assert len(orderData) == size - 2 ordersEvent = FastPathOrdersEvent(header, compressionFlags, orderCount, orderData) controlFlags = Uint8.unpack(orderData[0]) if controlFlags & (DrawingOrderControlFlags.TS_SECONDARY | DrawingOrderControlFlags.TS_STANDARD)\ == (DrawingOrderControlFlags.TS_SECONDARY | DrawingOrderControlFlags.TS_STANDARD): ordersEvent.secondaryDrawingOrders = self.parseSecondaryDrawingOrder( orderData) elif controlFlags & DrawingOrderControlFlags.TS_SECONDARY: pass return ordersEvent def parseSecondaryDrawingOrder(self, orderData: bytes) -> SecondaryDrawingOrder: 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 writeOrdersEvent(self, stream, event): Uint16LE.pack(event.orderCount, stream) stream.write(event.orderData) def write(self, event: FastPathOutputEvent) -> bytes: stream = BytesIO() Uint8.pack(event.header, stream) if event.compressionFlags is not None: Uint8.pack(event.compressionFlags if event.compressionFlags else 0, stream) updateStream = BytesIO() if isinstance(event, FastPathBitmapEvent): self.writeBitmapEvent(updateStream, event) elif isinstance(event, FastPathOrdersEvent): self.writeOrdersEvent(updateStream, event) else: # Means it's simply a FastPathOutputUpdateEvent, this needs to be the last elif. updateStream.write(event.payload) updateData = updateStream.getvalue() Uint16LE.pack(len(updateData), stream) stream.write(updateData) return stream.getvalue()
def __init__(self): super().__init__() self.bitmapParser = BitmapParser()
class FastPathOutputParser(Parser): def __init__(self): super().__init__() self.bitmapParser = BitmapParser() def getEventLength(self, event: FastPathOutputEvent) -> int: 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 += len(event.payload) elif isinstance(event, FastPathBitmapEvent): size += len(event.payload) elif isinstance(event, FastPathOutputEvent): length = len(event.payload) + 3 if event.compressionFlags is not None: length += 1 return length return size def isCompressed(self, header: int) -> bool: return ( header >> 6 ) & FastPathOutputCompressionType.FASTPATH_OUTPUT_COMPRESSION_USED != 0 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 parseBitmapEventRaw(self, stream: BytesIO, header: int, compressionFlags: int, size: int) \ -> FastPathBitmapEvent: return FastPathBitmapEvent(header, compressionFlags, [], stream.read(size)) 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 writeBitmapEvent(self, stream: BytesIO, event: FastPathBitmapEvent): stream.write(event.payload) def parseOrdersEvent(self, stream: BytesIO, header: int, compressionFlags: int, size: int) -> FastPathOrdersEvent: """ Parse the order events from a TS_FP_UPDATE_ORDERS. This is specified in MS-RDPEGDI. """ payload = stream.read(size) assert len(payload) == size orders = FastPathOrdersEvent(header, compressionFlags, payload) return orders def writeOrdersEvent(self, stream, event): # Just write the saved raw bytes as-is. stream.write(event.payload) def write(self, event: FastPathOutputEvent) -> bytes: stream = BytesIO() Uint8.pack(event.header, stream) if event.compressionFlags is not None: Uint8.pack(event.compressionFlags if event.compressionFlags else 0, stream) updateStream = BytesIO() if isinstance(event, FastPathBitmapEvent): self.writeBitmapEvent(updateStream, event) elif isinstance(event, FastPathOrdersEvent): self.writeOrdersEvent(updateStream, event) else: # Means it's simply a FastPathOutputUpdateEvent, this needs to be the last elif. updateStream.write(event.payload) updateData = updateStream.getvalue() Uint16LE.pack(len(updateData), stream) stream.write(updateData) return stream.getvalue()