fix bug + more license automata

This commit is contained in:
speyrefitte
2014-07-15 17:11:06 +02:00
parent 088893fa63
commit c04982f434
8 changed files with 321 additions and 109 deletions

View File

@@ -1,6 +1,9 @@
# RDPY # RDPY
Remote Desktop Protocol in Twisted Python Remote Desktop Protocol in Twisted Python.
RDPY is ful python except the bitmap decompression in RDP client for performance. RDPY has no ambition to be as faster as freerdp, rdesktop or mstsc, is made to play with microsoft protocol. There are some limitations essentially due to price of license (Packet redirection and License extesion in RDP protocol).
## Requirements ## Requirements
* python2.7 * python2.7
@@ -8,16 +11,45 @@ Remote Desktop Protocol in Twisted Python
* python-openssl * python-openssl
* python-qt4 * python-qt4
* python-qt4reactor * python-qt4reactor
## Requirements libs
* python-sip-dev * python-sip-dev
* scons * scons
## Build
``` ```
$ git clone https://github.com/citronneur/rdpy.git rdpy $ git clone https://github.com/citronneur/rdpy.git rdpy
$ scons -C rdpy/lib install $ scons -C rdpy/lib install
``` ```
## Binaries ## Binaries
Binaries are uses as examples to use rdpy lib.
To create an RDP client (this example doesn't need build step of project because it doesn't call bitmap uncompress):
```
from rdpy.protocol.rdp import rdp
class RDPClientQtFactory(rdp.ClientFactory):
def buildObserver(self, controller):
class MyObserver(rdp.RDPClientObserver)
def __init__(self, controller)
rdp.RDPClientObserver.__init__(self, controller)
def onBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
#here code handle bitmap
pass
def onReady(self):
#send r key
self._controller.sendKeyEventUnicode(ord(unicode("r".toUtf8(), encoding="UTF-8")), True)
#mouse and click at pixel 200x200
self._controller.sendPointerEvent(200, 200, 1, true)
return MyObserver(controller)
def startedConnecting(self, connector):
pass
def clientConnectionLost(self, connector, reason):
pass
def clientConnectionFailed(self, connector, reason):
pass
```
RDP Client RDP Client
``` ```
$ rdpy/bin/rdpy-rdpclient XXX.XXX.XXX.XXX 3389 $ rdpy/bin/rdpy-rdpclient XXX.XXX.XXX.XXX 3389
@@ -30,15 +62,15 @@ $ rdpy/bin/rdpy-vncclient XXX.XXX.XXX.XXX 5901
RDP Proxy RDP Proxy
``` ```
$ rdpy/bin/rdpy-vncclient XXX.XXX.XXX.XXX 5901 $ rdpy/bin/rdpy-rdpproxy
``` ```
##Must be implemented before first release ##Limitations
* CreedSSP * CreedSSP
* Packet redirection * Packet redirection
* License * License
* Most common orders * Most common orders
* FastPath messages
* Des VNC (using pyDes) * Des VNC (using pyDes)
* VNC server side
this project is still in progress. this project is still in progress.

View File

