17 Commits

Author SHA1 Message Date
Sylvain Peyrefitte
cef16a9f64 Merge pull request #108 from cudeso/master
Add logging for rdphoneypot
2020-04-10 21:12:15 +02:00
Koen Van Impe
9aea135fd9 Add logging 2020-04-10 21:05:18 +02:00
Sylvain Peyrefitte
4109b7a6fe Merge pull request #67 from Mutchako/patch-1
Update setup.py
2018-08-23 22:20:14 +02:00
Sylvain Peyrefitte
d3b0ae5e90 Merge pull request #80 from speidy/pointerex
onPointerEvent: handle 4, 5 mouse buttons based on INPUT_EVENT_MOUSEX event
2018-08-23 22:18:20 +02:00
Idan Freiberg
a1f9afa87a onPointerEvent: handle 4, 5 mouse buttons based on INPUT_EVENT_MOUSEX event 2018-08-06 07:52:32 +03:00
Sylvain Peyrefitte
b8ff4136b6 Merge pull request #78 from speidy/mitm
rdpy-rdpmitm: pep8 fixes, argument parsing improvements
2018-08-05 16:23:28 +02:00
Sylvain Peyrefitte
ce04150790 Merge pull request #77 from speidy/pointerex
data: add support for INPUT_EVENT_MOUSEX event
2018-08-05 16:23:03 +02:00
speidy
bcec1aad25 rdpy-rdpmitm: use argparse for argument parsing 2018-08-05 17:07:29 +03:00
speidy
c18a4c4101 rdpy-rdpmitm: apply pep8 fixes 2018-08-05 15:58:07 +03:00
speidy
3e37899ae4 data: add support for INPUT_EVENT_MOUSEX event 2018-08-05 14:28:23 +03:00
Mutchako
cd6e14e7ef Update setup.py
Syntax error on install_requires. Unnecessary comma on last statement.
2018-02-12 09:48:17 -05:00
Sylvain Peyrefitte
629d2160af Merge pull request #33 from ChrisTruncer/bin_
Small change to reference object attribute
2015-06-01 11:40:37 +02:00
Christopher Truncer
e23def3179 Small changeto reference object attribute 2015-05-28 12:08:55 -04:00
speyrefitte
a23ae25a1f fix issue on unhandle upadte 2015-05-21 10:29:32 +02:00
speyrefitte
763ed2e3ee fix major bug on update handle 2015-05-20 17:51:43 +02:00
speyrefitte
11d66a4818 add onSessionReady event -> user session is ready 2015-05-19 17:53:15 +02:00
speyrefitte
9b99365f80 fix bug on lwin key activation 2015-05-19 16:42:22 +02:00
14 changed files with 387 additions and 237 deletions

View File

@@ -182,6 +182,11 @@ class MyRDPFactory(rdp.ClientFactory):
@param data: bitmap data @param data: bitmap data
""" """
def onSessionReady(self):
"""
@summary: Windows session is ready
"""
def onClose(self): def onClose(self):
""" """
@summary: Call when stack is close @summary: Call when stack is close
@@ -231,7 +236,7 @@ class MyRDPFactory(rdp.ServerFactory):
@summary: Event call on mouse event @summary: Event call on mouse event
@param x: x position @param x: x position
@param y: y position @param y: y position
@param button: 1, 2 or 3 button @param button: 1, 2, 3, 4 or 5 button
@param isPressed: True if mouse button is pressed @param isPressed: True if mouse button is pressed
@see: rdp.RDPServerObserver.onPointerEvent @see: rdp.RDPServerObserver.onPointerEvent
""" """

View File

@@ -172,7 +172,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
connector.connect() connector.connect()
return return
QtGui.QMessageBox.warning(self._w, "Warning", "Lost connection : %s"%reason) log.info("Lost connection : %s"%reason)
reactor.stop() reactor.stop()
app.exit() app.exit()
@@ -182,7 +182,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
@param connector: twisted connector use for rdp connection (use reconnect to restart connection) @param connector: twisted connector use for rdp connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection @param reason: str use to advertise reason of lost connection
""" """
QtGui.QMessageBox.warning(self._w, "Warning", "Connection failed : %s"%reason) log.info("Connection failed : %s"%reason)
reactor.stop() reactor.stop()
app.exit() app.exit()

View File

