Files
2022-07-21 16:46:26 -07:00

214 lines
8.5 KiB
Python

import enum
import io
from typing import List
from aardwolf.protocol.fastpath.palette import TS_UPDATE_PALETTE_DATA
from aardwolf.protocol.fastpath.bitmap import TS_UPDATE_BITMAP_DATA
from aardwolf.protocol.fastpath.pointer import TS_FP_LARGEPOINTERATTRIBUTE, TS_FP_CACHEDPOINTERATTRIBUTE, TS_FP_POINTERATTRIBUTE, TS_POINTERPOSATTRIBUTE, TS_FP_COLORPOINTERATTRIBUTE
from aardwolf.protocol.fastpath.surface import TS_SURFCMD
#from aardwolf.protocol.fastpath.order import TS_FP_UPDATE_ORDERS
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/68b5ee54-d0d5-4d65-8d81-e1c4025f7597
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/a1c4caa8-00ed-45bb-a06e-5177473766d3
class FASTPATH_ACTION(enum.Enum):
FASTPATH = 0x0 #Indicates that the PDU is a fast-path output PDU.
X224 = 0x3 #Indicates the presence of a TPKT Header ([T123] section 8) initial version byte which indicates that the PDU is a slow-path output PDU (in this case the full value of the initial byte MUST be 0x03).
class FASTPATH_SEC(enum.IntFlag):
NONE = 0x0
SECURE_CHECKSUM = 0x1 #Indicates that the MAC signature for the PDU was generated using the "salted MAC generation" technique (section 5.3.6.1.1). If this bit is not set, then the standard technique was used (sections 2.2.8.1.1.2.2 and 2.2.8.1.1.2.3).
ENCRYPTED = 0x2 #Indicates that the PDU contains an 8-byte MAC signature after the optional length2 field (that is, the dataSignature field is present), and the contents of the PDU are encrypted using the negotiated encryption package (sections 5.3.2 and 5.3.6).
class TS_FP_FIPS_INFO:
def __init__(self):
self.length:int = 16
self.version:int = 1
self.padlen:int = None
def to_bytes(self):
t = self.length.to_bytes(2, byteorder='little', signed=False)
t += self.version.to_bytes(1, byteorder='little', signed=False)
t += self.padlen.to_bytes(1, byteorder='little', signed=False)
return t
@staticmethod
def from_bytes(bbuff: bytes):
return TS_FP_FIPS_INFO.from_buffer(io.BytesIO(bbuff))
@staticmethod
def from_buffer(buff: io.BytesIO):
msg = TS_FP_FIPS_INFO()
msg.length = int.from_bytes(buff.read(2), byteorder='little', signed=False)
msg.version = int.from_bytes(buff.read(1), byteorder='little', signed=False)
msg.padlen = int.from_bytes(buff.read(1), byteorder='little', signed=False)
return msg
def __repr__(self):
t = '==== TS_FP_FIPS_INFO ====\r\n'
for k in self.__dict__:
if isinstance(self.__dict__[k], enum.IntFlag):
value = self.__dict__[k]
elif isinstance(self.__dict__[k], enum.Enum):
value = self.__dict__[k].name
else:
value = self.__dict__[k]
t += '%s: %s\r\n' % (k, value)
return t
class FASTPATH_UPDATETYPE(enum.IntFlag):
ORDERS = 0x0 #Indicates a Fast-Path Orders Update ([MS-RDPEGDI] section 2.2.2.2).
BITMAP = 0x1 #Indicates a Fast-Path Bitmap Update (section 2.2.9.1.2.1.2).
PALETTE = 0x2 #Indicates a Fast-Path Palette Update (section 2.2.9.1.2.1.1).
SYNCHRONIZE = 0x3 #Indicates a Fast-Path Synchronize Update (section 2.2.9.1.2.1.3).
SURFCMDS = 0x4 #Indicates a Fast-Path Surface Commands Update (section 2.2.9.1.2.1.10).
PTR_NULL = 0x5 #Indicates a Fast-Path System Pointer Hidden Update (section 2.2.9.1.2.1.5).
PTR_DEFAULT = 0x6 #Indicates a Fast-Path System Pointer Default Update (section 2.2.9.1.2.1.6).
PTR_POSITION = 0x8 #Indicates a Fast-Path Pointer Position Update (section 2.2.9.1.2.1.4).
COLOR = 0x9 #Indicates a Fast-Path Color Pointer Update (section 2.2.9.1.2.1.7).
CACHED = 0xA #Indicates a Fast-Path Cached Pointer Update (section 2.2.9.1.2.1.9).
POINTER = 0xB #Indicates a Fast-Path New Pointer Update (section 2.2.9.1.2.1.8).
LARGE_POINTER = 0xC #Indicates a Fast-Path Large Pointer Update (section 2.2.9.1.2.1.11).
class FASTPATH_FRAGMENT(enum.Enum):
SINGLE = 0x0 #The fast-path data in the updateData field is not part of a sequence of fragments.
LAST = 0x1 #The fast-path data in the updateData field contains the last fragment in a sequence of fragments.
FIRST = 0x2 #The fast-path data in the updateData field contains the first fragment in a sequence of fragments.
NEXT = 0x3 #The fast-path data in the updateData field contains the second or subsequent fragment in a sequence of fragments.
class FASTPATH_OUTPUT_COMPRESSION(enum.IntFlag):
USED = 0x2 #Indicates that the compressionFlags field is present.
class TS_FP_UPDATE:
def __init__(self):
# HDR start
self.updateCode: FASTPATH_UPDATETYPE = None
self.fragmentation: FASTPATH_FRAGMENT = None
self.compression: int = None
# HDR end
self.compressionFlags:int = None
self.size:int = None
self.updateData:bytes = None
# high level
self.update = None
def to_bytes(self):
data = self.update.to_bytes()
t = (self.updateCode | self.fragmentation.value << 4 | self.compression << 6).to_bytes(1, byteorder='little', signed = False)
t += self.compressionFlags.to_bytes(1, byteorder='little', signed = False)
t += len(data).to_bytes(2, byteorder='little', signed = False)
t += data
return t
@staticmethod
def from_bytes(bbuff: bytes):
return TS_FP_UPDATE.from_buffer(io.BytesIO(bbuff))
@staticmethod
def from_buffer(buff: io.BytesIO):
msg = TS_FP_UPDATE()
updateHeader = int.from_bytes(buff.read(1), byteorder='little', signed = False)
msg.updateCode = FASTPATH_UPDATETYPE(updateHeader & 0xF)
msg.fragmentation = FASTPATH_FRAGMENT((updateHeader >> 4) & 3)
msg.compression = FASTPATH_OUTPUT_COMPRESSION(updateHeader >> 6)
if FASTPATH_OUTPUT_COMPRESSION.USED in msg.compression:
print('Bulk compression not implemented!')
msg.compressionFlags = int.from_bytes(buff.read(1), byteorder='little', signed = False)
msg.size = int.from_bytes(buff.read(2), byteorder='little', signed = False)
msg.updateData = buff.read(msg.size)
ot = otype2obj[msg.updateCode]
if ot is not None:
msg.update = ot.from_bytes(msg.updateData)
return msg
def __repr__(self):
t = '==== TS_FP_UPDATE ====\r\n'
for k in self.__dict__:
if isinstance(self.__dict__[k], enum.IntFlag):
value = self.__dict__[k]
elif isinstance(self.__dict__[k], enum.Enum):
value = self.__dict__[k].name
else:
value = self.__dict__[k]
t += '%s: %s\r\n' % (k, value)
return t
otype2obj = {
FASTPATH_UPDATETYPE.ORDERS : None, # TODO
FASTPATH_UPDATETYPE.BITMAP : TS_UPDATE_BITMAP_DATA,
FASTPATH_UPDATETYPE.PALETTE : TS_UPDATE_PALETTE_DATA,
FASTPATH_UPDATETYPE.SYNCHRONIZE : None, # this is normal
FASTPATH_UPDATETYPE.SURFCMDS : TS_SURFCMD,
FASTPATH_UPDATETYPE.PTR_NULL : None, # this is normal
FASTPATH_UPDATETYPE.PTR_DEFAULT : None, # this is normal
FASTPATH_UPDATETYPE.PTR_POSITION : TS_POINTERPOSATTRIBUTE,
FASTPATH_UPDATETYPE.COLOR : TS_FP_COLORPOINTERATTRIBUTE,
FASTPATH_UPDATETYPE.CACHED : TS_FP_CACHEDPOINTERATTRIBUTE,
FASTPATH_UPDATETYPE.POINTER : TS_FP_POINTERATTRIBUTE,
FASTPATH_UPDATETYPE.LARGE_POINTER : TS_FP_LARGEPOINTERATTRIBUTE,
}
obj2otype = {v: k for k, v in otype2obj.items()}
class TS_FP_UPDATE_PDU:
def __init__(self):
self.action:FASTPATH_ACTION = 0
self.reserved:int = 0
self.flags:FASTPATH_SEC = None
self.length1:int = None
self.length2:int = None
self.fipsInformation:TS_FP_FIPS_INFO = None #OPTIONAL!
self.dataSignature:bytes = None
self.fpOutputUpdates:List[TS_FP_UPDATE] = []
def to_bytes(self):
raise NotImplementedError()
@staticmethod
def from_bytes(bbuff: bytes, with_fips = False):
return TS_FP_UPDATE_PDU.from_buffer(io.BytesIO(bbuff), with_fips)
@staticmethod
def from_buffer(buff: io.BytesIO, with_fips = False):
msg = TS_FP_UPDATE_PDU()
t = buff.read(1)[0]
msg.action = FASTPATH_ACTION(t & 3)
msg.reserved = (t >> 2) & 15
msg.flags = FASTPATH_SEC(t >> 6)
msg.length1 = buff.read(1)[0]
length = msg.length1
if msg.length1 >> 7 == 1:
msg.length2 = buff.read(1)[0]
buff.seek(-2,1)
length = int.from_bytes(buff.read(2), byteorder='big', signed=False)
length = length & 0x7fff
if with_fips is True:
msg.fipsInformation = TS_FP_FIPS_INFO.from_buffer(buff)
if FASTPATH_SEC.ENCRYPTED in msg.flags or FASTPATH_SEC.SECURE_CHECKSUM in msg.flags:
msg.dataSignature = buff.read(8)
if FASTPATH_SEC.ENCRYPTED in msg.flags:
msg.fpOutputUpdates = buff.read()
else:
msg.fpOutputUpdates = TS_FP_UPDATE.from_buffer(buff)
return msg
def __repr__(self):
t = '==== TS_SHAREDATAHEADER ====\r\n'
for k in self.__dict__:
if k == 'fpOutputUpdates':
value = self.__dict__[k][:100]
elif isinstance(self.__dict__[k], enum.IntFlag):
value = self.__dict__[k].name
elif isinstance(self.__dict__[k], enum.Enum):
value = self.__dict__[k].name
else:
value = self.__dict__[k]
t += '%s: %s\r\n' % (k, value)
return t