From 5af9f0708abd82078f88a14e78cee582906e0f61 Mon Sep 17 00:00:00 2001 From: speyrefitte Date: Fri, 28 Nov 2014 17:54:49 +0100 Subject: [PATCH] bug fix on gui + add license neg (almost) --- rdpy/protocol/rdp/pdu/data.py | 2 +- rdpy/protocol/rdp/pdu/layer.py | 22 ++--- rdpy/protocol/rdp/pdu/lic.py | 175 ++++++++++++++++++++++++++------- rdpy/protocol/rdp/rc4.py | 56 +++++++++++ rdpy/protocol/rdp/rdp.py | 118 ++++++++++++++-------- rdpy/protocol/rdp/sec.py | 45 ++++++++- rdpy/ui/qt4.py | 98 +++++++++++------- 7 files changed, 389 insertions(+), 127 deletions(-) create mode 100644 rdpy/protocol/rdp/rc4.py diff --git a/rdpy/protocol/rdp/pdu/data.py b/rdpy/protocol/rdp/pdu/data.py index 8435fc6..d88e28f 100644 --- a/rdpy/protocol/rdp/pdu/data.py +++ b/rdpy/protocol/rdp/pdu/data.py @@ -930,7 +930,7 @@ class OrderUpdateDataPDU(CompositeType): self.pad2OctetsA = UInt16Le() self.numberOrders = UInt16Le(lambda:len(self.orderData._array)) self.pad2OctetsB = UInt16Le() - self.orderData = ArrayType(order.DrawingOrder, readLen = self.numberOrders) + self.orderData = ArrayType(order.PrimaryDrawingOrder, readLen = self.numberOrders) class BitmapCompressedDataHeader(CompositeType): """ diff --git a/rdpy/protocol/rdp/pdu/layer.py b/rdpy/protocol/rdp/pdu/layer.py index bcafdbc..c340109 100644 --- a/rdpy/protocol/rdp/pdu/layer.py +++ b/rdpy/protocol/rdp/pdu/layer.py @@ -136,6 +136,8 @@ class Client(PDULayer, tpkt.IFastPathListener): self._listener = listener #enable or not fast path self._fastPathSender = None + #todo generate hostname + self._licenceManager = lic.LicenseManager(self, self._info.userName.value, "wav-glw-009") def connect(self): """ @@ -177,18 +179,9 @@ class Client(PDULayer, tpkt.IFastPathListener): if not (securityFlag.value & data.SecurityFlag.SEC_LICENSE_PKT): raise InvalidExpectedDataException("Waiting license packet") - validClientPdu = lic.LicPacket() - s.readType(validClientPdu) - - 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: + if self._licenceManager.recv(s): self.setNextState(self.recvDemandActivePDU) - #not tested because i can't buy RDP license server - elif validClientPdu.bMsgtype.value == lic.MessageType.LICENSE_REQUEST: - newLicenseReq = lic.createNewLicenseRequest(validClientPdu.licensingMessage) - self._transport.send((UInt16Le(data.SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), newLicenseReq)) - else: - raise InvalidExpectedDataException("Not a valid license packet") - + def recvDemandActivePDU(self, s): """ Receive demand active PDU which contains @@ -343,6 +336,13 @@ class Client(PDULayer, tpkt.IFastPathListener): """ self._transport.send((UInt16Le(data.SecurityFlag.SEC_INFO_PKT), UInt16Le(), self._info)) + def sendLicensePacket(self, licPkt): + """ + @summary: send license packet + @param licPktr: license packet + """ + self._transport.send((UInt16Le(data.SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), licPkt)) + def sendConfirmActivePDU(self): """ Send all client capabilities diff --git a/rdpy/protocol/rdp/pdu/lic.py b/rdpy/protocol/rdp/pdu/lic.py index 24bc4c3..2a81bef 100644 --- a/rdpy/protocol/rdp/pdu/lic.py +++ b/rdpy/protocol/rdp/pdu/lic.py @@ -18,17 +18,20 @@ # """ -RDP extended license +@summary: RDP extended license @see: http://msdn.microsoft.com/en-us/library/cc241880.aspx """ -from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType +from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType,\ + Stream from rdpy.base.error import InvalidExpectedDataException import rdpy.base.log as log +import rdpy.protocol.rdp.sec as sec +import rdpy.protocol.rdp.rc4 as rc4 class MessageType(object): """ - License packet message type + @summary: License packet message type """ LICENSE_REQUEST = 0x01 PLATFORM_CHALLENGE = 0x02 @@ -42,7 +45,7 @@ class MessageType(object): class ErrorCode(object): """ - License error message code + @summary: License error message code @see: http://msdn.microsoft.com/en-us/library/cc240482.aspx """ ERR_INVALID_SERVER_CERTIFICATE = 0x00000001 @@ -57,7 +60,7 @@ class ErrorCode(object): class StateTransition(object): """ - Automata state transition + @summary: Automata state transition @see: http://msdn.microsoft.com/en-us/library/cc240482.aspx """ ST_TOTAL_ABORT = 0x00000001 @@ -67,9 +70,10 @@ class StateTransition(object): class BinaryBlobType(object): """ - Binary blob data type + @summary: Binary blob data type @see: http://msdn.microsoft.com/en-us/library/cc240481.aspx """ + BB_ANY_BLOB = 0x0000 BB_DATA_BLOB = 0x0001 BB_RANDOM_BLOB = 0x0002 BB_CERTIFICATE_BLOB = 0x0003 @@ -82,25 +86,25 @@ class BinaryBlobType(object): class Preambule(object): """ - Preambule version + @summary: Preambule version """ PREAMBLE_VERSION_2_0 = 0x2 PREAMBLE_VERSION_3_0 = 0x3 class LicenseBinaryBlob(CompositeType): """ - Blob use by license manager to exchange security data + @summary: Blob use by license manager to exchange security data @see: http://msdn.microsoft.com/en-us/library/cc240481.aspx """ - def __init__(self, blobType = 0): + def __init__(self, blobType = BinaryBlobType.BB_ANY_BLOB): CompositeType.__init__(self) - self.wBlobType = UInt16Le(blobType, constant = True) + self.wBlobType = UInt16Le(blobType, constant = True if blobType != BinaryBlobType.BB_ANY_BLOB else False) self.wBlobLen = UInt16Le(lambda:sizeof(self.blobData)) self.blobData = String(readLen = self.wBlobLen) class LicensingErrorMessage(CompositeType): """ - License error message + @summary: License error message @see: http://msdn.microsoft.com/en-us/library/cc240482.aspx """ _MESSAGE_TYPE_ = MessageType.ERROR_ALERT @@ -113,7 +117,7 @@ class LicensingErrorMessage(CompositeType): class ProductInformation(CompositeType): """ - License server product information + @summary: License server product information @see: http://msdn.microsoft.com/en-us/library/cc241915.aspx """ def __init__(self): @@ -121,15 +125,15 @@ class ProductInformation(CompositeType): self.dwVersion = UInt32Le() self.cbCompanyName = UInt32Le(lambda:sizeof(self.pbCompanyName)) #may contain "Microsoft Corporation" from server microsoft - self.pbCompanyName = String(readLen = self.cbCompanyName) + self.pbCompanyName = String(readLen = self.cbCompanyName, unicode = True) self.cbProductId = UInt32Le(lambda:sizeof(self.pbProductId)) #may contain "A02" from microsoft license server - self.pbProductId = String(readLen = self.cbProductId) + self.pbProductId = String(readLen = self.cbProductId, unicode = True) class Scope(CompositeType): """ - Use in license nego + @summary: Use in license nego @see: http://msdn.microsoft.com/en-us/library/cc241917.aspx """ def __init__(self): @@ -138,7 +142,7 @@ class Scope(CompositeType): class ScopeList(CompositeType): """ - Use in license nego + @summary: Use in license nego @see: http://msdn.microsoft.com/en-us/library/cc241916.aspx """ def __init__(self): @@ -148,8 +152,8 @@ class ScopeList(CompositeType): class ServerLicenseRequest(CompositeType): """ - Send by server to signal license request - server -> client + @summary: Send by server to signal license request + server -> client @see: http://msdn.microsoft.com/en-us/library/cc241914.aspx """ _MESSAGE_TYPE_ = MessageType.LICENSE_REQUEST @@ -164,8 +168,8 @@ class ServerLicenseRequest(CompositeType): class ClientNewLicenseRequest(CompositeType): """ - Send by client to ask new license for client. - RDPY doesn'support license reuse, need it in futur version + @summary: 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 """ _MESSAGE_TYPE_ = MessageType.NEW_LICENSE_REQUEST @@ -181,6 +185,32 @@ class ClientNewLicenseRequest(CompositeType): 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 ServerPlatformChallenge(CompositeType): + """ + @summary: challenge send from server to client + @see: http://msdn.microsoft.com/en-us/library/cc241921.aspx + """ + _MESSAGE_TYPE_ = MessageType.PLATFORM_CHALLENGE + + def __init__(self): + CompositeType.__init__(self) + self.connectFlags = UInt32Le() + self.encryptedPlatformChallenge = LicenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB) + self.MACData = String(readLen = UInt8(16)) + +class ClientPLatformChallengeResponse(CompositeType): + """ + @summary: client challenge response + @see: http://msdn.microsoft.com/en-us/library/cc241922.aspx + """ + _MESSAGE_TYPE_ = MessageType.PLATFORM_CHALLENGE_RESPONSE + + def __init__(self): + CompositeType.__init__(self) + self.encryptedPlatformChallengeResponse = LicenseBinaryBlob(BinaryBlobType.BB_ENCRYPTED_DATA_BLOB) + self.encryptedHWID = LicenseBinaryBlob(BinaryBlobType.BB_ENCRYPTED_DATA_BLOB) + self.MACData = String(readLen = UInt8(16)) class LicPacket(CompositeType): """ @@ -198,7 +228,7 @@ class LicPacket(CompositeType): factory for message nego Use in read mode """ - for c in [LicensingErrorMessage, ServerLicenseRequest, ClientNewLicenseRequest]: + for c in [LicensingErrorMessage, ServerLicenseRequest, ClientNewLicenseRequest, ServerPlatformChallenge, ClientPLatformChallengeResponse]: if self.bMsgtype.value == c._MESSAGE_TYPE_: return c() log.debug("unknown license message : %s"%self.bMsgtype.value) @@ -210,23 +240,94 @@ class LicPacket(CompositeType): raise InvalidExpectedDataException("Try to send an invalid license message") self.licensingMessage = message - + def createValidClientLicensingErrorMessage(): + """ + @summary: Create a licensing error message that accept client + server automata message + """ + message = LicensingErrorMessage() + message.dwErrorCode.value = ErrorCode.STATUS_VALID_CLIENT + message.dwStateTransition.value = StateTransition.ST_NO_TRANSITION + return LicPacket(message = message) + +class LicenseManager(object): """ - Create a licensing error message that accept client - server automata message + @summary: handle license automata + @see: http://msdn.microsoft.com/en-us/library/cc241890.aspx """ - message = LicensingErrorMessage() - message.dwErrorCode.value = ErrorCode.STATUS_VALID_CLIENT - message.dwStateTransition.value = StateTransition.ST_NO_TRANSITION - return LicPacket(message = 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 - """ - message = ClientNewLicenseRequest() + def __init__(self, transport, username, hostname): + """ + @param transport: layer use to send packet + """ + self._clientRandom = "\x00" * 32 + self._serverRandom = None + self._serverEncryptedChallenge = None + self._transport = transport + self._username = username + self._hostname = hostname + + def generateKeys(self): + """ + @summary: generate key for license session + """ + self._masterSecret = sec.generateMicrosoftKey("\x00" * 64, self._clientRandom, self._serverRandom) + self._sessionKeyBlob = sec.generateMicrosoftKey(self._masterSecret, self._serverRandom, self._clientRandom) + self._macSalt = self._sessionKeyBlob[:16] + self._licenseKey = sec.md5_16_32_32(self._sessionKeyBlob[16:], self._clientRandom, self._serverRandom) + + def recv(self, s): + """ + @summary: receive license packet from PDU layer + @return true when license automata is finish + """ + licPacket = LicPacket() + s.readType(licPacket) + + #end of automata + if licPacket.bMsgtype.value == MessageType.ERROR_ALERT and licPacket.licensingMessage.dwErrorCode.value == ErrorCode.STATUS_VALID_CLIENT and licPacket.licensingMessage.dwStateTransition.value == StateTransition.ST_NO_TRANSITION: + return True + + elif licPacket.bMsgtype.value == MessageType.LICENSE_REQUEST: + self._serverRandom = licPacket.licensingMessage.serverRandom.value + self.generateKeys() + self.sendClientNewLicenseRequest() + + elif licPacket.bMsgtype.value == MessageType.PLATFORM_CHALLENGE: + self._serverEncryptedChallenge = licPacket.licensingMessage.encryptedPlatformChallenge.blobData.value + self.sendClientChallengeResponse() + + else: + raise InvalidExpectedDataException("Not a valid license packet") + - return LicPacket(message) \ No newline at end of file + def sendClientNewLicenseRequest(self): + """ + @summary: 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 + """ + message = ClientNewLicenseRequest() + message.clientRandom.value = self._clientRandom + message.encryptedPreMasterSecret.blobData = String("\x00" * (64 + 8)) + message.ClientMachineName.blobData = String(self._hostname + "\x00") + message.ClientUserName.blobData = String(self._username + "\x00") + self._transport.sendLicensePacket(LicPacket(message)) + + def sendClientChallengeResponse(self): + #it should be TEST in unicode format + serverChallenge = rc4.crypt(self._licenseKey, self._serverEncryptedChallenge) + + #generate hwid + s = Stream() + s.writeType((UInt32Le(2), String(self._username + self._hostname + "\x00" * 20))) + hwid = s.getvalue()[:20] + + signature = sec.macData(self._macSalt, serverChallenge + hwid) + + message = ClientPLatformChallengeResponse() + message.encryptedPlatformChallengeResponse.blobData.value = self._serverEncryptedChallenge + message.encryptedHWID.blobData.value = rc4.crypt(self._licenseKey, hwid) + message.MACData.value = signature + + self._transport.sendLicensePacket(LicPacket(message)) \ No newline at end of file diff --git a/rdpy/protocol/rdp/rc4.py b/rdpy/protocol/rdp/rc4.py new file mode 100644 index 0000000..476bfa3 --- /dev/null +++ b/rdpy/protocol/rdp/rc4.py @@ -0,0 +1,56 @@ +""" + Copyright (C) 2012 Bo Zhu http://about.bozhu.me + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +""" + + +def KSA(key): + keylength = len(key) + + S = range(256) + + j = 0 + for i in range(256): + j = (j + S[i] + key[i % keylength]) % 256 + S[i], S[j] = S[j], S[i] # swap + + return S + + +def PRGA(S): + i = 0 + j = 0 + while True: + i = (i + 1) % 256 + j = (j + S[i]) % 256 + S[i], S[j] = S[j], S[i] # swap + + K = S[(S[i] + S[j]) % 256] + yield K + + +def RC4(key): + S = KSA(key) + return PRGA(S) + +def crypt(key, plaintext): + keystream = RC4([ord(c) for c in key]) + + return "".join([chr(ord(c) ^ keystream.next()) for c in plaintext]) \ No newline at end of file diff --git a/rdpy/protocol/rdp/rdp.py b/rdpy/protocol/rdp/rdp.py index 657e68e..2576011 100644 --- a/rdpy/protocol/rdp/rdp.py +++ b/rdpy/protocol/rdp/rdp.py @@ -68,13 +68,13 @@ class RDPClientController(pdu.layer.PDUClientListener): def setPerformanceSession(self): """ - Set particular flag in RDP stack to avoid wall-paper, theme, menu animation etc... + @summary: Set particular flag in RDP stack to avoid wall-paper, theme, menu animation etc... """ self._pduLayer._info.extendedInfo.performanceFlags.value = pdu.data.PerfFlag.PERF_DISABLE_WALLPAPER | pdu.data.PerfFlag.PERF_DISABLE_MENUANIMATIONS | pdu.data.PerfFlag.PERF_DISABLE_CURSOR_SHADOW | pdu.data.PerfFlag.PERF_DISABLE_THEMING | pdu.data.PerfFlag.PERF_DISABLE_FULLWINDOWDRAG def setScreen(self, width, height): """ - Set screen dim of session + @summary: Set screen dim of session @param width: width in pixel of screen @param height: height in pixel of screen """ @@ -84,7 +84,7 @@ class RDPClientController(pdu.layer.PDUClientListener): def setUsername(self, username): """ - Set the username for session + @summary: Set the username for session @param username: username of session """ #username in PDU info packet @@ -92,7 +92,7 @@ class RDPClientController(pdu.layer.PDUClientListener): def setPassword(self, password): """ - Set password for session + @summary: Set password for session @param password: password of session """ self.setAutologon() @@ -100,7 +100,7 @@ class RDPClientController(pdu.layer.PDUClientListener): def setDomain(self, domain): """ - Set the windows domain of session + @summary: Set the windows domain of session @param domain: domain of session """ self._pduLayer._info.domain.value = domain @@ -113,14 +113,14 @@ class RDPClientController(pdu.layer.PDUClientListener): def addClientObserver(self, observer): """ - Add observer to RDP protocol + @summary: Add observer to RDP protocol @param observer: new observer to add """ self._clientObserver.append(observer) def removeClientObserver(self, observer): """ - Remove observer to RDP protocol stack + @summary: Remove observer to RDP protocol stack @param observer: observer to remove """ for i in range(0, len(self._clientObserver)): @@ -130,7 +130,7 @@ class RDPClientController(pdu.layer.PDUClientListener): def onUpdate(self, rectangles): """ - Call when a bitmap data is received from update PDU + @summary: Call when a bitmap data is received from update PDU @param rectangles: [pdu.BitmapData] struct """ for observer in self._clientObserver: @@ -140,7 +140,7 @@ class RDPClientController(pdu.layer.PDUClientListener): def onReady(self): """ - Call when PDU layer is connected + @summary: Call when PDU layer is connected """ self._isReady = True #signal all listener @@ -149,7 +149,7 @@ class RDPClientController(pdu.layer.PDUClientListener): def onClose(self): """ - Event call when RDP stack is closed + @summary: Event call when RDP stack is closed """ self._isReady = False for observer in self._clientObserver: @@ -157,7 +157,7 @@ class RDPClientController(pdu.layer.PDUClientListener): def sendPointerEvent(self, x, y, button, isPressed): """ - send pointer events + @summary: send pointer events @param x: x position of pointer @param y: y position of pointer @param button: 1 or 2 or 3 @@ -189,10 +189,44 @@ class RDPClientController(pdu.layer.PDUClientListener): except InvalidValue: log.info("try send pointer event with incorrect position") + + def sendWheelEvent(self, x, y, step, isNegative = False, isHorizontal = False): + """ + @summary: Send a mouse wheel event + @param x: x position of pointer + @param y: y position of pointer + @param step: number of step rolled + @param isHorizontal: horizontal wheel (default is vertical) + @param isNegative: is upper (default down) + """ + if not self._isReady: + return + + try: + event = pdu.data.PointerEvent() + if isHorizontal: + event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_HWHEEL + else: + event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL + + if isNegative: + event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL_NEGATIVE + + event.pointerFlags.value |= (step & pdu.data.PointerFlag.WheelRotationMask) + + #position + event.xPos.value = x + event.yPos.value = y + + #send proper event + self._pduLayer.sendInputEvents([event]) + + except InvalidValue: + log.info("try send wheel event with incorrect position") def sendKeyEventScancode(self, code, isPressed): """ - Send a scan code to RDP stack + @summary: Send a scan code to RDP stack @param code: scan code @param isPressed: True if key is pressed and false if it's released """ @@ -215,7 +249,7 @@ class RDPClientController(pdu.layer.PDUClientListener): def sendKeyEventUnicode(self, code, isPressed): """ - Send a scan code to RDP stack + @summary: Send a scan code to RDP stack @param code: unicode @param isPressed: True if key is pressed and false if it's released """ @@ -236,7 +270,7 @@ class RDPClientController(pdu.layer.PDUClientListener): def sendRefreshOrder(self, left, top, right, bottom): """ - Force server to resend a particular zone + @summary: Force server to resend a particular zone @param left: left coordinate @param top: top coordinate @param right: right coordinate @@ -253,13 +287,13 @@ class RDPClientController(pdu.layer.PDUClientListener): def close(self): """ - Close protocol stack + @summary: Close protocol stack """ self._pduLayer.close() class RDPServerController(pdu.layer.PDUServerListener): """ - Controller use in server side mode + @summary: Controller use in server side mode """ def __init__(self, privateKeyFileName, certificateFileName, colorDepth): """ @@ -283,7 +317,7 @@ class RDPServerController(pdu.layer.PDUServerListener): def close(self): """ - Close protocol stack + @summary: Close protocol stack """ self._pduLayer.close() @@ -296,28 +330,28 @@ class RDPServerController(pdu.layer.PDUServerListener): def getUsername(self): """ - Must be call after on ready event else always empty string + @summary: Must be call after on ready event else always empty string @return: username send by client may be an empty string """ return self._pduLayer._info.userName.value def getPassword(self): """ - Must be call after on ready event else always empty string + @summary: Must be call after on ready event else always empty string @return: password send by client may be an empty string """ return self._pduLayer._info.password.value def getDomain(self): """ - Must be call after on ready event else always empty string + @summary: Must be call after on ready event else always empty string @return: domain send by client may be an empty string """ return self._pduLayer._info.domain.value def getCredentials(self): """ - Must be call after on ready event else always empty string + @summary: Must be call after on ready event else always empty string @return: tuple(domain, username, password) """ return (self.getDomain(), self.getUsername(), self.getPassword()) @@ -337,15 +371,15 @@ class RDPServerController(pdu.layer.PDUServerListener): def addServerObserver(self, observer): """ - Add observer to RDP protocol + @summary: Add observer to RDP protocol @param observer: new observer to add """ self._serverObserver.append(observer) def setColorDepth(self, colorDepth): """ - Set color depth of session - if PDU stack is already connected send a deactive-reactive sequence + @summary: Set color depth of session + if PDU stack is already connected send a deactive-reactive sequence @param colorDepth: depth of session (15, 16, 24) """ self._colorDepth = colorDepth @@ -357,13 +391,13 @@ class RDPServerController(pdu.layer.PDUServerListener): def setKeyEventUnicodeSupport(self): """ - Enable key event in unicode format + @summary: Enable key event in unicode format """ self._pduLayer._serverCapabilities[pdu.caps.CapsType.CAPSTYPE_INPUT].capability.inputFlags.value |= pdu.caps.InputFlags.INPUT_FLAG_UNICODE def onReady(self): """ - RDP stack is now ready + @summary: RDP stack is now ready """ self._isReady = True for observer in self._serverObserver: @@ -371,7 +405,7 @@ class RDPServerController(pdu.layer.PDUServerListener): def onClose(self): """ - Event call when RDP stack is closed + @summary: Event call when RDP stack is closed """ self._isReady = False for observer in self._serverObserver: @@ -379,7 +413,7 @@ class RDPServerController(pdu.layer.PDUServerListener): def onSlowPathInput(self, slowPathInputEvents): """ - Event call when slow path input are available + @summary: Event call when slow path input are available @param slowPathInputEvents: [data.SlowPathInputEvent] """ for observer in self._serverObserver: @@ -404,7 +438,7 @@ class RDPServerController(pdu.layer.PDUServerListener): def sendUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): """ - send bitmap update + @summary: send bitmap update @param destLeft: xmin position @param destTop: ymin position @param destRight: xmax position because RDP can send bitmap with padding @@ -454,7 +488,7 @@ class ClientFactory(layer.RawLayerClientFactory): class ServerFactory(layer.RawLayerServerFactory): """ - Factory of Server RDP protocol + @summary: Factory of Server RDP protocol """ def __init__(self, privateKeyFileName, certificateFileName, colorDepth): """ @@ -476,7 +510,7 @@ class ServerFactory(layer.RawLayerServerFactory): def buildRawLayer(self, addr): """ - Function call from twisted and build rdp protocol stack + @summary: Function call from twisted and build rdp protocol stack @param addr: destination address """ controller = RDPServerController(self._privateKeyFileName, self._certificateFileName, self._colorDepth) @@ -485,7 +519,7 @@ class ServerFactory(layer.RawLayerServerFactory): def buildObserver(self, controller, addr): """ - Build observer use for connection + @summary: Build observer use for connection @param controller: RDP stack controller @param addr: destination address """ @@ -493,7 +527,7 @@ class ServerFactory(layer.RawLayerServerFactory): class RDPClientObserver(object): """ - Class use to inform all RDP event handle by RDPY + @summary: Class use to inform all RDP event handle by RDPY """ def __init__(self, controller): """ @@ -504,19 +538,19 @@ class RDPClientObserver(object): def onReady(self): """ - Stack is ready and connected + @summary: Stack is ready and connected """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPClientObserver")) def onClose(self): """ - Stack is closes + @summary: Stack is closes """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onClose", "RDPClientObserver")) def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): """ - Notify bitmap update + @summary: Notify bitmap update @param destLeft: xmin position @param destTop: ymin position @param destRight: xmax position because RDP can send bitmap with padding @@ -531,7 +565,7 @@ class RDPClientObserver(object): class RDPServerObserver(object): """ - Class use to inform all RDP event handle by RDPY + @summary: Class use to inform all RDP event handle by RDPY """ def __init__(self, controller): """ @@ -542,20 +576,20 @@ class RDPServerObserver(object): def onReady(self): """ - Stack is ready and connected + @summary: Stack is ready and connected May be called after an setColorDepth too """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPServerObserver")) def onClose(self): """ - Stack is closes + @summary: Stack is closes """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onClose", "RDPClientObserver")) def onKeyEventScancode(self, code, isPressed): """ - Event call when a keyboard event is catch in scan code format + @summary: Event call when a keyboard event is catch in scan code format @param code: scan code of key @param isPressed: True if key is down """ @@ -563,7 +597,7 @@ class RDPServerObserver(object): def onKeyEventUnicode(self, code, isPressed): """ - Event call when a keyboard event is catch in unicode format + @summary: Event call when a keyboard event is catch in unicode format @param code: unicode of key @param isPressed: True if key is down """ @@ -571,7 +605,7 @@ class RDPServerObserver(object): def onPointerEvent(self, x, y, button, isPressed): """ - Event call on mouse event + @summary: Event call on mouse event @param x: x position @param y: y position @param button: 1, 2 or 3 button diff --git a/rdpy/protocol/rdp/sec.py b/rdpy/protocol/rdp/sec.py index 60e06c2..a601901 100644 --- a/rdpy/protocol/rdp/sec.py +++ b/rdpy/protocol/rdp/sec.py @@ -22,6 +22,7 @@ Some use full methods for security in RDP """ import sha, md5 +from rdpy.network.type import Stream, UInt32Le def saltedHash(inputData, salt, salt1, salt2): """ @@ -47,6 +48,46 @@ def saltedHash(inputData, salt, salt1, salt2): return md5Digest.digest() -def masterSecret(preMasterSecret, clientRandom, serverRandom): +def md5_16_32_32(in0, in1, in2): """ - """ \ No newline at end of file + @summary: MD5(in0[:16] + in1[:32] + in2[:32]) + @param in0: in 16 + @param in1: in 32 + @param in2: in 32 + @return MD5(in0[:16] + in1[:32] + in2[:32]) + """ + md5Digest = md5.new() + md5Digest.update(in0[:16]) + md5Digest.update(in1[:32]) + md5Digest.update(in2[:32]) + return md5Digest.digest() + +def generateMicrosoftKey(secret, random1, random2): + """ + @summary: Generate master secret + @param secret: secret + @param clientRandom : client random + @param serverRandom : server random + """ + return saltedHash("A", secret, random1, random2) + saltedHash("BB", secret, random1, random2) + saltedHash("CCC", secret, random1, random2) + +def macData(macSaltKey, data): + sha1Digest = sha.new() + md5Digest = md5.new() + + #encode length + s = Stream() + s.writeType(UInt32Le(len(data))) + + sha1Digest.update(macSaltKey) + sha1Digest.update("\x36" * 40) + sha1Digest.update(s.getvalue()) + sha1Digest.update(data) + + sha1Sig = sha1Digest.digest() + + md5Digest.update(macSaltKey) + md5Digest.update("\x5c" * 48) + md5Digest.update(sha1Sig) + + return md5Digest.digest() \ No newline at end of file diff --git a/rdpy/ui/qt4.py b/rdpy/ui/qt4.py index 041e9e2..9f357fb 100644 --- a/rdpy/ui/qt4.py +++ b/rdpy/ui/qt4.py @@ -34,12 +34,12 @@ import rle class QAdaptor(object): """ - Adaptor model with link between protocol - And Qt widget + @summary: Adaptor model with link between protocol + And Qt widget """ def sendMouseEvent(self, e, isPressed): """ - Interface to send mouse event to protocol stack + @summary: Interface to send mouse event to protocol stack @param e: QMouseEvent @param isPressed: event come from press or release action """ @@ -47,11 +47,18 @@ class QAdaptor(object): def sendKeyEvent(self, e, isPressed): """ - Interface to send key event to protocol stack + @summary: Interface to send key event to protocol stack @param e: QEvent @param isPressed: event come from press or release action """ - raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendKeyEvent", "QAdaptor")) + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendKeyEvent", "QAdaptor")) + + def sendWheelEvent(self, e): + """ + @summary: Interface to send wheel event to protocol stack + @param e: QWheelEvent + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendWheelEvent", "QAdaptor")) def getWidget(self): """ @@ -61,7 +68,7 @@ class QAdaptor(object): def closeEvent(self, e): """ - Call when you want to close connection + @summary: Call when you want to close connection @param: QCloseEvent """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "closeEvent", "QAdaptor")) @@ -77,7 +84,7 @@ def qtImageFormatFromRFBPixelFormat(pixelFormat): class RFBClientQt(RFBClientObserver, QAdaptor): """ - QAdaptor for specific RFB protocol stack + @summary: QAdaptor for specific RFB protocol stack is to an RFB observer """ def __init__(self, controller): @@ -97,7 +104,7 @@ class RFBClientQt(RFBClientObserver, QAdaptor): def onUpdate(self, width, height, x, y, pixelFormat, encoding, data): """ - Implement RFBClientObserver interface + @summary: Implement RFBClientObserver interface @param width: width of new image @param height: height of new image @param x: x position of new image @@ -136,7 +143,7 @@ class RFBClientQt(RFBClientObserver, QAdaptor): def sendMouseEvent(self, e, isPressed): """ - Convert Qt mouse event to RFB mouse event + @summary: Convert Qt mouse event to RFB mouse event @param e: qMouseEvent @param isPressed: event come from press or release action """ @@ -152,29 +159,37 @@ class RFBClientQt(RFBClientObserver, QAdaptor): def sendKeyEvent(self, e, isPressed): """ - Convert Qt key press event to RFB press event + @summary: Convert Qt key press event to RFB press event @param e: qKeyEvent @param isPressed: event come from press or release action """ self.keyEvent(isPressed, e.nativeVirtualKey()) + def sendWheelEvent(self, e): + """ + @summary: Convert Qt wheel event to RFB Wheel event + @param e: QKeyEvent + @param isPressed: event come from press or release action + """ + pass + def closeEvent(self, e): """ - Call when you want to close connection + @summary: Call when you want to close connection @param: QCloseEvent """ self._controller.close() def onClose(self): """ - Call when stack is close + @summary: Call when stack is close """ #do something maybe a message pass def RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data): """ - Bitmap transformation to Qt object + @summary: Bitmap transformation to Qt object @param width: width of bitmap @param height: height of bitmap @param bitsPerPixel: number of bit per pixel @@ -204,15 +219,15 @@ def RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data): if isCompress: buf = bytearray(width * height * 3) rle.bitmap_decompress(buf, width, height, data, 3) - image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB24) + image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB888) else: - image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB24).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)) + image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB888).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)) elif bitsPerPixel == 32: if isCompress: buf = bytearray(width * height * 4) rle.bitmap_decompress(buf, width, height, data, 4) - image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB24) + image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB32) else: image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB32).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)) else: @@ -222,7 +237,7 @@ def RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data): class RDPClientQt(RDPClientObserver, QAdaptor): """ - Adaptor for RDP client + @summary: Adaptor for RDP client """ def __init__(self, controller, width, height): """ @@ -243,7 +258,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor): def sendMouseEvent(self, e, isPressed): """ - Convert Qt mouse event to RDP mouse event + @summary: Convert Qt mouse event to RDP mouse event @param e: qMouseEvent @param isPressed: event come from press(true) or release(false) action """ @@ -251,15 +266,15 @@ class RDPClientQt(RDPClientObserver, QAdaptor): buttonNumber = 0 if button == QtCore.Qt.LeftButton: buttonNumber = 1 - elif button == QtCore.Qt.MidButton: - buttonNumber = 2 elif button == QtCore.Qt.RightButton: + buttonNumber = 2 + elif button == QtCore.Qt.MidButton: buttonNumber = 3 self._controller.sendPointerEvent(e.pos().x(), e.pos().y(), buttonNumber, isPressed) def sendKeyEvent(self, e, isPressed): """ - Convert Qt key press event to RFB press event + @summary: Convert Qt key press event to RDP press event @param e: QKeyEvent @param isPressed: event come from press or release action """ @@ -267,17 +282,25 @@ class RDPClientQt(RDPClientObserver, QAdaptor): if sys.platform == "linux2": code -= 8 self._controller.sendKeyEventScancode(code, isPressed) + + def sendWheelEvent(self, e): + """ + @summary: Convert Qt wheel event to RDP Wheel event + @param e: QKeyEvent + @param isPressed: event come from press or release action + """ + self._controller.sendWheelEvent(e.pos().x(), e.pos().y(), (abs(e.delta()) / 8) / 15, e.delta() < 0, e.orientation() == QtCore.Qt.Horizontal) def closeEvent(self, e): """ - Convert Qt close widget event into close stack event + @summary: Convert Qt close widget event into close stack event @param e: QCloseEvent """ self._controller.close() def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): """ - Notify bitmap update + @summary: Notify bitmap update @param destLeft: xmin position @param destTop: ymin position @param destRight: xmax position because RDP can send bitmap with padding @@ -295,14 +318,14 @@ class RDPClientQt(RDPClientObserver, QAdaptor): def onReady(self): """ - Call when stack is ready + @summary: Call when stack is ready """ #do something maybe a loader pass def onClose(self): """ - Call when stack is close + @summary: Call when stack is close """ #do something maybe a message pass @@ -310,7 +333,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor): class QRemoteDesktop(QtGui.QWidget): """ - Qt display widget + @summary: Qt display widget """ def __init__(self, adaptor, width, height): """ @@ -334,7 +357,7 @@ class QRemoteDesktop(QtGui.QWidget): def notifyImage(self, x, y, qimage, width, height): """ - Function call from QAdaptor + @summary: Function call from QAdaptor @param x: x position of new image @param y: y position of new image @param qimage: new QImage @@ -346,7 +369,7 @@ class QRemoteDesktop(QtGui.QWidget): def paintEvent(self, e): """ - Call when Qt renderer engine estimate that is needed + @summary: Call when Qt renderer engine estimate that is needed @param e: QEvent """ #fill buffer image @@ -362,42 +385,49 @@ class QRemoteDesktop(QtGui.QWidget): def mouseMoveEvent(self, event): """ - Call when mouse move + @summary: Call when mouse move @param event: QMouseEvent """ self._adaptor.sendMouseEvent(event, False) def mousePressEvent(self, event): """ - Call when button mouse is pressed + @summary: Call when button mouse is pressed @param event: QMouseEvent """ self._adaptor.sendMouseEvent(event, True) def mouseReleaseEvent(self, event): """ - Call when button mouse is released + @summary: Call when button mouse is released @param event: QMouseEvent """ self._adaptor.sendMouseEvent(event, False) def keyPressEvent(self, event): """ - Call when button key is pressed + @summary: Call when button key is pressed @param event: QKeyEvent """ self._adaptor.sendKeyEvent(event, True) def keyReleaseEvent(self, event): """ - Call when button key is released + @summary: Call when button key is released @param event: QKeyEvent """ self._adaptor.sendKeyEvent(event, False) + def wheelEvent(self, event): + """ + @summary: Call on wheel event + @param event: QWheelEvent + """ + self._adaptor.sendWheelEvent(event) + def closeEvent(self, event): """ - Call when widget is closed + @summary: Call when widget is closed @param event: QCloseEvent """ self._adaptor.closeEvent(event) \ No newline at end of file