Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd7c708bf3 | ||
|
|
1deb2d69ea | ||
|
|
0a5a1fd12c | ||
|
|
c97b451ce3 | ||
|
|
80f989a804 | ||
|
|
c6e100f9a6 | ||
|
|
15df00ec20 | ||
|
|
342349cf41 | ||
|
|
d6043106e3 | ||
|
|
bd7e73a6e7 | ||
|
|
fc1685e652 | ||
|
|
4320824aae | ||
|
|
5a438174b9 |
@@ -1,4 +1,4 @@
|
|||||||
# RDPY [](https://travis-ci.org/citronneur/rdpy)
|
# RDPY [](https://travis-ci.org/citronneur/rdpy) [](http://badge.fury.io/py/rdpy)
|
||||||
|
|
||||||
Remote Desktop Protocol in twisted python.
|
Remote Desktop Protocol in twisted python.
|
||||||
|
|
||||||
@@ -33,6 +33,12 @@ Example for Debian based systems :
|
|||||||
sudo apt-get install python-qt4
|
sudo apt-get install python-qt4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### OS X
|
||||||
|
Example for OS X to install PyQt with homebrew
|
||||||
|
```
|
||||||
|
$ brew install qt sip pyqt
|
||||||
|
```
|
||||||
|
|
||||||
#### Windows
|
#### Windows
|
||||||
|
|
||||||
x86 | x86_64
|
x86 | x86_64
|
||||||
|
|||||||
@@ -115,7 +115,11 @@ class RDPClientQtFactory(rdp.ClientFactory):
|
|||||||
self._nego = security == "nego"
|
self._nego = security == "nego"
|
||||||
self._recodedPath = recodedPath
|
self._recodedPath = recodedPath
|
||||||
if self._nego:
|
if self._nego:
|
||||||
self._security = rdp.SecurityLevel.RDP_LEVEL_NLA
|
#compute start nego nla need credentials
|
||||||
|
if username != "" and password != "":
|
||||||
|
self._security = rdp.SecurityLevel.RDP_LEVEL_NLA
|
||||||
|
else:
|
||||||
|
self._security = rdp.SecurityLevel.RDP_LEVEL_SSL
|
||||||
else:
|
else:
|
||||||
self._security = security
|
self._security = security
|
||||||
self._w = None
|
self._w = None
|
||||||
@@ -190,7 +194,7 @@ def autoDetectKeyboardLayout():
|
|||||||
if os.name == 'posix':
|
if os.name == 'posix':
|
||||||
from subprocess import check_output
|
from subprocess import check_output
|
||||||
result = check_output(["setxkbmap", "-print"])
|
result = check_output(["setxkbmap", "-print"])
|
||||||
if "azerty" in result:
|
if 'azerty' in result:
|
||||||
return "fr"
|
return "fr"
|
||||||
elif os.name == 'nt':
|
elif os.name == 'nt':
|
||||||
import win32api, win32con, win32process
|
import win32api, win32con, win32process
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ class ProxyServer(rdp.RDPServerObserver):
|
|||||||
if self._client is None:
|
if self._client is None:
|
||||||
return
|
return
|
||||||
self._client._controller.sendKeyEventScancode(code, isPressed)
|
self._client._controller.sendKeyEventScancode(code, isPressed)
|
||||||
|
self._rss.keyScancode(code, isPressed)
|
||||||
|
|
||||||
def onKeyEventUnicode(self, code, isPressed):
|
def onKeyEventUnicode(self, code, isPressed):
|
||||||
"""
|
"""
|
||||||
@@ -113,6 +114,7 @@ class ProxyServer(rdp.RDPServerObserver):
|
|||||||
if self._client is None:
|
if self._client is None:
|
||||||
return
|
return
|
||||||
self._client._controller.sendKeyEventUnicode(code, isPressed)
|
self._client._controller.sendKeyEventUnicode(code, isPressed)
|
||||||
|
self._rss.keyUnicode(code, isPressed)
|
||||||
|
|
||||||
def onPointerEvent(self, x, y, button, isPressed):
|
def onPointerEvent(self, x, y, button, isPressed):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from PyQt4 import QtGui, QtCore
|
|||||||
|
|
||||||
from rdpy.core import log, rss
|
from rdpy.core import log, rss
|
||||||
from rdpy.ui.qt4 import QRemoteDesktop, RDPBitmapToQtImage
|
from rdpy.ui.qt4 import QRemoteDesktop, RDPBitmapToQtImage
|
||||||
|
from rdpy.core.scancode import scancodeToChar
|
||||||
log._LOG_LEVEL = log.Level.INFO
|
log._LOG_LEVEL = log.Level.INFO
|
||||||
|
|
||||||
class RssPlayerWidget(QRemoteDesktop):
|
class RssPlayerWidget(QRemoteDesktop):
|
||||||
@@ -45,9 +46,28 @@ class RssPlayerWidget(QRemoteDesktop):
|
|||||||
""" Not Handle """
|
""" Not Handle """
|
||||||
QRemoteDesktop.__init__(self, width, height, RssAdaptor())
|
QRemoteDesktop.__init__(self, width, height, RssAdaptor())
|
||||||
|
|
||||||
def drawInfos(self, domain, username, password, hostname):
|
class RssPlayerWindow(QtGui.QWidget):
|
||||||
QtGui.QMessageBox.about(self, "Credentials Event", "domain : %s\nusername : %s\npassword : %s\nhostname : %s" % (
|
"""
|
||||||
domain, username, password, hostname))
|
@summary: main window of rss player
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
super(RssPlayerWindow, self).__init__()
|
||||||
|
|
||||||
|
self._viewer = RssPlayerWidget(800, 600)
|
||||||
|
self._text = QtGui.QTextEdit()
|
||||||
|
self._text.setReadOnly(True)
|
||||||
|
self._text.setFixedHeight(150)
|
||||||
|
|
||||||
|
scrollViewer = QtGui.QScrollArea()
|
||||||
|
scrollViewer.setWidget(self._viewer)
|
||||||
|
|
||||||
|
layout = QtGui.QVBoxLayout()
|
||||||
|
layout.addWidget(scrollViewer, 1)
|
||||||
|
layout.addWidget(self._text, 2)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
self.setGeometry(0, 0, 800, 600)
|
||||||
|
|
||||||
def help():
|
def help():
|
||||||
print "Usage: rdpy-rssplayer [-h] rss_filepath"
|
print "Usage: rdpy-rssplayer [-h] rss_filepath"
|
||||||
@@ -64,16 +84,20 @@ def loop(widget, rssFile, nextEvent):
|
|||||||
|
|
||||||
if nextEvent.type.value == rss.EventType.UPDATE:
|
if nextEvent.type.value == rss.EventType.UPDATE:
|
||||||
image = RDPBitmapToQtImage(nextEvent.event.width.value, nextEvent.event.height.value, nextEvent.event.bpp.value, nextEvent.event.format.value == rss.UpdateFormat.BMP, nextEvent.event.data.value);
|
image = RDPBitmapToQtImage(nextEvent.event.width.value, nextEvent.event.height.value, nextEvent.event.bpp.value, nextEvent.event.format.value == rss.UpdateFormat.BMP, nextEvent.event.data.value);
|
||||||
widget.notifyImage(nextEvent.event.destLeft.value, nextEvent.event.destTop.value, image, nextEvent.event.destRight.value - nextEvent.event.destLeft.value + 1, nextEvent.event.destBottom.value - nextEvent.event.destTop.value + 1)
|
widget._viewer.notifyImage(nextEvent.event.destLeft.value, nextEvent.event.destTop.value, image, nextEvent.event.destRight.value - nextEvent.event.destLeft.value + 1, nextEvent.event.destBottom.value - nextEvent.event.destTop.value + 1)
|
||||||
|
|
||||||
elif nextEvent.type.value == rss.EventType.SCREEN:
|
elif nextEvent.type.value == rss.EventType.SCREEN:
|
||||||
widget.resize(nextEvent.event.width.value, nextEvent.event.height.value)
|
widget._viewer.resize(nextEvent.event.width.value, nextEvent.event.height.value)
|
||||||
|
|
||||||
elif nextEvent.type.value == rss.EventType.INFO:
|
elif nextEvent.type.value == rss.EventType.INFO:
|
||||||
widget.drawInfos(nextEvent.event.domain.value, nextEvent.event.username.value, nextEvent.event.password.value, nextEvent.event.hostname.value)
|
widget._text.append("Domain : %s\nUsername : %s\nPassword : %s\nHostname : %s\n" % (
|
||||||
|
nextEvent.event.domain.value, nextEvent.event.username.value, nextEvent.event.password.value, nextEvent.event.hostname.value))
|
||||||
|
elif nextEvent.type.value == rss.EventType.KEY_SCANCODE:
|
||||||
|
if nextEvent.event.isPressed.value == 0:
|
||||||
|
widget._text.moveCursor(QtGui.QTextCursor.End)
|
||||||
|
widget._text.insertPlainText(scancodeToChar(nextEvent.event.code.value))
|
||||||
|
|
||||||
elif nextEvent.type.value == rss.EventType.CLOSE:
|
elif nextEvent.type.value == rss.EventType.CLOSE:
|
||||||
widget.close()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
e = rssFile.nextEvent()
|
e = rssFile.nextEvent()
|
||||||
@@ -92,8 +116,10 @@ if __name__ == '__main__':
|
|||||||
filepath = args[0]
|
filepath = args[0]
|
||||||
#create application
|
#create application
|
||||||
app = QtGui.QApplication(sys.argv)
|
app = QtGui.QApplication(sys.argv)
|
||||||
widget = RssPlayerWidget(800, 600)
|
|
||||||
widget.show()
|
mainWindow = RssPlayerWindow()
|
||||||
|
mainWindow.show()
|
||||||
|
|
||||||
rssFile = rss.createReader(filepath)
|
rssFile = rss.createReader(filepath)
|
||||||
start(widget, rssFile)
|
start(mainWindow, rssFile)
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
||||||
@@ -39,7 +39,7 @@ def log(message):
|
|||||||
@summary: Main log function
|
@summary: Main log function
|
||||||
@param message: string to print
|
@param message: string to print
|
||||||
"""
|
"""
|
||||||
print message
|
print "[*] %s"%message
|
||||||
|
|
||||||
def error(message):
|
def error(message):
|
||||||
"""
|
"""
|
||||||
@@ -48,7 +48,7 @@ def error(message):
|
|||||||
"""
|
"""
|
||||||
if _LOG_LEVEL > Level.ERROR:
|
if _LOG_LEVEL > Level.ERROR:
|
||||||
return
|
return
|
||||||
log("ERROR : %s"%message)
|
log("ERROR:\t%s"%message)
|
||||||
|
|
||||||
def warning(message):
|
def warning(message):
|
||||||
"""
|
"""
|
||||||
@@ -57,7 +57,7 @@ def warning(message):
|
|||||||
"""
|
"""
|
||||||
if _LOG_LEVEL > Level.WARNING:
|
if _LOG_LEVEL > Level.WARNING:
|
||||||
return
|
return
|
||||||
log("WARNING : %s"%message)
|
log("WARNING:\t%s"%message)
|
||||||
|
|
||||||
def info(message):
|
def info(message):
|
||||||
"""
|
"""
|
||||||
@@ -66,7 +66,7 @@ def info(message):
|
|||||||
"""
|
"""
|
||||||
if _LOG_LEVEL > Level.INFO:
|
if _LOG_LEVEL > Level.INFO:
|
||||||
return
|
return
|
||||||
log("INFO : %s"%message)
|
log("INFO:\t%s"%message)
|
||||||
|
|
||||||
def debug(message):
|
def debug(message):
|
||||||
"""
|
"""
|
||||||
@@ -75,4 +75,4 @@ def debug(message):
|
|||||||
"""
|
"""
|
||||||
if _LOG_LEVEL > Level.DEBUG:
|
if _LOG_LEVEL > Level.DEBUG:
|
||||||
return
|
return
|
||||||
log("DEBUG : %s"%message)
|
log("DEBUG:\t%s"%message)
|
||||||
@@ -34,6 +34,8 @@ class EventType(object):
|
|||||||
SCREEN = 0x0002
|
SCREEN = 0x0002
|
||||||
INFO = 0x0003
|
INFO = 0x0003
|
||||||
CLOSE = 0x0004
|
CLOSE = 0x0004
|
||||||
|
KEY_UNICODE = 0x0005
|
||||||
|
KEY_SCANCODE = 0x0006
|
||||||
|
|
||||||
class UpdateFormat(object):
|
class UpdateFormat(object):
|
||||||
"""
|
"""
|
||||||
@@ -56,7 +58,7 @@ class Event(CompositeType):
|
|||||||
"""
|
"""
|
||||||
@summary: Closure for event factory
|
@summary: Closure for event factory
|
||||||
"""
|
"""
|
||||||
for c in [UpdateEvent, ScreenEvent, InfoEvent, CloseEvent]:
|
for c in [UpdateEvent, ScreenEvent, InfoEvent, CloseEvent, KeyEventScancode, KeyEventUnicode]:
|
||||||
if self.type.value == c._TYPE_:
|
if self.type.value == c._TYPE_:
|
||||||
return c(readLen = self.length)
|
return c(readLen = self.length)
|
||||||
log.debug("unknown event type : %s"%hex(self.type.value))
|
log.debug("unknown event type : %s"%hex(self.type.value))
|
||||||
@@ -123,6 +125,26 @@ class CloseEvent(CompositeType):
|
|||||||
def __init__(self, readLen = None):
|
def __init__(self, readLen = None):
|
||||||
CompositeType.__init__(self, readLen = readLen)
|
CompositeType.__init__(self, readLen = readLen)
|
||||||
|
|
||||||
|
class KeyEventUnicode(CompositeType):
|
||||||
|
"""
|
||||||
|
@summary: keyboard event (keylogger) as unicode event
|
||||||
|
"""
|
||||||
|
_TYPE_ = EventType.KEY_UNICODE
|
||||||
|
def __init__(self, readLen = None):
|
||||||
|
CompositeType.__init__(self, readLen = readLen)
|
||||||
|
self.code = UInt32Le()
|
||||||
|
self.isPressed = UInt8()
|
||||||
|
|
||||||
|
class KeyEventScancode(CompositeType):
|
||||||
|
"""
|
||||||
|
@summary: keyboard event (keylogger)
|
||||||
|
"""
|
||||||
|
_TYPE_ = EventType.KEY_SCANCODE
|
||||||
|
def __init__(self, readLen = None):
|
||||||
|
CompositeType.__init__(self, readLen = readLen)
|
||||||
|
self.code = UInt32Le()
|
||||||
|
self.isPressed = UInt8()
|
||||||
|
|
||||||
def timeMs():
|
def timeMs():
|
||||||
"""
|
"""
|
||||||
@return: {int} time stamp in milliseconds
|
@return: {int} time stamp in milliseconds
|
||||||
@@ -211,6 +233,28 @@ class FileRecorder(object):
|
|||||||
infoEvent.domain.value = domain
|
infoEvent.domain.value = domain
|
||||||
infoEvent.hostname.value = hostname
|
infoEvent.hostname.value = hostname
|
||||||
self.rec(infoEvent)
|
self.rec(infoEvent)
|
||||||
|
|
||||||
|
def keyUnicode(self, code, isPressed):
|
||||||
|
"""
|
||||||
|
@summary: record key event as unicode
|
||||||
|
@param code: unicode code
|
||||||
|
@param isPressed: True if a key press event
|
||||||
|
"""
|
||||||
|
keyEvent = KeyEventUnicode()
|
||||||
|
keyEvent.code.value = code
|
||||||
|
keyEvent.isPressed.value = 0 if isPressed else 1
|
||||||
|
self.rec(keyEvent)
|
||||||
|
|
||||||
|
def keyScancode(self, code, isPressed):
|
||||||
|
"""
|
||||||
|
@summary: record key event as scancode
|
||||||
|
@param code: scancode code
|
||||||
|
@param isPressed: True if a key press event
|
||||||
|
"""
|
||||||
|
keyEvent = KeyEventScancode()
|
||||||
|
keyEvent.code.value = code
|
||||||
|
keyEvent.isPressed.value = 0 if isPressed else 1
|
||||||
|
self.rec(keyEvent)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
60
rdpy/core/scancode.py
Normal file
60
rdpy/core/scancode.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||||
|
#
|
||||||
|
# This file is part of rdpy.
|
||||||
|
#
|
||||||
|
# rdpy is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Basic virtual scancode mapping
|
||||||
|
"""
|
||||||
|
|
||||||
|
_SCANCODE_QWERTY_ = {
|
||||||
|
0x10 : "q",
|
||||||
|
0x11 : "w",
|
||||||
|
0x12 : "e",
|
||||||
|
0x13 : "r",
|
||||||
|
0x14 : "t",
|
||||||
|
0x15 : "y",
|
||||||
|
0x16 : "u",
|
||||||
|
0x17 : "i",
|
||||||
|
0x18 : "o",
|
||||||
|
0x19 : "p",
|
||||||
|
0x1e : "a",
|
||||||
|
0x1f : "s",
|
||||||
|
0x20 : "d",
|
||||||
|
0x21 : "f",
|
||||||
|
0x22 : "g",
|
||||||
|
0x23 : "h",
|
||||||
|
0x24 : "j",
|
||||||
|
0x25 : "k",
|
||||||
|
0x26 : "l",
|
||||||
|
0x2c : "z",
|
||||||
|
0x2d : "x",
|
||||||
|
0x2e : "c",
|
||||||
|
0x2f : "v",
|
||||||
|
0x30 : "b",
|
||||||
|
0x31 : "n",
|
||||||
|
0x32 : "m"
|
||||||
|
}
|
||||||
|
|
||||||
|
def scancodeToChar(code):
|
||||||
|
"""
|
||||||
|
@summary: try to convert native code to char code
|
||||||
|
@return: char
|
||||||
|
"""
|
||||||
|
if not _SCANCODE_QWERTY_.has_key(code):
|
||||||
|
return "<unknown scancode %x>"%code
|
||||||
|
return _SCANCODE_QWERTY_[code];
|
||||||
@@ -287,11 +287,14 @@ class Client(PDULayer):
|
|||||||
@param dataPDU: DataPDU object
|
@param dataPDU: DataPDU object
|
||||||
"""
|
"""
|
||||||
if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
|
if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
|
||||||
|
#ignore 0 error code because is not an error code
|
||||||
|
if dataPDU.pduData.errorInfo.value == 0:
|
||||||
|
return
|
||||||
errorMessage = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value)
|
errorMessage = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value)
|
||||||
if data.ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo):
|
if data.ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo):
|
||||||
errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
|
errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
|
||||||
|
|
||||||
log.error("INFO PDU : %s"%errorMessage)
|
log.error("INFO PDU : %s"%errorMessage)
|
||||||
|
|
||||||
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()
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -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.0',
|
version='1.3.1',
|
||||||
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.
|
||||||
|
|||||||
Reference in New Issue
Block a user