@@ -22,7 +22,7 @@
RDP Honey pot use Rss scenario file to simulate RDP server RDP Honey pot use Rss scenario file to simulate RDP server
""" """
import sys, os, getopt, time import sys, os, getopt, time, datetime
from rdpy.core import log, error, rss from rdpy.core import log, error, rss
from rdpy.protocol.rdp import rdp from rdpy.protocol.rdp import rdp
@@ -54,23 +54,18 @@ class HoneyPotServer(rdp.RDPServerObserver):
width, height = self._controller.getScreen() width, height = self._controller.getScreen()
size = width * height size = width * height
rssFilePath = sorted(self._rssFileSizeList, key = lambda x: abs(x[0][0] * x[0][1] - size))[0][1] rssFilePath = sorted(self._rssFileSizeList, key = lambda x: abs(x[0][0] * x[0][1] - size))[0][1]
log.info("select file (%s, %s) -> %s"%(width, height, rssFilePath)) log.info("%s --- select file (%s, %s) -> %s"%(datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'),width, height, rssFilePath))
self._rssFile = rss.createReader(rssFilePath) self._rssFile = rss.createReader(rssFilePath)
domain, username, password = self._controller.getCredentials() domain, username, password = self._controller.getCredentials()
hostname = self._controller.getHostname() hostname = self._controller.getHostname()
log.info("""Credentials: log.info("""%s --- Credentials: domain: %s username: %s password: %s hostname: %s"""%(datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'), domain, username, password, hostname));
\tdomain : %s
\tusername : %s
\tpassword : %s
\thostname : %s
"""%(domain, username, password, hostname));
self.start() self.start()
def onClose(self): def onClose(self):
""" HoneyPot """ """ HoneyPot """
def onKeyEventScancode(self, code, isPressed): def onKeyEventScancode(self, code, isPressed, isExtended):
""" HoneyPot """ """ HoneyPot """
def onKeyEventUnicode(self, code, isPressed): def onKeyEventUnicode(self, code, isPressed):
@@ -125,7 +120,7 @@ class HoneyPotServerFactory(rdp.ServerFactory):
@param addr: destination address @param addr: destination address
@see: rdp.ServerFactory.buildObserver @see: rdp.ServerFactory.buildObserver
""" """
log.info("Connection from %s:%s"%(addr.host, addr.port)) log.info("%s --- Connection from %s:%s"%(datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'), addr.host, addr.port))
return HoneyPotServer(controller, self._rssFileSizeList) return HoneyPotServer(controller, self._rssFileSizeList)
def readSize(filePath): def readSize(filePath):
@@ -146,10 +141,12 @@ def help():
@summary: Print help in console @summary: Print help in console
""" """
print """ print """
Usage: rdpy-rdphoneypot.py rss_filepath(1..n) Usage: rdpy-rdphoneypot.py
[-L logfile]
[-l listen_port default 3389] [-l listen_port default 3389]
[-k private_key_file_path (mandatory for SSL)] [-k private_key_file_path (mandatory for SSL)]
[-c certificate_file_path (mandatory for SSL)] [-c certificate_file_path (mandatory for SSL)]
rss_filepath(1..n)
""" """
if __name__ == '__main__': if __name__ == '__main__':
@@ -159,13 +156,15 @@ if __name__ == '__main__':
rssFileSizeList = [] rssFileSizeList = []
try: try:
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:") opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:L:")
except getopt.GetoptError: except getopt.GetoptError:
help() help()
for opt, arg in opts: for opt, arg in opts:
if opt == "-h": if opt == "-h":
help() help()
sys.exit() sys.exit()
elif opt == "-L":
log._LOG_FILE = arg
elif opt == "-l": elif opt == "-l":
listen = arg listen = arg
elif opt == "-k": elif opt == "-k":
@@ -174,11 +173,12 @@ if __name__ == '__main__':
certificateFilePath = arg certificateFilePath = arg
#build size map #build size map
log.info("Build size map") log.info("%s --- Start rdphoneypot"%datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
log.info("%s --- Build size map"%datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
for arg in args: for arg in args:
size = readSize(arg) size = readSize(arg)
rssFileSizeList.append((size, arg)) rssFileSizeList.append((size, arg))
log.info("(%s, %s) -> %s"%(size[0], size[1], arg)) log.info("%s --- (%s, %s) -> %s"%(datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'), size[0], size[1], arg))
reactor.listenTCP(int(listen), HoneyPotServerFactory(rssFileSizeList, privateKeyFilePath, certificateFilePath)) reactor.listenTCP(int(listen), HoneyPotServerFactory(rssFileSizeList, privateKeyFilePath, certificateFilePath))
reactor.run() reactor.run()

View File

@@ -29,7 +29,10 @@ Client RDP -> | ProxyServer | ProxyClient | -> Server RDP
----------------- -----------------
""" """
import sys, os, getopt, time import sys
import os
import argparse
import time
from rdpy.core import log, error, rss from rdpy.core import log, error, rss
from rdpy.protocol.rdp import rdp from rdpy.protocol.rdp import rdp
@@ -37,10 +40,12 @@ from twisted.internet import reactor
log._LOG_LEVEL = log.Level.INFO log._LOG_LEVEL = log.Level.INFO
class ProxyServer(rdp.RDPServerObserver): class ProxyServer(rdp.RDPServerObserver):
""" """
@summary: Server side of proxy @summary: Server side of proxy
""" """
def __init__(self, controller, target, clientSecurityLevel, rssRecorder): def __init__(self, controller, target, clientSecurityLevel, rssRecorder):
""" """
@param controller: {RDPServerController} @param controller: {RDPServerController}
@@ -69,39 +74,42 @@ class ProxyServer(rdp.RDPServerObserver):
@see: rdp.RDPServerObserver.onReady @see: rdp.RDPServerObserver.onReady
""" """
if self._client is None: if self._client is None:
#try a connection # try a connection
domain, username, password = self._controller.getCredentials() domain, username, password = self._controller.getCredentials()
self._rss.credentials(username, password, domain, self._controller.getHostname()) self._rss.credentials(username, password,
domain, self._controller.getHostname())
width, height = self._controller.getScreen() width, height = self._controller.getScreen()
self._rss.screen(width, height, self._controller.getColorDepth()) self._rss.screen(width, height, self._controller.getColorDepth())
reactor.connectTCP(self._target[0], int(self._target[1]), ProxyClientFactory(self, width, height, reactor.connectTCP(self._target[0], int(self._target[1]), ProxyClientFactory(self, width, height,
domain, username, password,self._clientSecurityLevel)) domain, username, password, self._clientSecurityLevel))
def onClose(self): def onClose(self):
""" """
@summary: Call when human client close connection @summary: Call when human client close connection
@see: rdp.RDPServerObserver.onClose @see: rdp.RDPServerObserver.onClose
""" """
#end scenario # end scenario
self._rss.close() self._rss.close()
#close network stack # close network stack
if self._client is None: if self._client is None:
return return
self._client._controller.close() self._client._controller.close()
def onKeyEventScancode(self, code, isPressed): def onKeyEventScancode(self, code, isPressed, isExtended):
""" """
@summary: 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: {int} scan code of key @param code: {integer} scan code of key
@param isPressed: {bool} True if key is down @param isPressed: {boolean} True if key is down
@param isExtended: {boolean} True if a special key
@see: rdp.RDPServerObserver.onKeyEventScancode @see: rdp.RDPServerObserver.onKeyEventScancode
""" """
if self._client is None: if self._client is None:
return return
self._client._controller.sendKeyEventScancode(code, isPressed) self._client._controller.sendKeyEventScancode(
code, isPressed, isExtended)
self._rss.keyScancode(code, isPressed) self._rss.keyScancode(code, isPressed)
def onKeyEventUnicode(self, code, isPressed): def onKeyEventUnicode(self, code, isPressed):
@@ -121,7 +129,7 @@ class ProxyServer(rdp.RDPServerObserver):
@summary: Event call on mouse event @summary: Event call on mouse event
@param x: {int} x position @param x: {int} x position
@param y: {int} y position @param y: {int} y position
@param button: {int} 1, 2 or 3 button @param button: {int} 1, 2, 3, 4 or 5 button
@param isPressed: {bool} True if mouse button is pressed @param isPressed: {bool} True if mouse button is pressed
@see: rdp.RDPServerObserver.onPointerEvent @see: rdp.RDPServerObserver.onPointerEvent
""" """
@@ -129,10 +137,12 @@ class ProxyServer(rdp.RDPServerObserver):
return return
self._client._controller.sendPointerEvent(x, y, button, isPressed) self._client._controller.sendPointerEvent(x, y, button, isPressed)
class ProxyServerFactory(rdp.ServerFactory): class ProxyServerFactory(rdp.ServerFactory):
""" """
@summary: Factory on listening events @summary: Factory on listening events
""" """
def __init__(self, target, ouputDir, privateKeyFilePath, certificateFilePath, clientSecurity): def __init__(self, target, ouputDir, privateKeyFilePath, certificateFilePath, clientSecurity):
""" """
@param target: {tuple(ip, prt)} @param target: {tuple(ip, prt)}
@@ -140,11 +150,12 @@ class ProxyServerFactory(rdp.ServerFactory):
@param certificateFilePath: {str} file contain server certificate (if none -> back to standard RDP security) @param certificateFilePath: {str} file contain server certificate (if none -> back to standard RDP security)
@param clientSecurity: {str(ssl|rdp)} security layer use in client connection side @param clientSecurity: {str(ssl|rdp)} security layer use in client connection side
""" """
rdp.ServerFactory.__init__(self, 16, privateKeyFilePath, certificateFilePath) rdp.ServerFactory.__init__(
self, 16, privateKeyFilePath, certificateFilePath)
self._target = target self._target = target
self._ouputDir = ouputDir self._ouputDir = ouputDir
self._clientSecurity = clientSecurity self._clientSecurity = clientSecurity
#use produce unique file by connection # use produce unique file by connection
self._uniqueId = 0 self._uniqueId = 0
def buildObserver(self, controller, addr): def buildObserver(self, controller, addr):
@@ -154,12 +165,14 @@ class ProxyServerFactory(rdp.ServerFactory):
@see: rdp.ServerFactory.buildObserver @see: rdp.ServerFactory.buildObserver
""" """
self._uniqueId += 1 self._uniqueId += 1
return ProxyServer(controller, self._target, self._clientSecurity, rss.createRecorder(os.path.join(self._ouputDir, "%s_%s_%s.rss"%(time.strftime('%Y%m%d%H%M%S'), addr.host, self._uniqueId)))) return ProxyServer(controller, self._target, self._clientSecurity, rss.createRecorder(os.path.join(self._ouputDir, "%s_%s_%s.rss" % (time.strftime('%Y%m%d%H%M%S'), addr.host, self._uniqueId))))
class ProxyClient(rdp.RDPClientObserver): class ProxyClient(rdp.RDPClientObserver):
""" """
@summary: Client side of proxy @summary: Client side of proxy
""" """
def __init__(self, controller, server): def __init__(self, controller, server):
""" """
@param controller: {rdp.RDPClientController} @param controller: {rdp.RDPClientController}
@@ -175,15 +188,23 @@ class ProxyClient(rdp.RDPClientObserver):
@see: rdp.RDPClientObserver.onReady @see: rdp.RDPClientObserver.onReady
""" """
self._server.setClient(self) self._server.setClient(self)
#maybe color depth change # maybe color depth change
self._server._controller.setColorDepth(self._controller.getColorDepth()) self._server._controller.setColorDepth(
self._controller.getColorDepth())
def onSessionReady(self):
"""
@summary: Windows session is ready
@see: rdp.RDPClientObserver.onSessionReady
"""
pass
def onClose(self): def onClose(self):
""" """
@summary: Event inform that stack is close @summary: Event inform that stack is close
@see: rdp.RDPClientObserver.onClose @see: rdp.RDPClientObserver.onClose
""" """
#end scenario # end scenario
self._server._rss.close() self._server._rss.close()
self._server._controller.close() self._server._controller.close()
@@ -201,13 +222,17 @@ class ProxyClient(rdp.RDPClientObserver):
@param data: {str} bitmap data @param data: {str} bitmap data
@see: rdp.RDPClientObserver.onUpdate @see: rdp.RDPClientObserver.onUpdate
""" """
self._server._rss.update(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, rss.UpdateFormat.BMP if isCompress else rss.UpdateFormat.RAW, data) self._server._rss.update(destLeft, destTop, destRight, destBottom, width, height,
self._server._controller.sendUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data) bitsPerPixel, rss.UpdateFormat.BMP if isCompress else rss.UpdateFormat.RAW, data)
self._server._controller.sendUpdate(
destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data)
class ProxyClientFactory(rdp.ClientFactory): class ProxyClientFactory(rdp.ClientFactory):
""" """
@summary: Factory for proxy client @summary: Factory for proxy client
""" """
def __init__(self, server, width, height, domain, username, password, security): def __init__(self, server, width, height, domain, username, password, security):
""" """
@param server: {ProxyServer} @param server: {ProxyServer}
@@ -234,9 +259,9 @@ class ProxyClientFactory(rdp.ClientFactory):
@see: rdp.ClientFactory.buildObserver @see: rdp.ClientFactory.buildObserver
@return: ProxyClient @return: ProxyClient
""" """
#set screen resolution # set screen resolution
controller.setScreen(self._width, self._height) controller.setScreen(self._width, self._height)
#set credential # set credential
controller.setDomain(self._domain) controller.setDomain(self._domain)
controller.setUsername(self._username) controller.setUsername(self._username)
controller.setPassword(self._password) controller.setPassword(self._password)
@@ -244,59 +269,55 @@ class ProxyClientFactory(rdp.ClientFactory):
controller.setPerformanceSession() controller.setPerformanceSession()
return ProxyClient(controller, self._server) return ProxyClient(controller, self._server)
def help():
"""
@summary: Print help in console
"""
print """
Usage: rdpy-rdpmitm.py -o output_directory target
[-l listen_port default 3389]
[-k private_key_file_path (mandatory for SSL)]
[-c certificate_file_path (mandatory for SSL)]
[-o output directory for recoded files]
[-r RDP standard security (XP or server 2003 client or older)]
[-n For NLA Client authentication (need to provide credentials)]
"""
def parseIpPort(interface, defaultPort = "3389"): def parseIpPort(interface, defaultPort="3389"):
if ':' in interface: if ':' in interface:
return interface.split(':') s = interface.split(':')
return s[0], int(s[1])
else: else:
return interface, defaultPort return interface, int(defaultPort)
def isDirectory(outputDirectory):
if outputDirectory is None or not os.path.dirname(outputDirectory):
log.error("{} is an invalid output directory or directory doesn't exist".format(
outputDirectory))
return outputDirectory
def mapSecurityLayer(layer):
return {
"rdp": rdp.SecurityLevel.RDP_LEVEL_RDP,
"tls": rdp.SecurityLevel.RDP_LEVEL_SSL,
"nla": rdp.SecurityLevel.RDP_LEVEL_NLA
}[layer]
if __name__ == '__main__': if __name__ == '__main__':
listen = "3389" p = argparse.ArgumentParser(
privateKeyFilePath = None formatter_class=argparse.ArgumentDefaultsHelpFormatter)
certificateFilePath = None
ouputDirectory = None
#for anonymous authentication
clientSecurity = rdp.SecurityLevel.RDP_LEVEL_SSL
try: p.add_argument('-l', '--listen', type=parseIpPort, default="0.0.0.0:3389",
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:o:rn") help="<addr>[:<port>] to bind the server")
except getopt.GetoptError: p.add_argument('-t', '--target', type=parseIpPort, required=True,
help() help="<addr>[:<port>] of the target you want to connect to via proxy")
for opt, arg in opts: p.add_argument('-o', '--output', type=isDirectory,
if opt == "-h": help="output directory", required=True)
help() p.add_argument('-s', '--sec', choices=["rdp", "tls", "nla"],
sys.exit() default="rdp", help="set protocol security layer")
elif opt == "-l": ssl = p.add_argument_group()
listen = arg ssl.add_argument('-c', '--certificate', help="certificate for TLS connections")
elif opt == "-k": ssl.add_argument('-k', '--key', help="private key of the given certificate for TLS connections")
privateKeyFilePath = arg
elif opt == "-c":
certificateFilePath = arg
elif opt == "-o":
ouputDirectory = arg
elif opt == "-r":
clientSecurity = rdp.SecurityLevel.RDP_LEVEL_RDP
elif opt == "-n":
clientSecurity = rdp.SecurityLevel.RDP_LEVEL_NLA
if ouputDirectory is None or not os.path.dirname(ouputDirectory): args = p.parse_args()
log.error("%s is an invalid output directory"%ouputDirectory)
help() if args.certificate and args.key and not args.sec == "nla":
sys.exit() args.sec = "tls"
log.info("running server on {addr}, using {sec} security layer, proxying to {target}".format(
addr=args.listen, sec=args.sec.upper(), target=args.target))
reactor.listenTCP(args.listen[1], ProxyServerFactory(
args.target, args.output, args.key, args.certificate, mapSecurityLayer(args.sec)),
interface=args.listen[0])
reactor.listenTCP(int(listen), ProxyServerFactory(parseIpPort(args[0]), ouputDirectory, privateKeyFilePath, certificateFilePath, clientSecurity))
reactor.run() reactor.run()

View File

@@ -23,7 +23,9 @@ example of use rdpy
take screenshot of login page take screenshot of login page
""" """
import sys, os, getopt import getopt
import os
import sys
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from rdpy.protocol.rdp import rdp from rdpy.protocol.rdp import rdp
@@ -32,15 +34,17 @@ import rdpy.core.log as log
from rdpy.core.error import RDPSecurityNegoFail from rdpy.core.error import RDPSecurityNegoFail
from twisted.internet import task from twisted.internet import task
#set log level # set log level
log._LOG_LEVEL = log.Level.INFO log._LOG_LEVEL = log.Level.INFO
class RDPScreenShotFactory(rdp.ClientFactory): class RDPScreenShotFactory(rdp.ClientFactory):
""" """
@summary: Factory for screenshot exemple @summary: Factory for screenshot exemple
""" """
__INSTANCE__ = 0 __INSTANCE__ = 0
__STATE__ = [] __STATE__ = []
def __init__(self, reactor, app, width, height, path, timeout): def __init__(self, reactor, app, width, height, path, timeout):
""" """
@param reactor: twisted reactor @param reactor: twisted reactor
@@ -71,7 +75,7 @@ class RDPScreenShotFactory(rdp.ClientFactory):
connector.connect() connector.connect()
return return
log.info("connection lost : %s"%reason) log.info("connection lost : %s" % reason)
RDPScreenShotFactory.__STATE__.append((connector.host, connector.port, reason)) RDPScreenShotFactory.__STATE__.append((connector.host, connector.port, reason))
RDPScreenShotFactory.__INSTANCE__ -= 1 RDPScreenShotFactory.__INSTANCE__ -= 1
if(RDPScreenShotFactory.__INSTANCE__ == 0): if(RDPScreenShotFactory.__INSTANCE__ == 0):
@@ -91,7 +95,6 @@ class RDPScreenShotFactory(rdp.ClientFactory):
self._reactor.stop() self._reactor.stop()
self._app.exit() self._app.exit()
def buildObserver(self, controller, addr): def buildObserver(self, controller, addr):
""" """
@summary: build ScreenShot observer @summary: build ScreenShot observer
@@ -124,7 +127,7 @@ class RDPScreenShotFactory(rdp.ClientFactory):
""" """
image = RDPBitmapToQtImage(width, height, bitsPerPixel, isCompress, data); image = RDPBitmapToQtImage(width, height, bitsPerPixel, isCompress, data);
with QtGui.QPainter(self._buffer) as qp: with QtGui.QPainter(self._buffer) as qp:
#draw image # draw image
qp.drawImage(destLeft, destTop, image, 0, 0, destRight - destLeft + 1, destBottom - destTop + 1) qp.drawImage(destLeft, destTop, image, 0, 0, destRight - destLeft + 1, destBottom - destTop + 1)
if not self._startTimeout: if not self._startTimeout:
self._startTimeout = False self._startTimeout = False
@@ -134,19 +137,26 @@ class RDPScreenShotFactory(rdp.ClientFactory):
""" """
@summary: callback use when RDP stack is connected (just before received bitmap) @summary: callback use when RDP stack is connected (just before received bitmap)
""" """
log.info("connected %s"%addr) log.info("connected %s" % addr)
def onSessionReady(self):
"""
@summary: Windows session is ready
@see: rdp.RDPClientObserver.onSessionReady
"""
pass
def onClose(self): def onClose(self):
""" """
@summary: callback use when RDP stack is closed @summary: callback use when RDP stack is closed
""" """
log.info("save screenshot into %s"%self._path) log.info("save screenshot into %s" % self._path)
self._buffer.save(self._path) self._buffer.save(self._path)
def checkUpdate(self): def checkUpdate(self):
self._controller.close(); self._controller.close();
controller.setScreen(width, height); controller.setScreen(self._width, self._height);
controller.setSecurityLevel(self._security) controller.setSecurityLevel(self._security)
return ScreenShotObserver(controller, self._width, self._height, self._path, self._timeout, self._reactor) return ScreenShotObserver(controller, self._width, self._height, self._path, self._timeout, self._reactor)
@@ -174,12 +184,13 @@ def main(width, height, path, timeout, hosts):
else: else:
ip, port = host, "3389" ip, port = host, "3389"
reactor.connectTCP(ip, int(port), RDPScreenShotFactory(reactor, app, width, height, path + "%s.jpg"%ip, timeout)) reactor.connectTCP(ip, int(port), RDPScreenShotFactory(reactor, app, width, height, path + "%s.jpg" % ip, timeout))
reactor.runReturn() reactor.runReturn()
app.exec_() app.exec_()
return RDPScreenShotFactory.__STATE__ return RDPScreenShotFactory.__STATE__
def help(): def help():
print "Usage: rdpy-rdpscreenshot [options] ip[:port]" print "Usage: rdpy-rdpscreenshot [options] ip[:port]"
print "\t-w: width of screen default value is 1024" print "\t-w: width of screen default value is 1024"
@@ -188,7 +199,7 @@ def help():
print "\t-t: timeout of connection without any updating order (default is 2s)" print "\t-t: timeout of connection without any updating order (default is 2s)"
if __name__ == '__main__': if __name__ == '__main__':
#default script argument # default script argument
width = 1024 width = 1024
height = 800 height = 800
path = "/tmp/" path = "/tmp/"

View File

@@ -33,12 +33,17 @@ class Level(object):
NONE = 4 NONE = 4
_LOG_LEVEL = Level.DEBUG _LOG_LEVEL = Level.DEBUG
_LOG_FILE = False
def log(message): def log(message):
""" """
@summary: Main log function @summary: Main log function
@param message: string to print @param message: string to print
""" """
if _LOG_FILE:
f = open(_LOG_FILE, "a+")
f.write("%s\n"%message)
f.close()
print "[*] %s"%message print "[*] %s"%message
def error(message): def error(message):

View File

@@ -477,7 +477,7 @@ class CompositeType(Type):
raise e raise e
if not self._readLen is None and readLen < self._readLen.value: if not self._readLen is None and readLen < self._readLen.value:
log.debug("Still have correct data in packet %s, read it as padding"%self.__class__) log.debug("Still have correct data in packet %s, read %s bytes as padding"%(self.__class__, self._readLen.value - readLen))
s.read(self._readLen.value - readLen) s.read(self._readLen.value - readLen)
def __write__(self, s): def __write__(self, s):

View File

@@ -234,7 +234,7 @@ class LicPacket(CompositeType):
if self.bMsgtype.value == c._MESSAGE_TYPE_: if self.bMsgtype.value == c._MESSAGE_TYPE_:
return c(readLen = self.wMsgSize - 4) return c(readLen = self.wMsgSize - 4)
log.debug("unknown license message : %s"%self.bMsgtype.value) log.debug("unknown license message : %s"%self.bMsgtype.value)
return String() return String(readLen = self.wMsgSize - 4)
if message is None: if message is None:
message = FactoryType(LicensingMessageFactory) message = FactoryType(LicensingMessageFactory)

View File

@@ -161,6 +161,15 @@ class PointerFlag(object):
PTRFLAGS_BUTTON2 = 0x2000 PTRFLAGS_BUTTON2 = 0x2000
PTRFLAGS_BUTTON3 = 0x4000 PTRFLAGS_BUTTON3 = 0x4000
class PointerExFlag(object):
"""
@summary: Use in Pointer event
@see: https://msdn.microsoft.com/en-us/library/cc240587.aspx
"""
PTRXFLAGS_DOWN = 0x8000
PTRXFLAGS_BUTTON1 = 0x0001
PTRXFLAGS_BUTTON2 = 0x0002
class KeyboardFlag(object): class KeyboardFlag(object):
""" """
@summary: Use in scan code key event @summary: Use in scan code key event
@@ -202,6 +211,16 @@ class Display(object):
SUPPRESS_DISPLAY_UPDATES = 0x00 SUPPRESS_DISPLAY_UPDATES = 0x00
ALLOW_DISPLAY_UPDATES = 0x01 ALLOW_DISPLAY_UPDATES = 0x01
class ToogleFlag(object):
"""
@summary: Use to known state of keyboard
@see: https://msdn.microsoft.com/en-us/library/cc240588.aspx
"""
TS_SYNC_SCROLL_LOCK = 0x00000001
TS_SYNC_NUM_LOCK = 0x00000002
TS_SYNC_CAPS_LOCK = 0x00000004
TS_SYNC_KANA_LOCK = 0x00000008
class ErrorInfo(object): class ErrorInfo(object):
""" """
@summary: Error code use in Error info PDU @summary: Error code use in Error info PDU
@@ -413,8 +432,6 @@ class ErrorInfo(object):
ERRINFO_VCDATATOOLONG : "The size of a received Virtual Channel PDU (section 2.2.6.1) exceeds the chunking size specified in the Virtual Channel Capability Set (section 2.2.7.1.10).", ERRINFO_VCDATATOOLONG : "The size of a received Virtual Channel PDU (section 2.2.6.1) exceeds the chunking size specified in the Virtual Channel Capability Set (section 2.2.7.1.10).",
} }
class ShareControlHeader(CompositeType): class ShareControlHeader(CompositeType):
""" """
@summary: PDU share control header @summary: PDU share control header
@@ -461,10 +478,10 @@ class PDU(CompositeType):
""" """
for c in [DemandActivePDU, ConfirmActivePDU, DataPDU, DeactiveAllPDU]: for c in [DemandActivePDU, ConfirmActivePDU, DataPDU, DeactiveAllPDU]:
if self.shareControlHeader.pduType.value == c._PDUTYPE_: if self.shareControlHeader.pduType.value == c._PDUTYPE_:
return c() return c(readLen = CallableValue(self.shareControlHeader.totalLength.value - sizeof(self.shareControlHeader)))
log.debug("unknown PDU type : %s"%hex(self.shareControlHeader.pduType.value)) log.debug("unknown PDU type : %s"%hex(self.shareControlHeader.pduType.value))
#read entire packet #read entire packet
return String() return String(readLen = CallableValue(self.shareControlHeader.totalLength.value - sizeof(self.shareControlHeader)))
if pduMessage is None: if pduMessage is None:
pduMessage = FactoryType(PDUMessageFactory) pduMessage = FactoryType(PDUMessageFactory)
@@ -481,8 +498,8 @@ class DemandActivePDU(CompositeType):
#may declare the PDU type #may declare the PDU type
_PDUTYPE_ = PDUType.PDUTYPE_DEMANDACTIVEPDU _PDUTYPE_ = PDUType.PDUTYPE_DEMANDACTIVEPDU
def __init__(self): def __init__(self, readLen = None):
CompositeType.__init__(self) CompositeType.__init__(self, readLen = readLen)
self.shareId = UInt32Le() self.shareId = UInt32Le()
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor)) self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
self.lengthCombinedCapabilities = UInt16Le(lambda:(sizeof(self.numberCapabilities) + sizeof(self.pad2Octets) + sizeof(self.capabilitySets))) self.lengthCombinedCapabilities = UInt16Le(lambda:(sizeof(self.numberCapabilities) + sizeof(self.pad2Octets) + sizeof(self.capabilitySets)))
@@ -500,8 +517,8 @@ class ConfirmActivePDU(CompositeType):
#may declare the PDU type #may declare the PDU type
_PDUTYPE_ = PDUType.PDUTYPE_CONFIRMACTIVEPDU _PDUTYPE_ = PDUType.PDUTYPE_CONFIRMACTIVEPDU
def __init__(self): def __init__(self, readLen = None):
CompositeType.__init__(self) CompositeType.__init__(self, readLen = readLen)
self.shareId = UInt32Le() self.shareId = UInt32Le()
self.originatorId = UInt16Le(0x03EA, constant = True) self.originatorId = UInt16Le(0x03EA, constant = True)
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor)) self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
@@ -519,10 +536,10 @@ class DeactiveAllPDU(CompositeType):
#may declare the PDU type #may declare the PDU type
_PDUTYPE_ = PDUType.PDUTYPE_DEACTIVATEALLPDU _PDUTYPE_ = PDUType.PDUTYPE_DEACTIVATEALLPDU
def __init__(self): def __init__(self, readLen = None):
#in old version this packet is empty i don't know #in old version this packet is empty i don't know
#and not specified #and not specified
CompositeType.__init__(self, optional = True) CompositeType.__init__(self, optional = True, readLen = readLen)
self.shareId = UInt32Le() self.shareId = UInt32Le()
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor)) self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor) self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor)
@@ -534,19 +551,19 @@ class DataPDU(CompositeType):
#may declare the PDU type #may declare the PDU type
_PDUTYPE_ = PDUType.PDUTYPE_DATAPDU _PDUTYPE_ = PDUType.PDUTYPE_DATAPDU
def __init__(self, pduData = None, shareId = 0): def __init__(self, pduData = None, shareId = 0, readLen = None):
CompositeType.__init__(self) CompositeType.__init__(self, readLen = readLen)
self.shareDataHeader = ShareDataHeader(lambda:sizeof(self), lambda:self.pduData.__class__._PDUTYPE2_, shareId) self.shareDataHeader = ShareDataHeader(lambda:sizeof(self), lambda:self.pduData.__class__._PDUTYPE2_, shareId)
def PDUDataFactory(): def PDUDataFactory():
""" """
@summary: Create object in accordance self.shareDataHeader.pduType2 value @summary: Create object in accordance self.shareDataHeader.pduType2 value
""" """
for c in [UpdateDataPDU, SynchronizeDataPDU, ControlDataPDU, ErrorInfoDataPDU, FontListDataPDU, FontMapDataPDU, PersistentListPDU, ClientInputEventPDU, ShutdownDeniedPDU, ShutdownRequestPDU, SupressOutputDataPDU]: for c in [UpdateDataPDU, SynchronizeDataPDU, ControlDataPDU, ErrorInfoDataPDU, FontListDataPDU, FontMapDataPDU, PersistentListPDU, ClientInputEventPDU, ShutdownDeniedPDU, ShutdownRequestPDU, SupressOutputDataPDU, SaveSessionInfoPDU]:
if self.shareDataHeader.pduType2.value == c._PDUTYPE2_: if self.shareDataHeader.pduType2.value == c._PDUTYPE2_:
return c() return c(readLen = CallableValue(readLen.value - sizeof(self.shareDataHeader)))
log.debug("unknown PDU data type : %s"%hex(self.shareDataHeader.pduType2.value)) log.debug("unknown PDU data type : %s"%hex(self.shareDataHeader.pduType2.value))
return String() return String(readLen = CallableValue(readLen.value - sizeof(self.shareDataHeader)))
if pduData is None: if pduData is None:
pduData = FactoryType(PDUDataFactory) pduData = FactoryType(PDUDataFactory)
@@ -655,8 +672,8 @@ class PersistentListPDU(CompositeType):
""" """
_PDUTYPE2_ = PDUType2.PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST _PDUTYPE2_ = PDUType2.PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST
def __init__(self, userId = 0, shareId = 0): def __init__(self, userId = 0, shareId = 0, readLen = None):
CompositeType.__init__(self) CompositeType.__init__(self, readLen = readLen)
self.numEntriesCache0 = UInt16Le() self.numEntriesCache0 = UInt16Le()
self.numEntriesCache1 = UInt16Le() self.numEntriesCache1 = UInt16Le()
self.numEntriesCache2 = UInt16Le() self.numEntriesCache2 = UInt16Le()
@@ -679,8 +696,8 @@ class ClientInputEventPDU(CompositeType):
""" """
_PDUTYPE2_ = PDUType2.PDUTYPE2_INPUT _PDUTYPE2_ = PDUType2.PDUTYPE2_INPUT
def __init__(self): def __init__(self, readLen = None):
CompositeType.__init__(self) CompositeType.__init__(self, readLen = readLen)
self.numEvents = UInt16Le(lambda:len(self.slowPathInputEvents._array)) self.numEvents = UInt16Le(lambda:len(self.slowPathInputEvents._array))
self.pad2Octets = UInt16Le() self.pad2Octets = UInt16Le()
self.slowPathInputEvents = ArrayType(SlowPathInputEvent, readLen = self.numEvents) self.slowPathInputEvents = ArrayType(SlowPathInputEvent, readLen = self.numEvents)
@@ -691,8 +708,8 @@ class ShutdownRequestPDU(CompositeType):
client -> server client -> server
""" """
_PDUTYPE2_ = PDUType2.PDUTYPE2_SHUTDOWN_REQUEST _PDUTYPE2_ = PDUType2.PDUTYPE2_SHUTDOWN_REQUEST
def __init__(self): def __init__(self, readLen = None):
CompositeType.__init__(self) CompositeType.__init__(self, readLen = readLen)
class ShutdownDeniedPDU(CompositeType): class ShutdownDeniedPDU(CompositeType):
""" """
@@ -700,8 +717,8 @@ class ShutdownDeniedPDU(CompositeType):
server -> client server -> client
""" """
_PDUTYPE2_ = PDUType2.PDUTYPE2_SHUTDOWN_DENIED _PDUTYPE2_ = PDUType2.PDUTYPE2_SHUTDOWN_DENIED
def __init__(self): def __init__(self, readLen = None):
CompositeType.__init__(self) CompositeType.__init__(self, readLen = readLen)
class InclusiveRectangle(CompositeType): class InclusiveRectangle(CompositeType):
""" """
@@ -761,9 +778,9 @@ class UpdateDataPDU(CompositeType):
""" """
for c in [BitmapUpdateDataPDU]: for c in [BitmapUpdateDataPDU]:
if self.updateType.value == c._UPDATE_TYPE_: if self.updateType.value == c._UPDATE_TYPE_:
return c() return c(readLen = CallableValue(readLen.value - 2))
log.debug("unknown PDU update data type : %s"%hex(self.updateType.value)) log.debug("unknown PDU update data type : %s"%hex(self.updateType.value))
return String() return String(readLen = CallableValue(readLen.value - 2))
if updateData is None: if updateData is None:
updateData = FactoryType(UpdateDataFactory, conditional = lambda:(self.updateType.value != UpdateType.UPDATETYPE_SYNCHRONIZE)) updateData = FactoryType(UpdateDataFactory, conditional = lambda:(self.updateType.value != UpdateType.UPDATETYPE_SYNCHRONIZE))
@@ -772,6 +789,18 @@ class UpdateDataPDU(CompositeType):
self.updateData = updateData self.updateData = updateData
class SaveSessionInfoPDU(CompositeType):
"""
@see: https://msdn.microsoft.com/en-us/library/cc240636.aspx
"""
_PDUTYPE2_ = PDUType2.PDUTYPE2_SAVE_SESSION_INFO
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
self.infoType = UInt32Le()
#TODO parse info data
self.infoData = String()
class FastPathUpdatePDU(CompositeType): class FastPathUpdatePDU(CompositeType):
""" """
@summary: Fast path update PDU packet @summary: Fast path update PDU packet
@@ -789,9 +818,9 @@ class FastPathUpdatePDU(CompositeType):
""" """
for c in [FastPathBitmapUpdateDataPDU]: for c in [FastPathBitmapUpdateDataPDU]:
if (self.updateHeader.value & 0xf) == c._FASTPATH_UPDATE_TYPE_: if (self.updateHeader.value & 0xf) == c._FASTPATH_UPDATE_TYPE_:
return c() return c(readLen = self.size)
log.debug("unknown Fast Path PDU update data type : %s"%hex(self.updateHeader.value & 0xf)) log.debug("unknown Fast Path PDU update data type : %s"%hex(self.updateHeader.value & 0xf))
return String() return String(readLen = self.size)
if updateData is None: if updateData is None:
updateData = FactoryType(UpdateDataFactory) updateData = FactoryType(UpdateDataFactory)
@@ -821,8 +850,8 @@ class OrderUpdateDataPDU(CompositeType):
@see: http://msdn.microsoft.com/en-us/library/cc241571.aspx @see: http://msdn.microsoft.com/en-us/library/cc241571.aspx
@todo: not implemented yet but need it @todo: not implemented yet but need it
""" """
def __init__(self): def __init__(self, readLen = None):
CompositeType.__init__(self) CompositeType.__init__(self, readLen = readLen)
self.pad2OctetsA = UInt16Le() self.pad2OctetsA = UInt16Le()
self.numberOrders = UInt16Le(lambda:len(self.orderData._array)) self.numberOrders = UInt16Le(lambda:len(self.orderData._array))
self.pad2OctetsB = UInt16Le() self.pad2OctetsB = UInt16Le()
@@ -882,8 +911,8 @@ class FastPathBitmapUpdateDataPDU(CompositeType):
""" """
_FASTPATH_UPDATE_TYPE_ = FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP _FASTPATH_UPDATE_TYPE_ = FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP
def __init__(self): def __init__(self, readLen = None):
CompositeType.__init__(self) CompositeType.__init__(self, readLen = readLen)
self.header = UInt16Le(FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP, constant = True) self.header = UInt16Le(FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP, constant = True)
self.numberRectangles = UInt16Le(lambda:len(self.rectangles._array)) self.numberRectangles = UInt16Le(lambda:len(self.rectangles._array))
self.rectangles = ArrayType(BitmapData, readLen = self.numberRectangles) self.rectangles = ArrayType(BitmapData, readLen = self.numberRectangles)
@@ -899,11 +928,10 @@ class SlowPathInputEvent(CompositeType):
self.messageType = UInt16Le(lambda:self.slowPathInputData.__class__._INPUT_MESSAGE_TYPE_) self.messageType = UInt16Le(lambda:self.slowPathInputData.__class__._INPUT_MESSAGE_TYPE_)
def SlowPathInputDataFactory(): def SlowPathInputDataFactory():
for c in [PointerEvent, ScancodeKeyEvent, UnicodeKeyEvent]: for c in [PointerEvent, PointerExEvent, ScancodeKeyEvent, UnicodeKeyEvent, SynchronizeEvent]:
if self.messageType.value == c._INPUT_MESSAGE_TYPE_: if self.messageType.value == c._INPUT_MESSAGE_TYPE_:
return c() return c()
log.debug("unknown slow path input : %s"%hex(self.messageType.value)) raise InvalidExpectedDataException("unknown slow path input : %s"%hex(self.messageType.value))
return String()
if messageData is None: if messageData is None:
messageData = FactoryType(SlowPathInputDataFactory) messageData = FactoryType(SlowPathInputDataFactory)
@@ -912,6 +940,18 @@ class SlowPathInputEvent(CompositeType):
self.slowPathInputData = messageData self.slowPathInputData = messageData
class SynchronizeEvent(CompositeType):
"""
@summary: Synchronize keyboard
@see: https://msdn.microsoft.com/en-us/library/cc240588.aspx
"""
_INPUT_MESSAGE_TYPE_ = InputMessageType.INPUT_EVENT_SYNC
def __init__(self):
CompositeType.__init__(self)
self.pad2Octets = UInt16Le()
self.toggleFlags = UInt32Le()
class PointerEvent(CompositeType): class PointerEvent(CompositeType):
""" """
@summary: Event use to communicate mouse position @summary: Event use to communicate mouse position
@@ -925,6 +965,19 @@ class PointerEvent(CompositeType):
self.xPos = UInt16Le() self.xPos = UInt16Le()
self.yPos = UInt16Le() self.yPos = UInt16Le()
class PointerExEvent(CompositeType):
"""
@summary: Event use to communicate mouse position
@see: http://msdn.microsoft.com/en-us/library/cc240587.aspx
"""
_INPUT_MESSAGE_TYPE_ = InputMessageType.INPUT_EVENT_MOUSEX
def __init__(self):
CompositeType.__init__(self)
self.pointerFlags = UInt16Le()
self.xPos = UInt16Le()
self.yPos = UInt16Le()
class ScancodeKeyEvent(CompositeType): class ScancodeKeyEvent(CompositeType):
""" """
@summary: Event use to communicate keyboard informations @summary: Event use to communicate keyboard informations