@@ -443,10 +443,10 @@ class CompositeType(Type):
readLen += sizeof(self.__dict__[name]) readLen += sizeof(self.__dict__[name])
def __write__(self, s): def __write__(self, s):
''' """
call write on each ordered subtype Call write on each ordered sub type
@param s: Stream @param s: Stream
''' """
for name in self._typeName: for name in self._typeName:
try: try:
s.writeType(self.__dict__[name]) s.writeType(self.__dict__[name])
@@ -818,21 +818,20 @@ class Stream(StringIO):
value.write(self) value.write(self)
class ArrayType(Type): class ArrayType(Type):
''' """
in write mode ArrayType is just list In write mode ArrayType is just list
but in read mode it can be dynamic But in read mode it can be dynamic
readLen may be dynamic readLen may be dynamic
''' """
def __init__(self, typeFactory, init = None, readLen = UInt8(), conditional = lambda:True, optional = False, constant = False): def __init__(self, typeFactory, init = None, readLen = UInt8(), conditional = lambda:True, optional = False, constant = False):
''' """
constructor
@param typeFactory: class use to init new element on read @param typeFactory: class use to init new element on read
@param init: init array @param init: init array
@param readLen: number of element in sequence @param readLen: number of element in sequence
@param conditional : function call before read or write type @param conditional : function call before read or write type
@param optional: boolean check before read if there is still data in stream @param optional: boolean check before read if there is still data in stream
@param constant: if true check any changes of object during reading @param constant: if true check any changes of object during reading
''' """
Type.__init__(self, conditional, optional, constant) Type.__init__(self, conditional, optional, constant)
self._typeFactory = typeFactory self._typeFactory = typeFactory
self._readLen = readLen self._readLen = readLen
@@ -841,10 +840,10 @@ class ArrayType(Type):
self._array = init self._array = init
def __read__(self, s): def __read__(self, s):
''' """
create new object and read it Create new object and read it
@param s: Stream @param s: Stream
''' """
self._array = [] self._array = []
for _ in range(0, self._readLen.value): for _ in range(0, self._readLen.value):
element = self._typeFactory() element = self._typeFactory()
@@ -852,21 +851,21 @@ class ArrayType(Type):
self._array.append(element) self._array.append(element)
def __write__(self, s): def __write__(self, s):
''' """
just write array Just write array
@param s: Stream @param s: Stream
''' """
s.writeType(self._array) s.writeType(self._array)
def __sizeof__(self): def __sizeof__(self):
''' """
sizeof inner array Size of inner array
''' """
return sizeof(self._array) return sizeof(self._array)
class FactoryType(Type): class FactoryType(Type):
""" """
Call factory function on read or write Call factory function on read
""" """
def __init__(self, factory, conditional = lambda:True, optional = False, constant = False): def __init__(self, factory, conditional = lambda:True, optional = False, constant = False):
""" """
@@ -880,29 +879,43 @@ class FactoryType(Type):
if not callable(factory): if not callable(factory):
self._factory = lambda:factory self._factory = lambda:factory
self._value = self._factory() self._value = None
def __read__(self, s): def __read__(self, s):
''' """
call factory and read it Call factory and write it
@param s: Stream @param s: Stream
''' """
self._value = self._factory() self._value = self._factory()
s.readType(self._value) s.readType(self._value)
def __write__(self, s): def __write__(self, s):
''' """
call factory and write elements Call factory and read it
@param s: Stream @param s: Stream
''' """
self._value = self._factory() self._value = self._factory()
s.writeType(self._value) s.writeType(self._value)
def __getattr__(self, name):
"""
Magic function to be FactoryType as transparent as possible
@return: _value parameter
"""
return self._value.__getattribute__(name)
def __getitem__(self, item):
"""
Magic function to be FactoryType as transparent as possible
@return: index of _value
"""
return self._value.__getitem__(item)
def __sizeof__(self): def __sizeof__(self):
''' """
sizeof of object returned by factory Size of of object returned by factory
''' """
return sizeof(self._factory()) return sizeof(self._value)
def CheckValueOnRead(cls): def CheckValueOnRead(cls):
''' '''
@@ -923,7 +936,7 @@ def CheckValueOnRead(cls):
def hexDump(src, length=16): def hexDump(src, length=16):
''' '''
print hex representation of sr print hex representation of str
@param src: string @param src: string
''' '''
FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)]) FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)])

View File

@@ -269,9 +269,9 @@ class Capability(CompositeType):
return String(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) return String(readLen = UInt16Le(lambda:self.lengthCapability.value - 4))
if capability is None: if capability is None:
capability = CapabilityFactory capability = FactoryType(CapabilityFactory)
self.capability = FactoryType(capability) self.capability = capability
class GeneralCapability(CompositeType): class GeneralCapability(CompositeType):
""" """

View File

