bug fix + finish proxy

This commit is contained in:
speyrefitte
2014-07-24 17:48:12 +02:00
parent 4aa9d8c389
commit 6ba4f62ef4
9 changed files with 249 additions and 42 deletions

View File

@@ -138,5 +138,4 @@ if __name__ == '__main__':
from twisted.internet import reactor
reactor.connectTCP(ip, int(port), RDPClientQtFactory(width, height, username, password, domain))
reactor.runReturn()
app.exec_()
reactor.stop()
app.exec_()

View File

@@ -21,8 +21,6 @@
"""
RDP proxy recorder and spyer
Proxy RDP protocol
With admin account you can watch all currently session
For special session you can record
"""
import sys, os
@@ -43,14 +41,6 @@ class ProxyServer(rdp.RDPServerObserver):
rdp.RDPServerObserver.__init__(self, controller)
self._client = None
def onReady(self):
"""
Event use to inform state of server stack
Use to connect client
"""
width, height = self._controller.getScreen()
reactor.connectTCP("wav-glw-013", 3389, ProxyClientFactory(self, width, height))
def clientConnected(self, client):
"""
Event throw by client when it's ready
@@ -58,6 +48,52 @@ class ProxyServer(rdp.RDPServerObserver):
"""
self._client = client
self._controller.setColorDepth(self._client._controller.getColorDepth())
def onReady(self):
"""
Event use to inform state of server stack
Use to connect client
"""
width, height = self._controller.getScreen()
reactor.connectTCP("si-hyperv-002", 3389, ProxyClientFactory(self, width, height))
def onKeyEventScancode(self, code, isPressed):
"""
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
"""
#no client connected
if self._client is None:
return
self._client._controller.sendKeyEventScancode(code, isPressed)
def onKeyEventUnicode(self, code, isPressed):
"""
Event call when a keyboard event is catch in unicode format
@param code: unicode of key
@param isPressed: True if key is down
"""
#no client connected
if self._client is None:
return
self._client._controller.sendKeyEventUnicode(code, isPressed)
def onPointerEvent(self, x, y, button, isPressed):
"""
Event call on mouse event
@param x: x position
@param y: y position
@param button: 1, 2 or 3 button
@param isPressed: True if mouse button is pressed
"""
#no client connected
if self._client is None:
return
self._client._controller.sendPointerEvent(x, y, button, isPressed)
class ProxyClient(rdp.RDPClientObserver):
"""

View File

@@ -151,6 +151,12 @@ class RawLayer(protocol.Protocol, LayerAutomata, StreamSender):
"""
#join two scheme
self.connect()
def close(self):
"""
Close raw layer
"""
self.transport.loseConnection()
def expect(self, expectedLen, callback = None):
"""

View File

@@ -240,8 +240,8 @@ class ClientCoreData(CompositeType):
self.postBeta2ColorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP)
self.clientProductId = UInt16Le(1)
self.serialNumber = UInt32Le(0)
self.highColorDepth = UInt16Le(HighColor.HIGH_COLOR_15BPP)
self.supportedColorDepths = UInt16Le(Support.RNS_UD_15BPP_SUPPORT)
self.highColorDepth = UInt16Le(HighColor.HIGH_COLOR_24BPP)
self.supportedColorDepths = UInt16Le(Support.RNS_UD_15BPP_SUPPORT | Support.RNS_UD_16BPP_SUPPORT | Support.RNS_UD_24BPP_SUPPORT | Support.RNS_UD_32BPP_SUPPORT)
self.earlyCapabilityFlags = UInt16Le(CapabilityFlags.RNS_UD_CS_SUPPORT_ERRINFO_PDU)
self.clientDigProductId = String("\x00"*64, readLen = UInt8(64))
self.connectionType = UInt8()

View File

@@ -25,7 +25,7 @@ The main channel is the graphical channel.
It exist channel for file system order, audio channel, clipboard etc...
"""
from rdpy.network.layer import LayerAutomata, StreamSender, Layer
from rdpy.network.type import sizeof, Stream, UInt8, UInt16Le
from rdpy.network.type import sizeof, Stream, UInt8, UInt16Le, String
from rdpy.base.error import InvalidExpectedDataException, InvalidValue, InvalidSize
from rdpy.protocol.rdp.ber import writeLength
import rdpy.base.log as log
@@ -137,6 +137,14 @@ class MCSLayer(LayerAutomata):
#receive opcode
self._receiveOpcode = receiveOpcode
def close(self):
"""
Send disconnect provider ultimatum
"""
self._transport.send((UInt8(self.writeMCSPDUHeader(DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM, 1)),
per.writeEnumerates(0x80), String("\x00" * 6)))
self._transport.close()
def allChannelConnected(self):
"""
All channels are connected to MCS layer

View File

@@ -783,8 +783,8 @@ class ClientInputEventPDU(CompositeType):
class ShutdownRequestPDU(CompositeType):
"""
PDU use to signal that the session will be closzed are connected
server -> client
PDU use to signal that the session will be closed
client -> server
"""
_PDUTYPE2_ = PDUType2.PDUTYPE2_SHUTDOWN_REQUEST
def __init__(self):
@@ -792,7 +792,7 @@ class ShutdownRequestPDU(CompositeType):
class ShutdownDeniedPDU(CompositeType):
"""
PDU use to signal that the session will be closzed are connected
PDU use to signal which the session will be closed is connected
server -> client
"""
_PDUTYPE2_ = PDUType2.PDUTYPE2_SHUTDOWN_DENIED
@@ -842,17 +842,17 @@ class FastPathUpdatePDU(CompositeType):
CompositeType.__init__(self)
self.updateHeader = UInt8(lambda:updateData.__class__._FASTPATH_UPDATE_TYPE_)
self.compressionFlags = UInt8(conditional = lambda:((self.updateHeader.value >> 4) & FastPathOutputCompression.FASTPATH_OUTPUT_COMPRESSION_USED))
self.size = UInt16Le()
self.size = UInt16Le(lambda:sizeof(self.updateData))
def UpdateDataFactory():
"""
Create correct object in accordance to self.updateHeader field
"""
if (self.updateHeader.value & 0xf) == FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
return (UInt16Le(FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP, constant = True), BitmapUpdateDataPDU(readLen = self.size - 2))
else:
log.debug("unknown Fast Path PDU update data type : %s"%hex(self.updateHeader.value & 0xf))
return String()
for c in [FastPathBitmapUpdateDataPDU]:
if (self.updateHeader.value & 0xf) == c._FASTPATH_UPDATE_TYPE_:
return c()
log.debug("unknown Fast Path PDU update data type : %s"%hex(self.updateHeader.value & 0xf))
return String()
if updateData is None:
updateData = FactoryType(UpdateDataFactory)
@@ -867,7 +867,6 @@ class BitmapUpdateDataPDU(CompositeType):
@see: http://msdn.microsoft.com/en-us/library/dd306368.aspx
"""
_UPDATE_TYPE_ = UpdateType.UPDATETYPE_BITMAP
_FASTPATH_UPDATE_TYPE_ = FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP
def __init__(self, readLen = None):
"""
@@ -946,7 +945,20 @@ class BitmapData(CompositeType):
self.bitmapLength = UInt16Le(lambda:(sizeof(self.bitmapComprHdr) + sizeof(self.bitmapDataStream)))
self.bitmapComprHdr = BitmapCompressedDataHeader(bodySize = lambda:sizeof(self.bitmapDataStream), scanWidth = lambda:self.width.value, uncompressedSize = lambda:(self.width.value * self.height.value * self.bitsPerPixel.value), conditional = lambda:((self.flags.value | BitmapFlag.BITMAP_COMPRESSION) and not (self.flags.value | BitmapFlag.NO_BITMAP_COMPRESSION_HDR)))
self.bitmapDataStream = String(bitmapDataStream, readLen = UInt16Le(lambda:(self.bitmapLength.value if (self.flags.value | BitmapFlag.NO_BITMAP_COMPRESSION_HDR) else self.bitmapComprHdr.cbCompMainBodySize.value)))
class FastPathBitmapUpdateDataPDU(CompositeType):
"""
Fast path version of bitmap update PDU
@see: http://msdn.microsoft.com/en-us/library/dd306368.aspx
"""
_FASTPATH_UPDATE_TYPE_ = FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP
def __init__(self):
CompositeType.__init__(self)
self.header = UInt16Le(FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP, constant = True)
self.numberRectangles = UInt16Le(lambda:len(self.rectangles._array))
self.rectangles = ArrayType(BitmapData, readLen = self.numberRectangles)
class SlowPathInputEvent(CompositeType):
"""
PDU use in slow-path sending client inputs

