diff --git a/rdpy/protocol/network/type.py b/rdpy/protocol/network/type.py index 285411f..0385345 100644 --- a/rdpy/protocol/network/type.py +++ b/rdpy/protocol/network/type.py @@ -17,7 +17,7 @@ def sizeof(element): @param element: Type or Tuple(Type | Tuple,) @return: size of element in byte ''' - if isinstance(element, tuple): + if isinstance(element, tuple) or isinstance(element, list): size = 0 for i in element: size += sizeof(i) @@ -96,7 +96,7 @@ class Type(object): @return: size in byte of type ''' pass - + class CallableValue(object): ''' wrap access of callable value. @@ -695,7 +695,7 @@ class Stream(StringIO): @param value: (tuple | Type) object ''' #read each tuple - if isinstance(value, tuple): + if isinstance(value, tuple) or isinstance(value, list): for element in value: self.readType(element) return @@ -721,11 +721,54 @@ class Stream(StringIO): @param value: (tuple | Type) ''' #write each element of tuple - if isinstance(value, tuple): + if isinstance(value, tuple) or isinstance(value, list): for element in value: self.writeType(element) return value.write(self) + +class ArrayType(Type): + ''' + in write mode ArrayType is just list + but in read mode it can be dynamic + readLen may be dynamic + ''' + def __init__(self, typeFactory, readLen = UInt8(), conditional = lambda:True, optional = False, constant = False): + ''' + constructor + @param typeFactory: class use to init new element on read + @param readLen: number of element in sequence + @param conditional : function call before read or write type + @param optional: boolean check before read if there is still data in stream + @param constant: if true check any changement of object during reading + ''' + Type.__init__(self, conditional, optional, constant) + self._typeFactory = typeFactory + self._readLen = readLen + self._array = [] + + def __read__(self, s): + ''' + create new object and read it + @param s: Stream + ''' + for i in range(0, self._readLen.value): + element = self._typeFactory() + s.readType(element) + self._array.append(element) + + def __write__(self, s): + ''' + just write array + @param s: Stream + ''' + s.writeType(self._array) + + def __sizeof__(self): + ''' + sizeof inner array + ''' + return sizeof(self._array) def CheckValueOnRead(cls): ''' diff --git a/rdpy/protocol/rdp/gdl.py b/rdpy/protocol/rdp/gdl.py index 9b342ac..43f0a29 100644 --- a/rdpy/protocol/rdp/gdl.py +++ b/rdpy/protocol/rdp/gdl.py @@ -3,7 +3,7 @@ ''' from rdpy.protocol.network.layer import LayerAutomata -from rdpy.protocol.network.type import CompositeType, UniString, UInt16Le, UInt16Be, UInt32Le, sizeof +from rdpy.protocol.network.type import CompositeType, UniString, String, UInt8, UInt16Le, UInt16Be, UInt32Le, sizeof, ArrayType from rdpy.utils.const import ConstAttributes, TypeAttributes from rdpy.protocol.network.error import InvalidExpectedDataException @@ -64,8 +64,53 @@ class PerfFlag(object): @TypeAttributes(UInt16Le) class AfInet(object): AF_INET = 0x00002 - AF_INET6 = 0x0017 + AF_INET6 = 0x0017 +@ConstAttributes +@TypeAttributes(UInt16Le) +class PDUType(object): + PDUTYPE_DEMANDACTIVEPDU = 0x1001 + PDUTYPE_CONFIRMACTIVEPDU = 0x3001 + PDUTYPE_DEACTIVATEALLPDU = 0x6001 + PDUTYPE_DATAPDU = 0x7001 + PDUTYPE_SERVER_REDIR_PKT = 0xA001 + +@ConstAttributes +@TypeAttributes(UInt16Le) +class CapsType(object): + ''' + different type of capabilities + @see: http://msdn.microsoft.com/en-us/library/cc240486.aspx + ''' + CAPSTYPE_GENERAL = 0x0001 + CAPSTYPE_BITMAP = 0x0002 + CAPSTYPE_ORDER = 0x0003 + CAPSTYPE_BITMAPCACHE = 0x0004 + CAPSTYPE_CONTROL = 0x0005 + CAPSTYPE_ACTIVATION = 0x0007 + CAPSTYPE_POINTER = 0x0008 + CAPSTYPE_SHARE = 0x0009 + CAPSTYPE_COLORCACHE = 0x000A + CAPSTYPE_SOUND = 0x000C + CAPSTYPE_INPUT = 0x000D + CAPSTYPE_FONT = 0x000E + CAPSTYPE_BRUSH = 0x000F + CAPSTYPE_GLYPHCACHE = 0x0010 + CAPSTYPE_OFFSCREENCACHE = 0x0011 + CAPSTYPE_BITMAPCACHE_HOSTSUPPORT = 0x0012 + CAPSTYPE_BITMAPCACHE_REV2 = 0x0013 + CAPSTYPE_VIRTUALCHANNEL = 0x0014 + CAPSTYPE_DRAWNINEGRIDCACHE = 0x0015 + CAPSTYPE_DRAWGDIPLUS = 0x0016 + CAPSTYPE_RAIL = 0x0017 + CAPSTYPE_WINDOW = 0x0018 + CAPSETTYPE_COMPDESK = 0x0019 + CAPSETTYPE_MULTIFRAGMENTUPDATE = 0x001A + CAPSETTYPE_LARGE_POINTER = 0x001B + CAPSETTYPE_SURFACE_COMMANDS = 0x001C + CAPSETTYPE_BITMAP_CODECS = 0x001D + CAPSSETTYPE_FRAME_ACKNOWLEDGE = 0x001E + class RDPInfo(CompositeType): ''' client informations @@ -88,15 +133,15 @@ class RDPInfo(CompositeType): #length of working directory unistring less 2 byte null terminate self.cbWorkingDir = UInt16Le(lambda:sizeof(self.workingDir) - 2) #microsoft domain - self.domain = UniString(readLen = self.cbDomain) + self.domain = UniString(readLen = UInt16Le(lambda:self.cbDomain.value - 2)) #session username - self.userName = UniString(readLen = self.cbUserName) + self.userName = UniString(readLen = UInt16Le(lambda:self.cbUserName.value - 2)) #associate password - self.password = UniString(readLen = self.cbPassword) + self.password = UniString(readLen = UInt16Le(lambda:self.cbPassword.value - 2)) #shell execute at start of session - self.alternateShell = UniString(readLen = self.cbAlternateShell) + self.alternateShell = UniString(readLen = UInt16Le(lambda:self.cbAlternateShell.value - 2)) #working directory for session - self.workingDir = UniString(readLen = self.cbWorkingDir) + self.workingDir = UniString(readLen = UInt16Le(lambda:self.cbWorkingDir.value - 2)) #more client informations self.extendedInfo = RDPExtendedInfo(conditional = extendedInfoConditional) @@ -120,6 +165,103 @@ class RDPExtendedInfo(CompositeType): #TODO make tiomezone #self.performanceFlags = PerfFlag.PERF_DISABLE_WALLPAPER | PerfFlag.PERF_DISABLE_MENUANIMATIONS | PerfFlag.PERF_DISABLE_CURSOR_SHADOW +class ShareControlHeader(CompositeType): + ''' + @see: http://msdn.microsoft.com/en-us/library/cc240576.aspx + ''' + def __init__(self, totalLength): + ''' + constructor + @param totalLength: total length of pdu packet + ''' + CompositeType.__init__(self) + #share control header + self.totalLength = UInt16Le(totalLength) + self.pduType = UInt16Le() + self.PDUSource = UInt16Le() + +class Capability(CompositeType): + ''' + @see: http://msdn.microsoft.com/en-us/library/cc240486.aspx + ''' + def __init__(self): + CompositeType.__init__(self) + self.capabilitySetType = UInt16Le() + self.lengthCapability = UInt16Le(lambda:sizeof(self)) + self.generalCapability = GeneralCapability(conditional = lambda:self.capabilitySetType == CapsType.CAPSTYPE_GENERAL) + self.bitmapCapability = BitmapCapability(conditional = lambda:self.capabilitySetType == CapsType.CAPSTYPE_BITMAP) + self.capabilityData = String(readLen = UInt16Le(lambda:self.lengthCapability.value - 4), conditional = lambda:not self.capabilitySetType in [CapsType.CAPSTYPE_GENERAL, CapsType.CAPSTYPE_BITMAP]) + +class GeneralCapability(CompositeType): + ''' + @see: http://msdn.microsoft.com/en-us/library/cc240549.aspx + ''' + def __init__(self, conditional = lambda:True): + CompositeType.__init__(self, conditional = conditional) + self.osMajorType = UInt16Le() + self.osMinorType = UInt16Le() + self.protocolVersion = UInt16Le(0x0200, constant = True) + self.pad2octetsA = UInt16Le() + self.generalCompressionTypes = UInt16Le(0, constant = True) + self.extraFlags = UInt16Le() + self.updateCapabilityFlag = UInt16Le(0, constant = True) + self.remoteUnshareFlag = UInt16Le(0, constant = True) + self.generalCompressionLevel = UInt16Le(0, constant = True) + self.refreshRectSupport = UInt8() + self.suppressOutputSupport = UInt8() + +class BitmapCapability(CompositeType): + ''' + @see: http://msdn.microsoft.com/en-us/library/cc240554.aspx + ''' + def __init__(self, conditional = lambda:True): + CompositeType.__init__(self, conditional = conditional) + self.preferredBitsPerPixel = UInt16Le() + self.receive1BitPerPixel = UInt16Le(0x0001) + self.receive4BitsPerPixel = UInt16Le(0x0001) + self.receive8BitsPerPixel = UInt16Le(0x0001) + self.desktopWidth = UInt16Le() + self.desktopHeight = UInt16Le() + self.pad2octets = UInt16Le() + self.desktopResizeFlag = UInt16Le() + self.bitmapCompressionFlag = UInt16Le() + self.highColorFlags = UInt8(0) + self.drawingFlags = UInt8() + self.multipleRectangleSupport = UInt16Le(0x0001, constant = True) + self.pad2octetsB = UInt16Le() + +class DemandActivePDU(CompositeType): + ''' + @see: http://msdn.microsoft.com/en-us/library/cc240485.aspx + ''' + def __init__(self): + CompositeType.__init__(self) + self.shareControlHeader = ShareControlHeader(lambda:sizeof(self)) + self.shareId = UInt32Le() + self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor)) + self.lengthCombinedCapabilities = UInt16Le(lambda:(sizeof(self.numberCapabilities) + sizeof(self.pad2Octets) + sizeof(self.capabilitySets))) + self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor) + self.numberCapabilities = UInt16Le(lambda:len(self.capabilitySets._array)) + self.pad2Octets = UInt16Le() + self.capabilitySets = ArrayType(Capability, readLen = self.numberCapabilities) + self.sessionId = UInt32Le() + +class ConfirmActivePDU(CompositeType): + ''' + @see: http://msdn.microsoft.com/en-us/library/cc240488.aspx + ''' + def __init__(self): + CompositeType.__init__(self) + self.shareControlHeader = ShareControlHeader(lambda:sizeof(self)) + self.shareId = UInt32Le() + self.originatorId = UInt16Le(0x03EA, constant = True) + self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor)) + self.lengthCombinedCapabilities = UInt16Le(lambda:(sizeof(self.numberCapabilities) + sizeof(self.pad2Octets) + sizeof(self.capabilitySets))) + self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor) + self.numberCapabilities = UInt16Le(lambda:len(self.capabilitySets._array)) + self.pad2Octets = UInt16Le() + self.capabilitySets = ArrayType(Capability, readLen = self.numberCapabilities) + class GDL(LayerAutomata): ''' Global Display Layer @@ -135,6 +277,10 @@ class GDL(LayerAutomata): self._channelId = UInt16Be() #logon info send from client to server self._info = RDPInfo(extendedInfoConditional = lambda:self._transport._serverSettings.core.rdpVersion == gcc.Version.RDP_VERSION_5_PLUS) + #server capabilities + self._serverCapabilities = {} + #client capabilities + self._clientCapabilities = {} def connect(self): ''' @@ -153,6 +299,10 @@ class GDL(LayerAutomata): self._transport.send(self._channelId, (SecurityFlag.SEC_INFO_PKT, UInt16Le(), self._info)) def recvLicenceInfo(self, data): + ''' + read license info packet and check if is a valid client info + @param data: Stream + ''' securityFlag = UInt16Le() securityFlagHi = UInt16Le() data.readType((securityFlag, securityFlagHi)) @@ -167,4 +317,24 @@ class GDL(LayerAutomata): raise InvalidExpectedDataException("waiting valid client pdu : rdpy doesn't support licensing neg") if not (validClientPdu.errorMessage.dwErrorCode == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.errorMessage.dwStateTransition == lic.StateTransition.ST_NO_TRANSITION): - raise InvalidExpectedDataException("server refuse licensing negotiation") \ No newline at end of file + raise InvalidExpectedDataException("server refuse licensing negotiation") + + self.setNextState(self.recvDemandActivePDU) + + def recvDemandActivePDU(self, data): + ''' + receive demand active PDU which contains + server capabilities. In this version of RDPY only + restricted group of capabilities are used. + send confirm active PDU + ''' + demandActivePDU = DemandActivePDU() + data.readType(demandActivePDU) + + for cap in demandActivePDU.capabilitySets._array: + self._serverCapabilities[cap.capabilitySetType] = cap + + confirmActivePDU = ConfirmActivePDU() + confirmActivePDU.capabilitySets._array = self._clientCapabilities.values() + + self._transport.send(self._channelId, confirmActivePDU) \ No newline at end of file diff --git a/rdpy/protocol/rdp/lic.py b/rdpy/protocol/rdp/lic.py index 653609c..3c71aa5 100644 --- a/rdpy/protocol/rdp/lic.py +++ b/rdpy/protocol/rdp/lic.py @@ -47,6 +47,9 @@ class StateTransition(object): ST_RESEND_LAST_MESSAGE = 0x00000004 class LicenceBinaryBlob(CompositeType): + ''' + blob use by license manager to echange security data + ''' def __init__(self): CompositeType.__init__(self) self.wBlobType = UInt16Le() @@ -54,6 +57,9 @@ class LicenceBinaryBlob(CompositeType): self.blobData = String(readLen = self.wBlobLen, conditional = lambda:self.wBlobLen.value > 0) class LicensingErrorMessage(CompositeType): + ''' + license error message + ''' def __init__(self, conditional = lambda:True): CompositeType.__init__(self, conditional = conditional) self.dwErrorCode = UInt32Le() @@ -61,6 +67,9 @@ class LicensingErrorMessage(CompositeType): self.blob = LicenceBinaryBlob() class LicPacket(CompositeType): + ''' + a license packet + ''' def __init__(self): CompositeType.__init__(self) #preambule diff --git a/rdpy/rdpclient.py b/rdpy/rdpclient.py index f2d59c2..cf3b1f3 100644 --- a/rdpy/rdpclient.py +++ b/rdpy/rdpclient.py @@ -11,5 +11,5 @@ if __name__ == '__main__': from twisted.internet import reactor #reactor.connectTCP("127.0.0.1", 5901, factory.RfbFactory(protocol)) #reactor.connectTCP("192.168.1.90", 3389, factory.RfbFactory(tpkt.TPKT(tpdu.TPDU(mcs.MCS())))) - reactor.connectTCP("192.168.135.71", 3389, rdp.Factory()) + reactor.connectTCP("192.168.135.182", 3389, rdp.Factory()) reactor.run() \ No newline at end of file