@@ -22,7 +22,7 @@ RDP extended license
@see: http://msdn.microsoft.com/en-us/library/cc241880.aspx @see: http://msdn.microsoft.com/en-us/library/cc241880.aspx
""" """
from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType
class MessageType(object): class MessageType(object):
""" """
@@ -41,6 +41,7 @@ class MessageType(object):
class ErrorCode(object): class ErrorCode(object):
""" """
License error message code License error message code
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
""" """
ERR_INVALID_SERVER_CERTIFICATE = 0x00000001 ERR_INVALID_SERVER_CERTIFICATE = 0x00000001
ERR_NO_LICENSE = 0x00000002 ERR_NO_LICENSE = 0x00000002
@@ -55,41 +56,163 @@ class ErrorCode(object):
class StateTransition(object): class StateTransition(object):
""" """
Automata state transition Automata state transition
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
""" """
ST_TOTAL_ABORT = 0x00000001 ST_TOTAL_ABORT = 0x00000001
ST_NO_TRANSITION = 0x00000002 ST_NO_TRANSITION = 0x00000002
ST_RESET_PHASE_TO_START = 0x00000003 ST_RESET_PHASE_TO_START = 0x00000003
ST_RESEND_LAST_MESSAGE = 0x00000004 ST_RESEND_LAST_MESSAGE = 0x00000004
class LicenceBinaryBlob(CompositeType): class BinaryBlobType(object):
""" """
Blob use by license manager to echange security data Binary blob data type
@see: http://msdn.microsoft.com/en-us/library/cc240481.aspx
""" """
def __init__(self): BB_DATA_BLOB = 0x0001
BB_RANDOM_BLOB = 0x0002
BB_CERTIFICATE_BLOB = 0x0003
BB_ERROR_BLOB = 0x0004
BB_ENCRYPTED_DATA_BLOB = 0x0009
BB_KEY_EXCHG_ALG_BLOB = 0x000D
BB_SCOPE_BLOB = 0x000E
BB_CLIENT_USER_NAME_BLOB = 0x000F
BB_CLIENT_MACHINE_NAME_BLOB = 0x0010
class LicenseBinaryBlob(CompositeType):
"""
Blob use by license manager to exchange security data
@see: http://msdn.microsoft.com/en-us/library/cc240481.aspx
"""
def __init__(self, blobType = 0):
CompositeType.__init__(self) CompositeType.__init__(self)
self.wBlobType = UInt16Le() self.wBlobType = UInt16Le(blobType, constant = True)
self.wBlobLen = UInt16Le(lambda:sizeof(self.blobData)) self.wBlobLen = UInt16Le(lambda:sizeof(self.blobData))
self.blobData = String(readLen = self.wBlobLen, conditional = lambda:self.wBlobLen.value > 0) self.blobData = String(readLen = self.wBlobLen)
class LicensingErrorMessage(CompositeType): class LicensingErrorMessage(CompositeType):
""" """
License error message License error message
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
""" """
def __init__(self, conditional = lambda:True): def __init__(self, conditional = lambda:True):
CompositeType.__init__(self, conditional = conditional) CompositeType.__init__(self, conditional = conditional)
self.dwErrorCode = UInt32Le() self.dwErrorCode = UInt32Le()
self.dwStateTransition = UInt32Le() self.dwStateTransition = UInt32Le()
self.blob = LicenceBinaryBlob() self.blob = LicenseBinaryBlob(BinaryBlobType.BB_ERROR_BLOB)
class ProductInformation(CompositeType):
"""
License server product information
@see: http://msdn.microsoft.com/en-us/library/cc241915.aspx
"""
def __init__(self):
CompositeType.__init__(self)
self.dwVersion = UInt32Le()
self.cbCompanyName = UInt32Le(lambda:sizeof(self.pbCompanyName))
#may contain "Microsoft Corporation" from server microsoft
self.pbCompanyName = String(readLen = self.cbCompanyName)
self.cbProductId = UInt32Le(lambda:sizeof(self.pbProductId))
#may contain "A02" from microsoft license server
self.pbProductId = String(readLen = self.cbProductId)
class Scope(CompositeType):
"""
Use in license nego
@see: http://msdn.microsoft.com/en-us/library/cc241917.aspx
"""
def __init__(self):
CompositeType.__init__(self)
self.scope = LicenseBinaryBlob(BinaryBlobType.BB_SCOPE_BLOB)
class ScopeList(CompositeType):
"""
Use in license nego
@see: http://msdn.microsoft.com/en-us/library/cc241916.aspx
"""
def __init__(self):
CompositeType.__init__(self)
self.scopeCount = UInt32Le(lambda:sizeof(self.scopeArray))
self.scopeArray = ArrayType(Scope, readLen = self.scopeCount)
class ServerLicenseRequest(CompositeType):
"""
Send by server to signal license request
server -> client
@see: http://msdn.microsoft.com/en-us/library/cc241914.aspx
"""
def __init__(self):
CompositeType.__init__(self)
self.serverRandom = String(readLen = UInt8(32))
self.productInfo = ProductInformation()
self.keyExchangeList = LicenseBinaryBlob(BinaryBlobType.BB_KEY_EXCHG_ALG_BLOB)
self.serverCertificate = LicenseBinaryBlob(BinaryBlobType.BB_CERTIFICATE_BLOB)
self.scopeList = ScopeList()
class ClientNewLicenseRequest(CompositeType):
"""
Send by client to ask new license for client.
RDPY doesn'support license reuse, need it in futur version
@see: http://msdn.microsoft.com/en-us/library/cc241918.aspx
"""
def __init__(self):
CompositeType.__init__(self)
#RSA and must be only RSA
self.preferredKeyExchangeAlg = UInt32Le(0x00000001, constant = True)
#pure microsoft client ;-)
#http://msdn.microsoft.com/en-us/library/1040af38-c733-4fb3-acd1-8db8cc979eda#id10
self.platformId = UInt32Le(0x04000000 | 0x00020000)
self.clientRandom = String("\x00" * 32)
self.encryptedPreMasterSecret = LicenseBinaryBlob(BinaryBlobType.BB_RANDOM_BLOB)
self.ClientUserName = LicenseBinaryBlob(BinaryBlobType.BB_CLIENT_USER_NAME_BLOB)
self.ClientMachineName = LicenseBinaryBlob(BinaryBlobType.BB_CLIENT_MACHINE_NAME_BLOB)
class LicPacket(CompositeType): class LicPacket(CompositeType):
""" """
A license packet A license packet
""" """
def __init__(self): def __init__(self, message = None):
CompositeType.__init__(self) CompositeType.__init__(self)
def MessageTypeFactory():
"""
Determine type in accordance of instance of licensingMessage type
Use in write mode
"""
if isinstance(self.licensingMessage, LicensingErrorMessage):
return MessageType.ERROR_ALERT
elif isinstance(self.licensingMessage, ServerLicenseRequest):
return MessageType.LICENSE_REQUEST
elif isinstance(self.licensingMessage, ClientNewLicenseRequest):
return MessageType.NEW_LICENSE_REQUEST
#preambule #preambule
self.bMsgtype = UInt8() self.bMsgtype = UInt8(lambda:(MessageTypeFactory()))
self.flag = UInt8() self.flag = UInt8()
self.wMsgSize = UInt16Le(lambda: sizeof(self)) self.wMsgSize = UInt16Le(lambda: sizeof(self))
self.errorMessage = LicensingErrorMessage(conditional = lambda:self.bMsgtype.value == MessageType.ERROR_ALERT)
def LicensingMessageFactory():
"""
factory for message nego
Use in read mode
"""
if self.bMsgtype.value == MessageType.ERROR_ALERT:
return LicensingErrorMessage()
elif self.bMsgtype.value == MessageType.LICENSE_REQUEST:
return ServerLicenseRequest()
elif self.bMsgtype.value == MessageType.NEW_LICENSE_REQUEST:
ClientNewLicenseRequest()
if message is None:
message = FactoryType(LicensingMessageFactory)
self.licensingMessage = message
def createNewLicenseRequest(serverLicenseRequest):
"""
Create new license request in response to server license request
@see: http://msdn.microsoft.com/en-us/library/cc241989.aspx
@see: http://msdn.microsoft.com/en-us/library/cc241918.aspx
@todo: need RDP license server
"""
return LicPacket(message = ClientNewLicenseRequest())