View File

@@ -64,6 +64,12 @@ class PDUServerListener(object):
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUServerListener"))
def onSlowPathInput(self, slowPathInputEvents):
"""
Event call when slow path input are available
@param slowPathInputEvents: [data.SlowPathInputEvent]
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onSlowPathInput", "PDUServerListener"))
class PDULayer(LayerAutomata):
"""
@@ -128,6 +134,8 @@ class Client(PDULayer, tpkt.FastPathListener):
"""
PDULayer.__init__(self)
self._listener = listener
#enable or not fast path
self._fastPathSender = None
def connect(self):
"""
@@ -138,12 +146,22 @@ class Client(PDULayer, tpkt.FastPathListener):
self.sendInfoPkt()
#next state is license info PDU
self.setNextState(self.recvLicenceInfo)
#check if client support fast path message
self._clientFastPathSupported = False
def close(self):
"""
Send PDU close packet and call close method on transport method
"""
self.sendDataPDU(data.ShutdownRequestPDU())
self._transport.close()
#self.sendDataPDU(data.ShutdownRequestPDU())
def setFastPathSender(self, fastPathSender):
"""
@param fastPathSender: tpkt.FastPathSender
@note: implement tpkt.FastPathListener
"""
self._fastPathSender = fastPathSender
def recvLicenceInfo(self, s):
"""
@@ -290,7 +308,7 @@ class Client(PDULayer, tpkt.FastPathListener):
fastPathPDU = data.FastPathUpdatePDU()
fastPathS.readType(fastPathPDU)
if fastPathPDU.updateHeader.value == data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
self._listener.onUpdate(fastPathPDU.updateData[1].rectangles._array)
self._listener.onUpdate(fastPathPDU.updateData.rectangles._array)
def readDataPDU(self, dataPDU):
"""
@@ -333,7 +351,9 @@ class Client(PDULayer, tpkt.FastPathListener):
generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR
if not self._fastPathSender is None:
generalCapability.extraFlags.value |= caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
#init bitmap capability
bitmapCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].capability
@@ -390,7 +410,7 @@ class Client(PDULayer, tpkt.FastPathListener):
pdu.slowPathInputEvents._array = [data.SlowPathInputEvent(x) for x in pointerEvents]
self.sendDataPDU(pdu)
class Server(PDULayer):
class Server(PDULayer, tpkt.FastPathListener):
"""
Server Automata of PDU layer
"""
@@ -400,6 +420,8 @@ class Server(PDULayer):
"""
PDULayer.__init__(self)
self._listener = listener
#fast path layer
self._fastPathSender = None
def connect(self):
"""
@@ -408,6 +430,13 @@ class Server(PDULayer):
"""
self.setNextState(self.recvInfoPkt)
def setFastPathSender(self, fastPathSender):
"""
@param fastPathSender: tpkt.FastPathSender
@note: implement tpkt.FastPathListener
"""
self._fastPathSender = fastPathSender
def recvInfoPkt(self, s):
"""
Receive info packet from client
@@ -449,6 +478,9 @@ class Server(PDULayer):
for cap in pdu.pduMessage.capabilitySets._array:
self._clientCapabilities[cap.capabilitySetType] = cap
#find use full flag
self._clientFastPathSupported = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability.extraFlags.value & caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
self.setNextState(self.recvClientSynchronizePDU)
def recvClientSynchronizePDU(self, s):
@@ -538,6 +570,18 @@ class Server(PDULayer):
errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
log.error("INFO PDU : %s"%errorMessage)
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_INPUT:
self._listener.onSlowPathInput(dataPDU.pduData.slowPathInputEvents._array)
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SHUTDOWN_REQUEST:
self._transport.close()
def recvFastPath(self, fastPathS):
"""
Implement FastPathListener interface
Fast path is needed by RDP 8.0
@param fastPathS: Stream that contain fast path data
"""
pass
def sendLicensingErrorMessage(self):
"""
@@ -604,7 +648,16 @@ class Server(PDULayer):
#check bitmap header for client that want it (very old client)
if self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability.extraFlags.value & caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR:
for bitmapData in bitmapDatas:
bitmapData.flags.value |= data.BitmapFlag.NO_BITMAP_COMPRESSION_HDR
updateDataPDU = data.BitmapUpdateDataPDU()
updateDataPDU.rectangles._array = bitmapDatas
self.sendDataPDU(data.UpdateDataPDU(updateDataPDU))
if bitmapData.flags.value & data.BitmapFlag.BITMAP_COMPRESSION:
bitmapData.flags.value |= data.BitmapFlag.NO_BITMAP_COMPRESSION_HDR
if self._clientFastPathSupported and not self._fastPathSender is None:
#fast path case
fastPathUpdateDataPDU = data.FastPathBitmapUpdateDataPDU()
fastPathUpdateDataPDU.rectangles._array = bitmapDatas
self._fastPathSender.sendFastPath(data.FastPathUpdatePDU(fastPathUpdateDataPDU))
else:
#slow path case
updateDataPDU = data.BitmapUpdateDataPDU()
updateDataPDU.rectangles._array = bitmapDatas
self.sendDataPDU(data.UpdateDataPDU(updateDataPDU))

View File

@@ -60,6 +60,12 @@ class RDPClientController(pdu.layer.PDUClientListener):
@return: color depth set by the server (15, 16, 24)
"""
return self._pduLayer._serverCapabilities[pdu.caps.CapsType.CAPSTYPE_BITMAP].capability.preferredBitsPerPixel.value
def getKeyEventUniCodeSupport(self):
"""
@return: True if server support unicode input
"""
return self._pduLayer._serverCapabilities[pdu.caps.CapsType.CAPSTYPE_INPUT].capability.inputFlags.value & pdu.caps.InputFlags.INPUT_FLAG_UNICODE
def setPerformanceSession(self):
"""
@@ -298,6 +304,12 @@ class RDPServerController(pdu.layer.PDUServerListener):
#restart connection sequence
self._isReady = False
self._pduLayer.sendPDU(pdu.data.DeactiveAllPDU())
def setKeyEventUnicodeSupport(self):
"""
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):
"""
@@ -309,6 +321,31 @@ class RDPServerController(pdu.layer.PDUServerListener):
self._sendReady = True
for observer in self._serverObserver:
observer.onReady()
def onSlowPathInput(self, slowPathInputEvents):
"""
Event call when slow path input are available
@param slowPathInputEvents: [data.SlowPathInputEvent]
"""
for observer in self._serverObserver:
for event in slowPathInputEvents:
#scan code
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))
#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))
#mouse event
elif event.messageType.value == pdu.data.InputMessageType.INPUT_EVENT_MOUSE:
isPressed = event.slowPathInputData.pointerFlags.value & pdu.data.PointerFlag.PTRFLAGS_DOWN
button = 0
if event.slowPathInputData.pointerFlags.value & pdu.data.PointerFlag.PTRFLAGS_BUTTON1:
button = 1
elif event.slowPathInputData.pointerFlags.value & pdu.data.PointerFlag.PTRFLAGS_BUTTON2:
button = 2
elif event.slowPathInputData.pointerFlags.value & pdu.data.PointerFlag.PTRFLAGS_BUTTON3:
button = 3
observer.onPointerEvent(event.slowPathInputData.xPos.value, event.slowPathInputData.yPos.value, button, isPressed)
def sendUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
"""
@@ -428,4 +465,30 @@ class RDPServerObserver(object):
"""
Stack is ready and connected
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPServerObserver"))
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPServerObserver"))
def onKeyEventScancode(self, code, isPressed):
"""
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
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onKeyEventScanCode", "RDPServerObserver"))
def onKeyEventUnicode(self, code, isPressed):
"""
Event call when a keyboard event is catch in unicode format
@param code: unicode of key
@param isPressed: True if key is down
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onKeyEventUnicode", "RDPServerObserver"))
def onPointerEvent(self, x, y, button, isPressed):
"""
Event call on mouse event
@param x: x position
@param y: y position
@param button: 1, 2 or 3 button
@param isPressed: True if mouse button is pressed
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onPointerEvent", "RDPServerObserver"))