View File

@@ -25,6 +25,7 @@ In this layer are managed all mains bitmap update orders end user inputs
from rdpy.core.layer import LayerAutomata from rdpy.core.layer import LayerAutomata
from rdpy.core.error import CallPureVirtualFuntion from rdpy.core.error import CallPureVirtualFuntion
from rdpy.core.type import ArrayType
import rdpy.core.log as log import rdpy.core.log as log
import rdpy.protocol.rdp.tpkt as tpkt import rdpy.protocol.rdp.tpkt as tpkt
import data, caps import data, caps
@@ -39,6 +40,13 @@ class PDUClientListener(object):
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUClientListener")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUClientListener"))
def onSessionReady(self):
"""
@summary: Event call when Windows session is ready
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onSessionReady", "PDUClientListener"))
def onUpdate(self, rectangles): def onUpdate(self, rectangles):
""" """
@summary: call when a bitmap data is received from update PDU @summary: call when a bitmap data is received from update PDU
@@ -259,8 +267,9 @@ class Client(PDULayer):
@summary: Main receive function after connection sequence @summary: Main receive function after connection sequence
@param s: Stream from transport layer @param s: Stream from transport layer
""" """
pdu = data.PDU() pdus = ArrayType(data.PDU)
s.readType(pdu) s.readType(pdus)
for pdu in pdus:
if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU: if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU:
self.readDataPDU(pdu.pduMessage) self.readDataPDU(pdu.pduMessage)
elif pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DEACTIVATEALLPDU: elif pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DEACTIVATEALLPDU:
@@ -276,10 +285,11 @@ class Client(PDULayer):
@param fastPathS: {Stream} that contain fast path data @param fastPathS: {Stream} that contain fast path data
@param secFlag: {SecFlags} @param secFlag: {SecFlags}
""" """
fastPathPDU = data.FastPathUpdatePDU() updates = ArrayType(data.FastPathUpdatePDU)
fastPathS.readType(fastPathPDU) fastPathS.readType(updates)
if fastPathPDU.updateHeader.value == data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP: for update in updates:
self._listener.onUpdate(fastPathPDU.updateData.rectangles._array) if update.updateHeader.value == data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
self._listener.onUpdate(update.updateData.rectangles._array)
def readDataPDU(self, dataPDU): def readDataPDU(self, dataPDU):
""" """
@@ -298,6 +308,9 @@ class Client(PDULayer):
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED: elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
#may be an event to ask to user #may be an event to ask to user
self._transport.close() self._transport.close()
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SAVE_SESSION_INFO:
#handle session event
self._listener.onSessionReady()
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_UPDATE: elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_UPDATE:
self.readUpdateDataPDU(dataPDU.pduData) self.readUpdateDataPDU(dataPDU.pduData)

View File

@@ -100,7 +100,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
def setUsername(self, username): def setUsername(self, username):
""" """
@summary: Set the username for session @summary: Set the username for session
@param username: username of session @param username: {string} username of session
""" """
#username in PDU info packet #username in PDU info packet
self._secLayer._info.userName.value = username self._secLayer._info.userName.value = username
@@ -109,7 +109,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
def setPassword(self, password): def setPassword(self, password):
""" """
@summary: Set password for session @summary: Set password for session
@param password: password of session @param password: {string} password of session
""" """
self.setAutologon() self.setAutologon()
self._secLayer._info.password.value = password self._secLayer._info.password.value = password
@@ -117,7 +117,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
def setDomain(self, domain): def setDomain(self, domain):
""" """
@summary: Set the windows domain of session @summary: Set the windows domain of session
@param domain: domain of session @param domain: {string} domain of session
""" """
self._secLayer._info.domain.value = domain self._secLayer._info.domain.value = domain
@@ -127,6 +127,13 @@ class RDPClientController(pdu.layer.PDUClientListener):
""" """
self._secLayer._info.flag |= sec.InfoFlag.INFO_AUTOLOGON self._secLayer._info.flag |= sec.InfoFlag.INFO_AUTOLOGON
def setAlternateShell(self, appName):
"""
@summary: set application name of app which start at the begining of session
@param appName: {string} application name
"""
self._secLayer._info.alternateShell.value = appName
def setKeyboardLayout(self, layout): def setKeyboardLayout(self, layout):
""" """
@summary: keyboard layout @summary: keyboard layout
@@ -192,6 +199,15 @@ class RDPClientController(pdu.layer.PDUClientListener):
for observer in self._clientObserver: for observer in self._clientObserver:
observer.onReady() observer.onReady()
def onSessionReady(self):
"""
@summary: Call when Windows session is ready (connected)
"""
self._isReady = True
#signal all listener
for observer in self._clientObserver:
observer.onSessionReady()
def onClose(self): def onClose(self):
""" """
@summary: Event call when RDP stack is closed @summary: Event call when RDP stack is closed
@@ -212,6 +228,17 @@ class RDPClientController(pdu.layer.PDUClientListener):
return return
try: try:
if button == 4 or button == 5:
event = pdu.data.PointerExEvent()
if isPressed:
event.pointerFlags.value |= pdu.data.PointerExFlag.PTRXFLAGS_DOWN
if button == 4:
event.pointerFlags.value |= pdu.data.PointerExFlag.PTRXFLAGS_BUTTON1
elif button == 5:
event.pointerFlags.value |= pdu.data.PointerExFlag.PTRXFLAGS_BUTTON2
else:
event = pdu.data.PointerEvent() event = pdu.data.PointerEvent()
if isPressed: if isPressed:
event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN
@@ -225,11 +252,11 @@ class RDPClientController(pdu.layer.PDUClientListener):
else: else:
event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE
#position # position
event.xPos.value = x event.xPos.value = x
event.yPos.value = y event.yPos.value = y
#send proper event # send proper event
self._pduLayer.sendInputEvents([event]) self._pduLayer.sendInputEvents([event])
except InvalidValue: except InvalidValue:
@@ -269,11 +296,12 @@ class RDPClientController(pdu.layer.PDUClientListener):
except InvalidValue: except InvalidValue:
log.info("try send wheel event with incorrect position") log.info("try send wheel event with incorrect position")
def sendKeyEventScancode(self, code, isPressed): def sendKeyEventScancode(self, code, isPressed, extended = False):
""" """
@summary: Send a scan code to RDP stack @summary: Send a scan code to RDP stack
@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
@param extended: {boolean} extended scancode like ctr or win button
""" """
if not self._isReady: if not self._isReady:
return return
@@ -281,11 +309,12 @@ class RDPClientController(pdu.layer.PDUClientListener):
try: try:
event = pdu.data.ScancodeKeyEvent() event = pdu.data.ScancodeKeyEvent()
event.keyCode.value = code event.keyCode.value = code
if isPressed: if not isPressed:
event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_DOWN
else:
event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE
if extended:
event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED
#send event #send event
self._pduLayer.sendInputEvents([event]) self._pduLayer.sendInputEvents([event])
@@ -478,11 +507,11 @@ class RDPServerController(pdu.layer.PDUServerListener):
for event in slowPathInputEvents: for event in slowPathInputEvents:
#scan code #scan code
if event.messageType.value == pdu.data.InputMessageType.INPUT_EVENT_SCANCODE: if event.messageType.value == pdu.data.InputMessageType.INPUT_EVENT_SCANCODE:
observer.onKeyEventScancode(event.slowPathInputData.keyCode.value, not (event.slowPathInputData.keyboardFlags.value & pdu.data.KeyboardFlag.KBDFLAGS_RELEASE)) observer.onKeyEventScancode(event.slowPathInputData.keyCode.value, not (event.slowPathInputData.keyboardFlags.value & pdu.data.KeyboardFlag.KBDFLAGS_RELEASE), bool(event.slowPathInputData.keyboardFlags.value & pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED))
#unicode #unicode
elif event.messageType.value == pdu.data.InputMessageType.INPUT_EVENT_UNICODE: elif event.messageType.value == pdu.data.InputMessageType.INPUT_EVENT_UNICODE:
observer.onKeyEventUnicode(event.slowPathInputData.unicode.value, not (event.slowPathInputData.keyboardFlags.value & pdu.data.KeyboardFlag.KBDFLAGS_RELEASE)) observer.onKeyEventUnicode(event.slowPathInputData.unicode.value, not (event.slowPathInputData.keyboardFlags.value & pdu.data.KeyboardFlag.KBDFLAGS_RELEASE))
#mouse event #mouse events
elif event.messageType.value == pdu.data.InputMessageType.INPUT_EVENT_MOUSE: elif event.messageType.value == pdu.data.InputMessageType.INPUT_EVENT_MOUSE:
isPressed = event.slowPathInputData.pointerFlags.value & pdu.data.PointerFlag.PTRFLAGS_DOWN isPressed = event.slowPathInputData.pointerFlags.value & pdu.data.PointerFlag.PTRFLAGS_DOWN
button = 0 button = 0
@@ -493,6 +522,15 @@ class RDPServerController(pdu.layer.PDUServerListener):
elif event.slowPathInputData.pointerFlags.value & pdu.data.PointerFlag.PTRFLAGS_BUTTON3: elif event.slowPathInputData.pointerFlags.value & pdu.data.PointerFlag.PTRFLAGS_BUTTON3:
button = 3 button = 3
observer.onPointerEvent(event.slowPathInputData.xPos.value, event.slowPathInputData.yPos.value, button, isPressed) observer.onPointerEvent(event.slowPathInputData.xPos.value, event.slowPathInputData.yPos.value, button, isPressed)
elif event.messageType.value == pdu.data.InputMessageType.INPUT_EVENT_MOUSEX:
isPressed = event.slowPathInputData.pointerFlags.value & pdu.data.PointerExFlag.PTRXFLAGS_DOWN
button = 0
if event.slowPathInputData.pointerFlags.value & pdu.data.PointerExFlag.PTRXFLAGS_BUTTON1:
button = 4
elif event.slowPathInputData.pointerFlags.value & pdu.data.PointerExFlag.PTRXFLAGS_BUTTON2:
button = 5
observer.onPointerEvent(event.slowPathInputData.xPos.value, event.slowPathInputData.yPos.value, button, isPressed)
def sendUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): def sendUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
""" """
@@ -607,6 +645,12 @@ class RDPClientObserver(object):
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPClientObserver")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPClientObserver"))
def onSessionReady(self):
"""
@summary: Windows session is ready
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onSessionReady", "RDPClientObserver"))
def onClose(self): def onClose(self):
""" """
@summary: Stack is closes @summary: Stack is closes
@@ -652,11 +696,12 @@ class RDPServerObserver(object):
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onClose", "RDPClientObserver")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onClose", "RDPClientObserver"))
def onKeyEventScancode(self, code, isPressed): def onKeyEventScancode(self, code, isPressed, isExtended):
""" """
@summary: 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 code: {integer} scan code of key
@param isPressed: True if key is down @param isPressed: {boolean} True if key is down
@param isExtended: {boolean} True if a special key
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onKeyEventScanCode", "RDPServerObserver")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onKeyEventScanCode", "RDPServerObserver"))
@@ -673,7 +718,7 @@ class RDPServerObserver(object):
@summary: Event call on mouse event @summary: Event call on mouse event
@param x: x position @param x: x position
@param y: y position @param y: y position
@param button: 1, 2 or 3 button @param button: 1, 2, 3, 4 or 5 button
@param isPressed: True if mouse button is pressed @param isPressed: True if mouse button is pressed
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onPointerEvent", "RDPServerObserver")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onPointerEvent", "RDPServerObserver"))