View File

@@ -32,9 +32,24 @@ import gcc, lic, caps, tpkt
class SecurityFlag(object): class SecurityFlag(object):
""" """
Microsoft security flags Microsoft security flags
@see: http://msdn.microsoft.com/en-us/library/cc240579.aspx
""" """
SEC_EXCHANGE_PKT = 0x0001
SEC_TRANSPORT_REQ = 0x0002
RDP_SEC_TRANSPORT_RSP = 0x0004
SEC_ENCRYPT = 0x0008
SEC_RESET_SEQNO = 0x0010
SEC_IGNORE_SEQNO = 0x0020
SEC_INFO_PKT = 0x0040 SEC_INFO_PKT = 0x0040
SEC_LICENSE_PKT = 0x0080 SEC_LICENSE_PKT = 0x0080
SEC_LICENSE_ENCRYPT_CS = 0x0200
SEC_LICENSE_ENCRYPT_SC = 0x0200
SEC_REDIRECTION_PKT = 0x0400
SEC_SECURE_CHECKSUM = 0x0800
SEC_AUTODETECT_REQ = 0x1000
SEC_AUTODETECT_RSP = 0x2000
SEC_HEARTBEAT = 0x4000
SEC_FLAGSHI_VALID = 0x8000
class InfoFlag(object): class InfoFlag(object):
""" """
@@ -635,9 +650,9 @@ class DataPDU(CompositeType):
return String() return String()
if pduData is None: if pduData is None:
pduData = PDUDataFactory pduData = FactoryType(PDUDataFactory)
self.pduData = FactoryType(pduData) self.pduData = pduData
class SynchronizeDataPDU(CompositeType): class SynchronizeDataPDU(CompositeType):
""" """
@@ -737,9 +752,9 @@ class UpdateDataPDU(CompositeType):
return String() return String()
if updateData is None: if updateData is None:
updateData = UpdateDataFactory updateData = FactoryType(UpdateDataFactory, conditional = lambda:(self.updateType.value != UpdateType.UPDATETYPE_SYNCHRONIZE))
self.updateData = FactoryType(updateData, conditional = lambda:(self.updateType.value != UpdateType.UPDATETYPE_SYNCHRONIZE)) self.updateData = updateData
class FastPathUpdatePDU(CompositeType): class FastPathUpdatePDU(CompositeType):
""" """
@@ -766,9 +781,9 @@ class FastPathUpdatePDU(CompositeType):
return String() return String()
if updateData is None: if updateData is None:
updateData = UpdateDataFactory updateData = FactoryType(UpdateDataFactory)
self.updateData = FactoryType(updateData) self.updateData = updateData
class SynchronizeUpdatePDU(CompositeType): class SynchronizeUpdatePDU(CompositeType):
""" """
@@ -876,26 +891,21 @@ class SlowPathInputEvent(CompositeType):
""" """
if isinstance(event, PointerEvent): if isinstance(event, PointerEvent):
return InputMessageType.INPUT_EVENT_MOUSE return InputMessageType.INPUT_EVENT_MOUSE
elif isinstance(event, ScancodeKeyEvent): elif isinstance(event, ScancodeKeyEvent):
return InputMessageType.INPUT_EVENT_SCANCODE return InputMessageType.INPUT_EVENT_SCANCODE
elif isinstance(event, UnicodeKeyEvent): elif isinstance(event, UnicodeKeyEvent):
return InputMessageType.INPUT_EVENT_UNICODE return InputMessageType.INPUT_EVENT_UNICODE
else:
return None
self.messageType = UInt16Le(lambda:MessageTypeFactory(self.slowPathInputData._value)) self.messageType = UInt16Le(lambda:MessageTypeFactory(self.slowPathInputData))
def SlowPathInputDataFactory(): def SlowPathInputDataFactory():
if self.messageType.value == InputMessageType.INPUT_EVENT_MOUSE: if self.messageType.value == InputMessageType.INPUT_EVENT_MOUSE:
return PointerEvent() return PointerEvent()
if messageData is None: if messageData is None:
messageData = SlowPathInputDataFactory messageData = FactoryType(SlowPathInputDataFactory)
self.slowPathInputData = FactoryType(messageData) self.slowPathInputData = messageData
class PointerEvent(CompositeType): class PointerEvent(CompositeType):
""" """
@@ -935,7 +945,13 @@ class PDUClientListener(object):
""" """
Interface for PDU client automata listener Interface for PDU client automata listener
""" """
def recvBitmapUpdateDataPDU(self, rectangles): def onReady(self):
"""
Event call when PDU layer is ready to send events
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUClientListener"))
def onUpdate(self, rectangles):
""" """
call when a bitmap data is received from update PDU call when a bitmap data is received from update PDU
@param rectangles: [pdu.BitmapData] struct @param rectangles: [pdu.BitmapData] struct
@@ -1011,9 +1027,6 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
} }
#share id between client and server #share id between client and server
self._shareId = 0 self._shareId = 0
#determine if layer is connected
self._isConnected = False
def connect(self): def connect(self):
""" """
@@ -1043,6 +1056,7 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
Read license info packet and check if is a valid client info Read license info packet and check if is a valid client info
@param data: Stream @param data: Stream
""" """
#license preambule
securityFlag = UInt16Le() securityFlag = UInt16Le()
securityFlagHi = UInt16Le() securityFlagHi = UInt16Le()
data.readType((securityFlag, securityFlagHi)) data.readType((securityFlag, securityFlagHi))
@@ -1053,13 +1067,14 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
validClientPdu = lic.LicPacket() validClientPdu = lic.LicPacket()
data.readType(validClientPdu) data.readType(validClientPdu)
if not validClientPdu.errorMessage._is_readed: if validClientPdu.bMsgtype.value == lic.MessageType.ERROR_ALERT and validClientPdu.licensingMessage.dwErrorCode.value == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.licensingMessage.dwStateTransition.value == lic.StateTransition.ST_NO_TRANSITION:
raise InvalidExpectedDataException("Waiting valid client PDU : rdpy doesn't support licensing nego") self.setNextState(self.recvDemandActivePDU)
#not tested because i can't buy RDP license server
if not (validClientPdu.errorMessage.dwErrorCode.value == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.errorMessage.dwStateTransition.value == lic.StateTransition.ST_NO_TRANSITION): elif validClientPdu.bMsgtype.value == lic.MessageType.LICENSE_REQUEST:
raise InvalidExpectedDataException("Server refuse licensing negotiation") newLicenseReq = lic.createNewLicenseRequest(validClientPdu.licensingMessage)
self._transport.send((UInt16Le(SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), newLicenseReq))
self.setNextState(self.recvDemandActivePDU) else:
raise InvalidExpectedDataException("Not a valid license packet")
def readDataPDU(self, data): def readDataPDU(self, data):
""" """
@@ -1073,9 +1088,9 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU: if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
return dataPDU return dataPDU
message = "Unknown code %s"%hex(dataPDU.pduData._value.errorInfo.value) message = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value)
if ErrorInfo._MESSAGES_.has_key(dataPDU.pduData._value.errorInfo): if ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo):
message = ErrorInfo._MESSAGES_[dataPDU.pduData._value.errorInfo] message = ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
raise ErrorReportedFromPeer("Receive PDU Error info : %s"%message) raise ErrorReportedFromPeer("Receive PDU Error info : %s"%message)
@@ -1114,7 +1129,7 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
@param data: Stream from transport layer @param data: Stream from transport layer
""" """
dataPDU = self.readDataPDU(data) dataPDU = self.readDataPDU(data)
if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or dataPDU.pduData._value.action.value != Action.CTRLACTION_COOPERATE: if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or dataPDU.pduData.action.value != Action.CTRLACTION_COOPERATE:
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU") raise InvalidExpectedDataException("Error in PDU layer automata : expected controlCooperatePDU")
self.setNextState(self.recvServerControlGrantedPDU) self.setNextState(self.recvServerControlGrantedPDU)
@@ -1124,7 +1139,7 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
@param data: Stream from transport layer @param data: Stream from transport layer
""" """
dataPDU = self.readDataPDU(data) dataPDU = self.readDataPDU(data)
if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or dataPDU.pduData._value.action.value != Action.CTRLACTION_GRANTED_CONTROL: if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_CONTROL or dataPDU.pduData.action.value != Action.CTRLACTION_GRANTED_CONTROL:
raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU") raise InvalidExpectedDataException("Error in PDU layer automata : expected controlGrantedPDU")
self.setNextState(self.recvServerFontMapPDU) self.setNextState(self.recvServerFontMapPDU)
@@ -1138,7 +1153,7 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU") raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU")
#here i'm connected #here i'm connected
self._isConnected = True self._clientListener.onReady()
self.setNextState(self.recvDataPDU) self.setNextState(self.recvDataPDU)
def recvDataPDU(self, data): def recvDataPDU(self, data):
@@ -1147,8 +1162,8 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
@param data: Stream from transport layer @param data: Stream from transport layer
""" """
dataPDU = self.readDataPDU(data) dataPDU = self.readDataPDU(data)
if dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_UPDATE and dataPDU.pduData._value.updateType.value == UpdateType.UPDATETYPE_BITMAP: if dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_UPDATE and dataPDU.pduData.updateType.value == UpdateType.UPDATETYPE_BITMAP:
self._clientListener.recvBitmapUpdateDataPDU(dataPDU.pduData._value.updateData._value.rectangles._array) self._clientListener.onUpdate(dataPDU.pduData.updateData.rectangles._array)
def recvFastPath(self, fastPathData): def recvFastPath(self, fastPathData):
""" """
@@ -1159,30 +1174,30 @@ class PDU(LayerAutomata, tpkt.FastPathListener):
fastPathPDU = FastPathUpdatePDU() fastPathPDU = FastPathUpdatePDU()
fastPathData.readType(fastPathPDU) fastPathData.readType(fastPathPDU)
if fastPathPDU.updateHeader.value == FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP: if fastPathPDU.updateHeader.value == FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
self._clientListener.recvBitmapUpdateDataPDU(fastPathPDU.updateData._value[1].rectangles._array) self._clientListener.onUpdate(fastPathPDU.updateData[1].rectangles._array)
def sendConfirmActivePDU(self): def sendConfirmActivePDU(self):
""" """
Send all client capabilities Send all client capabilities
""" """
#init general capability #init general capability
generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability._value generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
#init bitmap capability #init bitmap capability
bitmapCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability._value bitmapCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability
bitmapCapability.preferredBitsPerPixel = self._transport.getGCCClientSettings().core.highColorDepth bitmapCapability.preferredBitsPerPixel = self._transport.getGCCClientSettings().core.highColorDepth
bitmapCapability.desktopWidth = self._transport.getGCCClientSettings().core.desktopWidth bitmapCapability.desktopWidth = self._transport.getGCCClientSettings().core.desktopWidth
bitmapCapability.desktopHeight = self._transport.getGCCClientSettings().core.desktopHeight bitmapCapability.desktopHeight = self._transport.getGCCClientSettings().core.desktopHeight
#init order capability #init order capability
orderCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].capability._value orderCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].capability
orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT
#init input capability #init input capability
inputCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_INPUT].capability._value inputCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_INPUT].capability
inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE
inputCapability.keyboardLayout = self._transport.getGCCClientSettings().core.kbdLayout inputCapability.keyboardLayout = self._transport.getGCCClientSettings().core.kbdLayout
inputCapability.keyboardType = self._transport.getGCCClientSettings().core.keyboardType inputCapability.keyboardType = self._transport.getGCCClientSettings().core.keyboardType

View File

@@ -45,6 +45,9 @@ class RDPClientController(pdu.PDUClientListener):
#transport packet (protocol layer) #transport packet (protocol layer)
self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer) self._tpktLayer = tpkt.TPKT(self._tpduLayer, self._pduLayer)
#is pdu layer is ready to send
self._isReady = False
def getProtocol(self): def getProtocol(self):
""" """
@return: return Protocol layer for twisted @return: return Protocol layer for twisted
@@ -92,21 +95,30 @@ class RDPClientController(pdu.PDUClientListener):
def addClientObserver(self, observer): def addClientObserver(self, observer):
""" """
add observer to RDP protocol Add observer to RDP protocol
@param observer: new observer to add @param observer: new observer to add
""" """
self._clientObserver.append(observer) self._clientObserver.append(observer)
observer._clientListener = self observer._clientListener = self
def recvBitmapUpdateDataPDU(self, rectangles): def onUpdate(self, rectangles):
""" """
call when a bitmap data is received from update PDU Call when a bitmap data is received from update PDU
@param rectangles: [pdu.BitmapData] struct @param rectangles: [pdu.BitmapData] struct
""" """
for observer in self._clientObserver: for observer in self._clientObserver:
#for each rectangle in update PDU #for each rectangle in update PDU
for rectangle in rectangles: for rectangle in rectangles:
observer.onBitmapUpdate(rectangle.destLeft.value, rectangle.destTop.value, rectangle.destRight.value, rectangle.destBottom.value, rectangle.width.value, rectangle.height.value, rectangle.bitsPerPixel.value, rectangle.flags.value & pdu.BitmapFlag.BITMAP_COMPRESSION, rectangle.bitmapDataStream.value) observer.onUpdate(rectangle.destLeft.value, rectangle.destTop.value, rectangle.destRight.value, rectangle.destBottom.value, rectangle.width.value, rectangle.height.value, rectangle.bitsPerPixel.value, rectangle.flags.value & pdu.BitmapFlag.BITMAP_COMPRESSION, rectangle.bitmapDataStream.value)
def onReady(self):
"""
Call when PDU layer is connected
"""
self._isReady = True
#signal all listener
for observer in self._clientObserver:
observer.onReady()
def sendPointerEvent(self, x, y, button, isPressed): def sendPointerEvent(self, x, y, button, isPressed):
""" """
@@ -116,7 +128,7 @@ class RDPClientController(pdu.PDUClientListener):
@param button: 1 or 2 or 3 @param button: 1 or 2 or 3
@param isPressed: true if button is pressed or false if it's released @param isPressed: true if button is pressed or false if it's released
""" """
if not self._pduLayer._isConnected: if not self._isReady:
return return
try: try:
@@ -149,7 +161,7 @@ class RDPClientController(pdu.PDUClientListener):
@param code: scan code @param code: scan code
@param isPressed: True if key is pressed and false if it's released @param isPressed: True if key is pressed and false if it's released
""" """
if not self._pduLayer._isConnected: if not self._isReady:
return return
try: try:
@@ -172,7 +184,7 @@ class RDPClientController(pdu.PDUClientListener):
@param code: unicode @param code: unicode
@param isPressed: True if key is pressed and false if it's released @param isPressed: True if key is pressed and false if it's released
""" """
if not self._pduLayer._isConnected: if not self._isReady:
return return
try: try:
@@ -271,8 +283,13 @@ class RDPClientObserver(object):
self._controller = controller self._controller = controller
self._controller.addClientObserver(self) self._controller.addClientObserver(self)
def onReady(self):
def onBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): """
Stack is ready and connected
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPClientObserver"))
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
""" """
Notify bitmap update Notify bitmap update
@param destLeft: xmin position @param destLeft: xmin position
@@ -285,4 +302,4 @@ class RDPClientObserver(object):
@param isCompress: use RLE compression @param isCompress: use RLE compression
@param data: bitmap data @param data: bitmap data
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onBitmapUpdate", "RDPClientObserver")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "RDPClientObserver"))