View File

@@ -26,6 +26,14 @@ from rdpy.network.layer import RawLayer
from rdpy.network.type import UInt8, UInt16Be, sizeof
from rdpy.base.error import CallPureVirtualFuntion
class Action(object):
"""
@see: http://msdn.microsoft.com/en-us/library/cc240621.aspx
@see: http://msdn.microsoft.com/en-us/library/cc240589.aspx
"""
FASTPATH_ACTION_FASTPATH = 0x0
FASTPATH_ACTION_X224 = 0x3
class FastPathListener(object):
"""
Fast path packet listener
@@ -36,19 +44,33 @@ class FastPathListener(object):
Call when fast path packet is received
@param fastPathS: Stream
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "StreamListener"))
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recvFastPath", "recvFastPath"))
def setFastPathSender(self, fastPathSender):
"""
@param fastPathSender: FastPathSender
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "setFastPathSender", "recvFastPath"))
class TPKT(RawLayer):
class FastPathSender(object):
"""
Fast path send capability
"""
def sendFastPath(self, fastPathS):
"""
@param fastPathS: type transform to stream and send as fastpath
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendFastPath", "FastPathSender"))
class TPKT(RawLayer, FastPathSender):
"""
TPKT layer in RDP protocol stack
This layer only handle size of packet and determine if is a fast path packet
"""
#first byte of classic tpkt header
TPKT_PACKET = 3
def __init__(self, presentation, fastPathListener):
"""
@param presentation: presentation layer, in RDP case is TPDU layer
@param fastPathListener: FastPathListener
"""
RawLayer.__init__(self, presentation)
#last packet version read from header
@@ -57,6 +79,8 @@ class TPKT(RawLayer):
self._lastShortLength = UInt8()
#fast path listener
self._fastPathListener = fastPathListener
#set me as fast path sender
fastPathListener.setFastPathSender(self)
def connect(self):
"""
@@ -77,7 +101,7 @@ class TPKT(RawLayer):
#first read packet version
data.readType(self._lastPacketVersion)
#classic packet
if self._lastPacketVersion.value == TPKT.TPKT_PACKET:
if self._lastPacketVersion.value == Action.FASTPATH_ACTION_X224:
#padding
data.readType(UInt8())
#read end header
@@ -136,4 +160,10 @@ class TPKT(RawLayer):
Send encompassed data
@param message: network.Type message to send
"""
RawLayer.send(self, (UInt8(TPKT.TPKT_PACKET), UInt8(0), UInt16Be(sizeof(message) + 4), message))
RawLayer.send(self, (UInt8(Action.FASTPATH_ACTION_X224), UInt8(0), UInt16Be(sizeof(message) + 4), message))
def sendFastPath(self, fastPathS):
"""
@param fastPathS: type transform to stream and send as fastpath
"""
RawLayer.send(self, (UInt8(Action.FASTPATH_ACTION_FASTPATH), UInt16Be((sizeof(fastPathS) + 3) | 0x8000), fastPathS))