View File

@@ -323,7 +323,7 @@ class RDPInfo(CompositeType):
#code page #code page
self.codePage = UInt32Le() self.codePage = UInt32Le()
#support flag #support flag
self.flag = UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_DISABLECTRLALTDEL) self.flag = UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_DISABLECTRLALTDEL | InfoFlag.INFO_ENABLEWINDOWSKEY)
self.cbDomain = UInt16Le(lambda:sizeof(self.domain) - 2) self.cbDomain = UInt16Le(lambda:sizeof(self.domain) - 2)
self.cbUserName = UInt16Le(lambda:sizeof(self.userName) - 2) self.cbUserName = UInt16Le(lambda:sizeof(self.userName) - 2)
self.cbPassword = UInt16Le(lambda:sizeof(self.password) - 2) self.cbPassword = UInt16Le(lambda:sizeof(self.password) - 2)

View File

@@ -303,7 +303,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
@param isCompress: {bool} use RLE compression @param isCompress: {bool} use RLE compression
@param data: {str} bitmap data @param data: {str} bitmap data
""" """
image = RDPBitmapToQtImage(width, height, bitsPerPixel, isCompress, data); image = RDPBitmapToQtImage(width, height, bitsPerPixel, isCompress, data)
#if image need to be cut #if image need to be cut
#For bit alignement server may send more than image pixel #For bit alignement server may send more than image pixel
self._widget.notifyImage(destLeft, destTop, image, destRight - destLeft + 1, destBottom - destTop + 1) self._widget.notifyImage(destLeft, destTop, image, destRight - destLeft + 1, destBottom - destTop + 1)
@@ -311,12 +311,21 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
def onReady(self): def onReady(self):
""" """
@summary: Call when stack is ready @summary: Call when stack is ready
@see: rdp.RDPClientObserver.onReady
""" """
#do something maybe a loader #do something maybe a loader
def onSessionReady(self):
"""
@summary: Windows session is ready
@see: rdp.RDPClientObserver.onSessionReady
"""
pass
def onClose(self): def onClose(self):
""" """
@summary: Call when stack is close @summary: Call when stack is close
@see: rdp.RDPClientObserver.onClose
""" """
#do something maybe a message #do something maybe a message
@@ -336,12 +345,6 @@ class QRemoteDesktop(QtGui.QWidget):
self._adaptor = adaptor self._adaptor = adaptor
#set correct size #set correct size
self.resize(width, height) self.resize(width, height)
#refresh stack of image
#because we can update image only in paint
#event function. When protocol receive image
#we will stock into refresh list
#and in paint event paint list of all refresh images
self._refresh = []
#bind mouse event #bind mouse event
self.setMouseTracking(True) self.setMouseTracking(True)
#buffer image #buffer image
@@ -354,8 +357,9 @@ class QRemoteDesktop(QtGui.QWidget):
@param y: y position of new image @param y: y position of new image
@param qimage: new QImage @param qimage: new QImage
""" """
#save in refresh list (order is important) #fill buffer image
self._refresh.append((x, y, qimage, width, height)) with QtGui.QPainter(self._buffer) as qp:
qp.drawImage(x, y, qimage, 0, 0, width, height)
#force update #force update
self.update() self.update()
@@ -373,17 +377,10 @@ class QRemoteDesktop(QtGui.QWidget):
@summary: Call when Qt renderer engine estimate that is needed @summary: Call when Qt renderer engine estimate that is needed
@param e: QEvent @param e: QEvent
""" """
#fill buffer image
with QtGui.QPainter(self._buffer) as qp:
#draw image
for (x, y, image, width, height) in self._refresh:
qp.drawImage(x, y, image, 0, 0, width, height)
#draw in widget #draw in widget
with QtGui.QPainter(self) as qp: with QtGui.QPainter(self) as qp:
qp.drawImage(0, 0, self._buffer) qp.drawImage(0, 0, self._buffer)
self._refresh = []
def mouseMoveEvent(self, event): def mouseMoveEvent(self, event):
""" """
@summary: Call when mouse move @summary: Call when mouse move

View File

@@ -4,7 +4,7 @@ import setuptools
from distutils.core import setup, Extension from distutils.core import setup, Extension
setup(name='rdpy', setup(name='rdpy',
version='1.3.1', version='1.3.2',
description='Remote Desktop Protocol in Python', description='Remote Desktop Protocol in Python',
long_description=""" long_description="""
RDPY is a pure Python implementation of the Microsoft RDP (Remote Desktop Protocol) protocol (Client and Server side). RDPY is built over the event driven network engine Twisted. RDPY is a pure Python implementation of the Microsoft RDP (Remote Desktop Protocol) protocol (Client and Server side). RDPY is built over the event driven network engine Twisted.
@@ -42,6 +42,6 @@ setup(name='rdpy',
'service_identity', 'service_identity',
'qt4reactor', 'qt4reactor',
'rsa', 'rsa',
'pyasn1', 'pyasn1'
], ],
) )