View File

@@ -140,8 +140,7 @@ class TPDU(LayerAutomata, StreamSender):
def connect(self): def connect(self):
""" """
connection request Connection request for client send a connection request packet
for client send a connection request packet
""" """
if self._mode == LayerMode.CLIENT: if self._mode == LayerMode.CLIENT:
self.sendConnectionRequest() self.sendConnectionRequest()
@@ -150,9 +149,9 @@ class TPDU(LayerAutomata, StreamSender):
def recvConnectionConfirm(self, data): def recvConnectionConfirm(self, data):
""" """
receive connection confirm message Receive connection confirm message
next state is recvData Next state is recvData
call connect on presentation layer if all is good Call connect on presentation layer if all is good
@param data: Stream that contain connection confirm @param data: Stream that contain connection confirm
@see: response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx @see: response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx
@see: failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx @see: failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx
@@ -170,14 +169,16 @@ class TPDU(LayerAutomata, StreamSender):
self._selectedProtocol = message.protocolNeg.selectedProtocol.value self._selectedProtocol = message.protocolNeg.selectedProtocol.value
if self._selectedProtocol != Protocols.PROTOCOL_SSL: if self._selectedProtocol != Protocols.PROTOCOL_SSL:
raise InvalidExpectedDataException("only ssl protocol is supported in RDPY version") raise InvalidExpectedDataException("only SSL protocol is supported in RDPY version")
#_transport is TPKT and transport is TCP layer of twisted #_transport is TPKT and transport is TCP layer of twisted
self._transport.transport.startTLS(ClientTLSContext()) self._transport.transport.startTLS(ClientTLSContext())
#now i'm ready to receive data
self.setNextState(self.recvData) self.setNextState(self.recvData)
#connection is done send to presentation #connection is done send to presentation
self._presentation.connect(self) self._presentation.connect()
def recvConnectionRequest(self, data): def recvConnectionRequest(self, data):
""" """

View File

@@ -162,7 +162,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
""" """
self._controller.sendKeyEventUnicode(ord(unicode(e.text().toUtf8(), encoding="UTF-8")), isPressed) self._controller.sendKeyEventUnicode(ord(unicode(e.text().toUtf8(), encoding="UTF-8")), isPressed)
def onBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
""" """
Notify bitmap update Notify bitmap update
@param destLeft: xmin position @param destLeft: xmin position
@@ -207,7 +207,18 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
print "Receive image in bad format" print "Receive image in bad format"
return return
#if image need to be cut
#For bit alignement server may send more than image pixel
if width != destRight - destLeft + 1 or height != destBottom - destTop + 1:
image = image.copy(0, 0, destRight - destLeft + 1, destBottom - destTop + 1)
self._widget.notifyImage(destLeft, destTop, image) self._widget.notifyImage(destLeft, destTop, image)
def onReady(self):
"""
Call when stack is ready
"""
#do something maybe a loader
pass
class QRemoteDesktop(QtGui.QWidget): class QRemoteDesktop(QtGui.QWidget):
@@ -225,7 +236,7 @@ class QRemoteDesktop(QtGui.QWidget):
#because we can update image only in paint #because we can update image only in paint
#event function. When protocol receive image #event function. When protocol receive image
#we will stock into refresh list #we will stock into refresh list
#and in paiont event paint list of all refresh images #and in paint event paint list of all refresh images
self._refresh = [] self._refresh = []
#bind mouse event #bind mouse event
self.setMouseTracking(True) self.setMouseTracking(True)