Update: Start MCS layer

This commit is contained in:
citronneur
2020-04-21 22:02:35 +02:00
parent 018c59fe42
commit 73b97d6929
8 changed files with 382 additions and 346 deletions

View File

@@ -26,6 +26,7 @@ import asyncio
from rdpy.core import tpkt, x224
from rdpy.core.nla import ntlm
from rdpy.core.t125 import mcs
from rdpy.model.message import UInt8
if __name__ == '__main__':
@@ -37,7 +38,9 @@ if __name__ == '__main__':
'127.0.0.1', 33389)
x224_layer = await x224.connect(tpkt.Tpkt(reader, writer), ntlm.NTLMv2("", "sylvain", "sylvain"))
await x224_layer.write(UInt8(8))
mcs_layer = mcs.Client(x224_layer)
await mcs_layer.connect()
await asyncio.sleep(10)
print("foooooooooooooooooooo")

View File

@@ -99,8 +99,8 @@ class RDPClientController(PDUClientListener):
@param height: height in pixel of screen
"""
#set screen definition in MCS layer
self._mcsLayer._clientSettings.getBlock(gcc.MessageType.CS_CORE).desktopHeight.value = height
self._mcsLayer._clientSettings.getBlock(gcc.MessageType.CS_CORE).desktopWidth.value = width
self._mcsLayer._clientSettings.get_block(gcc.MessageType.CS_CORE).desktopHeight.value = height
self._mcsLayer._clientSettings.get_block(gcc.MessageType.CS_CORE).desktopWidth.value = width
def setUsername(self, username):
"""

View File

@@ -22,22 +22,25 @@ Basic Encoding Rules use in RDP.
ASN.1 standard
"""
from rdpy.model.type import UInt8, UInt16Be, UInt32Be, Buffer
from rdpy.model.message import UInt8, UInt16Be, UInt32Be, Buffer
from rdpy.model.error import InvalidExpectedDataException, InvalidSize
class BerPc(object):
class BerPc:
BER_PC_MASK = 0x20
BER_PRIMITIVE = 0x00
BER_CONSTRUCT = 0x20
class Class(object):
class Class:
BER_CLASS_MASK = 0xC0
BER_CLASS_UNIV = 0x00
BER_CLASS_APPL = 0x40
BER_CLASS_CTXT = 0x80
BER_CLASS_PRIV = 0xC0
class Tag(object):
class Tag:
BER_TAG_MASK = 0x1F
BER_TAG_BOOLEAN = 0x01
BER_TAG_INTEGER = 0x02
@@ -48,6 +51,7 @@ class Tag(object):
BER_TAG_SEQUENCE = 0x10
BER_TAG_SEQUENCE_OF = 0x10
def berPC(pc):
"""
@summary: Return BER_CONSTRUCT if true
@@ -60,6 +64,7 @@ def berPC(pc):
else:
return BerPc.BER_PRIMITIVE
def readLength(s):
"""
@summary: Read length of BER structure
@@ -84,6 +89,7 @@ def readLength(s):
size = length
return size.value
def writeLength(size):
"""
@summary: Return structure length as expected in BER specification
@@ -95,6 +101,7 @@ def writeLength(size):
else:
return UInt8(size)
def readUniversalTag(s, tag, pc):
"""
@summary: Read tag of BER packet
@@ -106,6 +113,7 @@ def readUniversalTag(s, tag, pc):
s.read_type(byte)
return byte.value == ((Class.BER_CLASS_UNIV | berPC(pc)) | (Tag.BER_TAG_MASK & tag))
def writeUniversalTag(tag, pc):
"""
@summary: Return universal tag byte
@@ -115,6 +123,7 @@ def writeUniversalTag(tag, pc):
"""
return UInt8((Class.BER_CLASS_UNIV | berPC(pc)) | (Tag.BER_TAG_MASK & tag))
def readApplicationTag(s, tag):
"""
@summary: Read application tag
@@ -136,6 +145,7 @@ def readApplicationTag(s, tag):
return readLength(s)
def writeApplicationTag(tag, size):
"""
@summary: Return structure that represent BER application tag

View File

@@ -23,7 +23,7 @@ http://msdn.microsoft.com/en-us/library/cc240508.aspx
"""
from hashlib import md5
from rdpy.model.type import UInt8, UInt16Le, UInt32Le, CompositeType, CallableValue, Buffer, Stream, sizeof, FactoryType, ArrayType
from rdpy.model.message import UInt8, UInt16Le, UInt32Le, CompositeType, Buffer, Stream, sizeof, FactoryType, ArrayType
from rdpy.core.t125 import per, mcs
from rdpy.model.error import InvalidExpectedDataException
from rdpy.model import log
@@ -32,8 +32,8 @@ import rdpy.security.rsa_wrapper as rsa
t124_02_98_oid = ( 0, 0, 20, 124, 0, 1 )
h221_cs_key = "Duca";
h221_sc_key = "McDn";
h221_cs_key = b"Duca";
h221_sc_key = b"McDn";
class MessageType(object):
"""
@@ -209,42 +209,46 @@ class CertificateType(object):
CERT_CHAIN_VERSION_1 = 0x00000001
CERT_CHAIN_VERSION_2 = 0x00000002
class DataBlock(CompositeType):
"""
@summary: Block settings
"""
def __init__(self, dataBlock = None):
CompositeType.__init__(self)
self.type = UInt16Le(lambda:self.dataBlock.__class__._TYPE_)
def __init__(self, data_block=None):
super().__init__()
self.type = UInt16Le(lambda:data_block._TYPE_)
self.length = UInt16Le(lambda: sizeof(self))
def DataBlockFactory():
def factory():
"""
@summary: build settings in accordance of type self.type.value
"""
for c in [ClientCoreData, ClientSecurityData, ClientNetworkData, ServerCoreData, ServerNetworkData, ServerSecurityData]:
gcc_type = [
ClientCoreData, ClientSecurityData, ClientNetworkData,
ServerCoreData, ServerNetworkData, ServerSecurityData
]
for c in gcc_type:
if self.type.value == c._TYPE_:
return c(readLen = self.length - 4)
return c(read_len=lambda: (self.length.value - 4))
log.debug("unknown GCC block type : %s"%hex(self.type.value))
# read entire packet
return Buffer(readLen =self.length - 4)
return Buffer(read_len=lambda: (self.length.value - 4))
if dataBlock is None:
dataBlock = FactoryType(DataBlockFactory)
elif not "_TYPE_" in dataBlock.__class__.__dict__:
if data_block is None:
data_block = FactoryType(factory)
elif "_TYPE_" not in data_block.__class__.__dict__:
raise InvalidExpectedDataException("Try to send an invalid GCC blocks")
self.dataBlock = dataBlock
self.dataBlock = data_block
class ClientCoreData(CompositeType):
"""
@summary: Class that represent core setting of client
@see: http://msdn.microsoft.com/en-us/library/cc240510.aspx
"""
_TYPE_ = MessageType.CS_CORE
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
def __init__(self, read_len=None):
super().__init__(read_len=read_len)
self.rdpVersion = UInt32Le(Version.RDP_VERSION_5_PLUS)
self.desktopWidth = UInt16Le(1280)
self.desktopHeight = UInt16Le(800)
@@ -252,62 +256,59 @@ class ClientCoreData(CompositeType):
self.sasSequence = UInt16Le(Sequence.RNS_UD_SAS_DEL)
self.kbdLayout = UInt32Le(KeyboardLayout.US)
self.clientBuild = UInt32Le(3790)
self.clientName = Buffer("rdpy" + "\x00" * 11, readLen = CallableValue(32), unicode = True)
self.clientName = Buffer(b"rdpy" + b"\x00" * 11, read_len=lambda: 32)
self.keyboardType = UInt32Le(KeyboardType.IBM_101_102_KEYS)
self.keyboardSubType = UInt32Le(0)
self.keyboardFnKeys = UInt32Le(12)
self.imeFileName = Buffer("\x00" * 64, readLen = CallableValue(64), optional = True)
self.imeFileName = Buffer(b"\x00" * 64, read_len=lambda: 64, optional=True)
self.postBeta2ColorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP, optional=True)
self.clientProductId = UInt16Le(1, optional=True)
self.serialNumber = UInt32Le(0, optional=True)
self.highColorDepth = UInt16Le(HighColor.HIGH_COLOR_24BPP, optional=True)
self.supportedColorDepths = UInt16Le(Support.RNS_UD_15BPP_SUPPORT | Support.RNS_UD_16BPP_SUPPORT | Support.RNS_UD_24BPP_SUPPORT | Support.RNS_UD_32BPP_SUPPORT, optional=True)
self.earlyCapabilityFlags = UInt16Le(CapabilityFlags.RNS_UD_CS_SUPPORT_ERRINFO_PDU, optional=True)
self.clientDigProductId = Buffer("\x00" * 64, readLen = CallableValue(64), optional = True)
self.clientDigProductId = Buffer(b"\x00" * 64, read_len=lambda: 64, optional=True)
self.connectionType = UInt8(optional=True)
self.pad1octet = UInt8(optional=True)
self.serverSelectedProtocol = UInt32Le(optional=True)
class ServerCoreData(CompositeType):
"""
@summary: Server side core settings structure
@see: http://msdn.microsoft.com/en-us/library/cc240517.aspx
"""
_TYPE_ = MessageType.SC_CORE
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
def __init__(self, read_len=None):
super().__init__(read_len=read_len)
self.rdpVersion = UInt32Le(Version.RDP_VERSION_5_PLUS)
self.clientRequestedProtocol = UInt32Le(optional=True)
self.earlyCapabilityFlags = UInt32Le(optional=True)
class ClientSecurityData(CompositeType):
"""
@summary: Client security setting
@see: http://msdn.microsoft.com/en-us/library/cc240511.aspx
"""
_TYPE_ = MessageType.CS_SECURITY
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
def __init__(self, read_len=None):
super().__init__(read_len=read_len)
self.encryptionMethods = UInt32Le(EncryptionMethod.ENCRYPTION_FLAG_40BIT | EncryptionMethod.ENCRYPTION_FLAG_56BIT | EncryptionMethod.ENCRYPTION_FLAG_128BIT)
self.extEncryptionMethods = UInt32Le()
class ServerSecurityData(CompositeType):
"""
@summary: Server security settings
@see: http://msdn.microsoft.com/en-us/library/cc240518.aspx
"""
_TYPE_ = MessageType.SC_SECURITY
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
def __init__(self, read_len=None):
super().__init__(read_len=read_len)
self.encryptionMethod = UInt32Le()
self.encryptionLevel = UInt32Le()
self.serverRandomLen = UInt32Le(0x00000020, constant = True, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
self.serverCertLen = UInt32Le(lambda:sizeof(self.serverCertificate), conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
self.serverRandom = Buffer(readLen = self.serverRandomLen, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
self.serverCertificate = ServerCertificate(readLen = self.serverCertLen, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
self.serverRandomLen = UInt32Le(0x00000020, constant=True, conditional=lambda: not(self.encryptionMethod.value == 0 and self.encryptionLevel.value == 0))
self.serverCertLen = UInt32Le(lambda: sizeof(self.serverCertificate), conditional=lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel.value == 0))
self.serverRandom = Buffer(read_len=lambda: self.serverRandomLen.value, conditional=lambda: not(self.encryptionMethod.value == 0 and self.encryptionLevel.value == 0))
self.serverCertificate = ServerCertificate(readLen=lambda: self.serverCertLen.value, conditional=lambda: not(self.encryptionMethod.value == 0 and self.encryptionLevel.value == 0))
class ServerCertificate(CompositeType):
"""
@@ -448,79 +449,62 @@ class RSAPublicKey(CompositeType):
self.datalen = UInt32Le(lambda:((self.bitlen.value / 8) - 1))
self.pubExp = UInt32Le()
self.modulus = Buffer(readLen = CallableValue(lambda:(self.keylen.value - 8)))
self.padding = Buffer("\x00" * 8, readLen = CallableValue(8))
self.padding = Buffer(b"\x00" * 8, readLen = CallableValue(8))
class ChannelDef(CompositeType):
"""
Channels structure share between client and server
@see: http://msdn.microsoft.com/en-us/library/cc240513.aspx
"""
def __init__(self, name = "", options = 0):
CompositeType.__init__(self)
def __init__(self, name=b""):
super().__init__()
# name of channel
self.name = Buffer(name[0:8] + "\x00" * (8 - len(name)), readLen = CallableValue(8))
self.name = Buffer(name[0:8] + b"\x00" * (8 - len(name)), read_len=lambda: 8)
# unknown
self.options = UInt32Le()
class ClientNetworkData(CompositeType):
"""
@summary: GCC client network block
All channels asked by client are listed here
@see: http://msdn.microsoft.com/en-us/library/cc240512.aspx
"""
_TYPE_ = MessageType.CS_NET
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
self.channelCount = UInt32Le(lambda:len(self.channelDefArray._array))
self.channelDefArray = ArrayType(ChannelDef, readLen = self.channelCount)
def __init__(self, read_len=None):
CompositeType.__init__(self, read_len=read_len)
self.channelCount = UInt32Le(lambda: len(self.channelDefArray))
self.channelDefArray = ArrayType(ChannelDef, read_len=lambda: self.channelCount.value)
class ServerNetworkData(CompositeType):
"""
@summary: GCC server network block
All channels asked by client are listed here
@see: All channels asked by client are listed here
"""
_TYPE_ = MessageType.SC_NET
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
def __init__(self, read_len=None):
super().__init__(read_len=read_len)
self.MCSChannelId = UInt16Le(mcs.Channel.MCS_GLOBAL_CHANNEL)
self.channelCount = UInt16Le(lambda:len(self.channelIdArray._array))
self.channelIdArray = ArrayType(UInt16Le, readLen = self.channelCount)
self.channelCount = UInt16Le(lambda: len(self.channelIdArray))
self.channelIdArray = ArrayType(UInt16Le, read_len=lambda: self.channelCount.value)
self.pad = UInt16Le(conditional=lambda: ((self.channelCount.value % 2) == 1))
class Settings(CompositeType):
"""
@summary: Class which group all clients settings supported by RDPY
"""
def __init__(self, init = [], readLen = None):
CompositeType.__init__(self, readLen = readLen)
def __init__(self, init=None, read_len=None):
super().__init__(read_len=read_len)
self.settings = ArrayType(DataBlock, [DataBlock(i) for i in init])
def getBlock(self, messageType):
def get_block(self, message_type):
"""
@param messageType: type of block
@return: specific block of type messageType
"""
for i in self.settings._array:
if i.type.value == messageType:
if i.type.value == message_type:
return i.dataBlock
return None
def __getattr__(self, name):
"""
@summary: Magic function for better access
@return: _value parameter
"""
if not name in MessageType.__dict__:
return None
return self.getBlock(MessageType.__dict__[name])
def clientSettings():
def client_settings():
"""
@summary: Build settings for client
@return: Settings
"""
return Settings([ClientCoreData(), ClientNetworkData(), ClientSecurityData()])
@@ -578,9 +562,9 @@ def readConferenceCreateResponse(s):
raise InvalidExpectedDataException("cannot read h221_sc_key")
length = per.readLength(s)
serverSettings = Settings(readLen = CallableValue(length))
s.read_type(serverSettings)
return serverSettings
server_settings = Settings(read_len=lambda: length)
s.read_type(server_settings)
return server_settings
def writeConferenceCreateRequest(userData):
"""
@@ -593,7 +577,7 @@ def writeConferenceCreateRequest(userData):
return (per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
per.writeLength(len(userDataStream.getvalue()) + 14), per.writeChoice(0),
per.writeSelection(0x08), per.writeNumericString("1", 1), per.writePadding(1),
per.writeSelection(0x08), per.writeNumericString(b"1", 1), per.writePadding(1),
per.writeNumberOfSet(1), per.writeChoice(0xc0),
per.writeOctetStream(h221_cs_key, 4), per.writeOctetStream(userDataStream.getvalue()))

View File

@@ -24,6 +24,9 @@ Each channel have a particular role.
The main channel is the graphical channel.
It exist channel for file system order, audio channel, clipboard etc...
"""
from typing import Tuple
from rdpy.core import x224
from rdpy.model.layer import LayerAutomata, IStreamSender, Layer
from rdpy.model.message import sizeof, Stream, UInt8, UInt16Le, Buffer
from rdpy.model.error import InvalidExpectedDataException, InvalidValue, InvalidSize, CallPureVirtualFuntion
@@ -33,16 +36,16 @@ import rdpy.model.log as log
from rdpy.core.t125 import ber, gcc, per
import rdpy.security.rsa_wrapper as rsa
class Message(object):
class Message:
"""
@summary: Message type
"""
MCS_TYPE_CONNECT_INITIAL = 0x65
MCS_TYPE_CONNECT_RESPONSE = 0x66
class DomainMCSPDU:
"""
@summary: Domain MCS PDU header
"""
ERECT_DOMAIN_REQUEST = 1
DISCONNECT_PROVIDER_ULTIMATUM = 8
@@ -53,17 +56,19 @@ class DomainMCSPDU:
SEND_DATA_REQUEST = 25
SEND_DATA_INDICATION = 26
class Channel:
"""
@summary: Channel id of main channels use in RDP
"""
MCS_GLOBAL_CHANNEL = 1003
MCS_USERCHANNEL_BASE = 1001
class IGCCConfig(object):
"""
@summary: Channel information
"""
def getUserId(self):
"""
@return: {integer} mcs user id
@@ -83,14 +88,82 @@ class IGCCConfig(object):
@return: {gcc.Settings} mcs layer gcc client settings
@see: mcs.IGCCConfig
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getGCCClientSettings", "IGCCConfig"))
raise CallPureVirtualFuntion(
"%s:%s defined by interface %s" % (self.__class__, "getGCCClientSettings", "IGCCConfig"))
def getGCCServerSettings(self):
"""
@return: {gcc.Settings} mcs layer gcc server settings
@see: mcs.IGCCConfig
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getGCCServerSettings", "IGCCConfig"))
raise CallPureVirtualFuntion(
"%s:%s defined by interface %s" % (self.__class__, "getGCCServerSettings", "IGCCConfig"))
def write_domain_params(max_channels: int, max_users: int, max_tokens: int, max_pdu_size: int) -> tuple:
"""
"""
domain_param = (ber.writeInteger(max_channels), ber.writeInteger(max_users), ber.writeInteger(max_tokens),
ber.writeInteger(1), ber.writeInteger(0), ber.writeInteger(1),
ber.writeInteger(max_pdu_size), ber.writeInteger(2))
return ber.writeUniversalTag(ber.Tag.BER_TAG_SEQUENCE, True), writeLength(sizeof(domain_param)), domain_param
def read_domain_params(stream: Stream) -> Tuple[int, int, int, int]:
"""
"""
if not ber.readUniversalTag(stream, ber.Tag.BER_TAG_SEQUENCE, True):
raise InvalidValue("bad BER tags")
ber.readLength(stream) # length
max_channels = ber.readInteger(stream)
max_users = ber.readInteger(stream)
max_tokens = ber.readInteger(stream)
ber.readInteger(stream)
ber.readInteger(stream)
ber.readInteger(stream)
max_pdu_size = ber.readInteger(stream)
ber.readInteger(stream)
return max_channels, max_users, max_tokens, max_pdu_size
class Client:
def __init__(self, x224_layer: x224.X224):
self.x224 = x224_layer
async def write_connect_initial(self):
"""
"""
settings = gcc.client_settings()
settings.get_block(gcc.MessageType.CS_CORE).serverSelectedProtocol.value = self.x224.get_selected_protocol()
cc_req = gcc.writeConferenceCreateRequest(settings)
cc_req_stream = Stream()
cc_req_stream.write_type(cc_req)
tmp = (ber.writeOctetstring(b"\x01"), ber.writeOctetstring(b"\x01"), ber.writeBoolean(True),
write_domain_params(34, 2, 0, 0xffff),
write_domain_params(1, 1, 1, 0x420),
write_domain_params(0xffff, 0xfc17, 0xffff, 0xffff),
ber.writeOctetstring(cc_req_stream.getvalue()))
await self.x224.write((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_INITIAL, sizeof(tmp)), tmp))
async def read_connect_response(self):
payload = await self.x224.read()
ber.readApplicationTag(payload, UInt8(Message.MCS_TYPE_CONNECT_RESPONSE))
ber.readEnumerated(payload)
ber.readInteger(payload)
read_domain_params(payload)
if not ber.readUniversalTag(payload, ber.Tag.BER_TAG_OCTET_STRING, False):
raise InvalidExpectedDataException("invalid expected BER tag")
gccRequestLength = ber.readLength(payload)
if payload.data_len() != gccRequestLength:
raise InvalidSize("bad size of GCC request")
gcc.readConferenceCreateResponse(payload)
async def connect(self):
await self.write_connect_initial()
await self.read_connect_response()
class MCSLayer(LayerAutomata):
"""
@@ -98,11 +171,13 @@ class MCSLayer(LayerAutomata):
the main layer of RDP protocol
is why he can do everything and more!
"""
class MCSProxySender(Layer, IStreamSender, IGCCConfig):
"""
@summary: Proxy use to set as transport layer for upper channel
use to abstract channel id for presentation layer
"""
def __init__(self, presentation, mcs, channelId):
"""
@param presentation: {Layer} presentation layer
@@ -155,7 +230,6 @@ class MCSLayer(LayerAutomata):
"""
return self._mcs._serverSettings
def __init__(self, presentation, receiveOpcode, sendOpcode, virtualChannels=[]):
"""
@param presentation: {Layer} presentation layer
@@ -182,7 +256,7 @@ class MCSLayer(LayerAutomata):
@summary: Send disconnect provider ultimatum
"""
self._transport.send((UInt8(self.writeMCSPDUHeader(DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM, 1)),
per.writeEnumerates(0x80), Buffer("\x00" * 6)))
per.writeEnumerates(0x80), Buffer(b"\x00" * 6)))
self._transport.close()
def allChannelConnected(self):
@@ -294,16 +368,19 @@ class MCSLayer(LayerAutomata):
ber.readInteger(s)
return (max_channels, max_users, max_tokens, max_pdu_size)
class Client(MCSLayer):
class ClientOld(MCSLayer):
"""
@summary: Client automata of multiple channel service layer
"""
def __init__(self, presentation, virtualChannels=[]):
"""
@param presentation: {Layer} presentation layer
@param virtualChannels: {Array(Layer)} list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)]
"""
MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST, virtualChannels)
MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST,
virtualChannels)
# use to know state of static channel
self._isGlobalChannelRequested = False
self._isUserChannelRequested = False
@@ -344,8 +421,8 @@ class Client(MCSLayer):
return
# static virtual channel
if self._nbChannelRequested < self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value:
channelId = self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray[self._nbChannelRequested]
if self._nbChannelRequested < self._serverSettings.get_block(gcc.MessageType.SC_NET).channelCount.value:
channelId = self._serverSettings.get_block(gcc.MessageType.SC_NET).channelIdArray[self._nbChannelRequested]
self._nbChannelRequested += 1
self.sendChannelJoinRequest(channelId)
return
@@ -421,7 +498,7 @@ class Client(MCSLayer):
raise InvalidExpectedDataException("Server must confirm static channel")
if confirm == 0:
serverNet = self._serverSettings.getBlock(gcc.MessageType.SC_NET)
serverNet = self._serverSettings.get_block(gcc.MessageType.SC_NET)
for i in range(0, serverNet.channelCount.value):
if channelId == serverNet.channelIdArray[i].value:
self._channels[channelId] = self._virtualChannels[i][1]
@@ -468,16 +545,19 @@ class Client(MCSLayer):
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
per.writeInteger16(channelId)))
class Server(MCSLayer):
"""
@summary: Server automata of multiple channel service layer
"""
def __init__(self, presentation, virtualChannels=[]):
"""
@param presentation: {Layer} presentation layer
@param virtualChannels: {List(Layer)} list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)]
"""
MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION, virtualChannels)
MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION,
virtualChannels)
# nb channel requested
self._nbChannelConfirmed = 0
@@ -488,7 +568,6 @@ class Server(MCSLayer):
"""
# basic rdp security layer
if self._transport._selectedProtocol == 0:
self._serverSettings.SC_SECURITY.encryptionMethod.value = gcc.EncryptionMethod.ENCRYPTION_FLAG_128BIT
self._serverSettings.SC_SECURITY.encryptionLevel.value = gcc.EncryptionLevel.ENCRYPTION_LEVEL_HIGH
self._serverSettings.SC_SECURITY.serverRandom.value = rsa.random(256)
@@ -584,7 +663,7 @@ class Server(MCSLayer):
confirm = 0 if channelId in self._channels.keys() or channelId == self._userId else 1
self.sendChannelJoinConfirm(channelId, confirm)
self._nbChannelConfirmed += 1
if self._nbChannelConfirmed == self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value + 2:
if self._nbChannelConfirmed == self._serverSettings.get_block(gcc.MessageType.SC_NET).channelCount.value + 2:
self.allChannelConnected()
def sendConnectResponse(self):

View File

@@ -223,12 +223,9 @@ def readNumericString(s, minValue):
length = (length + minValue + 1) / 2
s.read(length)
def writeNumericString(nStr, minValue):
"""
@summary: write string in per format
@param str: python string to write
@param min: min value
@return: String type that contain str encoded in per format
"""
length = len(nStr)
mlength = minValue
@@ -238,9 +235,9 @@ def writeNumericString(nStr, minValue):
result = []
for i in range(0, length, 2):
c1 = ord(nStr[i])
c1 = nStr[i]
if i + 1 < length:
c2 = ord(nStr[i + 1])
c2 = nStr[i + 1]
else:
c2 = 0x30
c1 = (c1 - 0x30) % 10
@@ -248,7 +245,7 @@ def writeNumericString(nStr, minValue):
result.append(UInt8((c1 << 4) | c2))
return (writeLength(mlength), tuple(result))
return writeLength(mlength), tuple(result)
def readPadding(s, length):
"""
@@ -264,7 +261,7 @@ def writePadding(length):
@param length: length of padding
@return: String with \x00 * length
"""
return Buffer("\x00" * length)
return Buffer(b"\x00" * length)
def readOctetStream(s, octetStream, minValue = 0):
"""
@@ -300,6 +297,6 @@ def writeOctetStream(oStr, minValue = 0):
result = []
for i in range(0, length):
result.append(UInt8(ord(oStr[i])))
result.append(UInt8(oStr[i]))
return (writeLength(mlength), tuple(result))
return writeLength(mlength), tuple(result)

View File

@@ -134,20 +134,15 @@ class Negotiation(CompositeType):
class X224:
"""
@summary: x224 layer management
there is an connection automata
"""
def __init__(self, tpkt: tpkt.Tpkt):
def __init__(self, tpkt: tpkt.Tpkt, selected_protocol: int):
"""
@param tpkt: TPKT layer
"""
self.tpkt = tpkt
self.selected_protocol = selected_protocol
async def read(self):
"""
@summary: Read data header from packet
And pass to presentation layer
@param data: Stream
"""
header = X224DataHeader()
payload = await self.tpkt.read()
@@ -156,12 +151,12 @@ class X224:
async def write(self, message):
"""
@summary: Write message packet for TPDU layer
Add TPDU header
@param message:
"""
await self.tpkt.write((X224DataHeader(), message))
def get_selected_protocol(self):
return self.selected_protocol
async def connect(tpkt: tpkt.Tpkt, authentication_protocol: sspi.IAuthenticationProtocol) -> X224:
"""
@@ -190,11 +185,11 @@ async def connect(tpkt: tpkt.Tpkt, authentication_protocol: sspi.IAuthentication
raise InvalidExpectedDataException("RDPY doesn't support PROTOCOL_HYBRID_EX security Layer")
if selected_protocol == Protocols.PROTOCOL_RDP:
return X224(tpkt)
return X224(tpkt, selected_protocol)
elif selected_protocol == Protocols.PROTOCOL_SSL:
return X224(await tpkt.start_tls())
return X224(await tpkt.start_tls(), selected_protocol)
elif selected_protocol == Protocols.PROTOCOL_HYBRID:
return X224(await tpkt.start_nla(authentication_protocol))
return X224(await tpkt.start_nla(authentication_protocol), selected_protocol)
class Server(X224):

View File

@@ -354,56 +354,35 @@ class SimpleType(DynMessage):
class CompositeType(Message):
"""
@summary: Type node in Type tree
Track type field declared in __init__ function
Ex: self.lengthOfPacket = UInt16Le() -> record lengthOfPacket as sub type of node
"""
def __init__(self, read_len = None, conditional = lambda:True, optional = False, constant = False, ):
def __init__(self, read_len=None, conditional=lambda: True, optional=False, constant=False):
"""
@param conditional : Callable object
Read and Write operation depend on return of this function
@param optional: If there is no enough byte in current stream
And optional is True, read type is ignored
@param constant: Check if object value doesn't change after read operation
@param readLen: Max length in bytes can be readed from stream
Use to check length information
"""
super().__init__(conditional=conditional, optional=optional, constant=constant)
# list of ordoned type
self._typeName = []
# list of ordorred type
self._type_name = []
self._read_len = read_len
def __setattr__(self, name, value):
"""
@summary: Track Type field
For Type field record it in same order as declared
Keep other but bot handle in read or write function
@param name: name of new attribute
@param value: value of new attribute
"""
if name[0] != '_' and (isinstance(value, Message) or isinstance(value, tuple)) and not name in self._typeName:
self._typeName.append(name)
if name[0] != '_' and (isinstance(value, Message) or isinstance(value, tuple)) and name not in self._type_name:
self._type_name.append(name)
self.__dict__[name] = value
def __read__(self, s):
"""
@summary: Read composite type
Call read on each ordered sub-type
And check read length parameter
If an error occurred rollback type already read
@param s: Stream
@raise InvalidSize: if stream is greater than readLen parameter
"""
readLen = 0
for name in self._typeName:
read_len = 0
for name in self._type_name:
try:
s.read_type(self.__dict__[name])
readLen += sizeof(self.__dict__[name])
read_len += sizeof(self.__dict__[name])
# read is ok but read out of bound
if not self._read_len is None and readLen > self._read_len.value:
if self._read_len is not None and read_len > self._read_len.value:
# roll back
s.pos -= sizeof(self.__dict__[name])
s.seek(-sizeof(self.__dict__[name]), 1)
# and notify if not optional
if not self.__dict__[name]._optional:
raise InvalidSize("Impossible to read type %s : read length is too small"%(self.__class__))
@@ -411,15 +390,15 @@ class CompositeType(Message):
except Exception as e:
log.error("Error during read %s::%s"%(self.__class__, name))
# roll back already read
for tmpName in self._typeName:
if tmpName == name:
for tmp_name in self._type_name:
if tmp_name == name:
break
s.seek(-sizeof(self.__dict__[tmpName]), 1)
s.seek(-sizeof(self.__dict__[tmp_name]), 1)
raise e
if not self._read_len is None and readLen < self._read_len.value:
log.debug("Still have correct data in packet %s, read %s bytes as padding"%(self.__class__, self._read_len.value - readLen))
s.read(self._read_len.value - readLen)
if self._read_len is not None and read_len < self._read_len.value:
log.debug("Still have correct data in packet %s, read %s bytes as padding"%(self.__class__, self._read_len.value - read_len))
s.read(self._read_len.value - read_len)
def __write__(self, s):
"""
@@ -427,7 +406,7 @@ class CompositeType(Message):
Call write on each ordered sub type
@param s: Stream
"""
for name in self._typeName:
for name in self._type_name:
try:
s.write_type(self.__dict__[name])
except Exception as e:
@@ -443,7 +422,7 @@ class CompositeType(Message):
return self._read_len.value
size = 0
for name in self._typeName:
for name in self._type_name:
size += sizeof(self.__dict__[name])
return size
@@ -454,9 +433,9 @@ class CompositeType(Message):
@param other: CompositeType
@return: True if each sub-type are equals
"""
if self._typeName != other._typeName:
if self._type_name != other._typeName:
return False
for name in self._typeName:
for name in self._type_name:
if self.__dict__[name] != other.__dict__[name]:
return False
return True
@@ -840,35 +819,23 @@ class ArrayType(Message):
"""
@summary: Factory af n element
"""
def __init__(self, typeFactory, init = None, readLen = None, conditional = lambda:True, optional = False, constant = False):
def __init__(self, type_factory, init=None, read_len=None, conditional=lambda:True, optional=False, constant=False):
"""
@param typeFactory: class use to init new element on read
@param init: init array
@param readLen: number of element in sequence
@param conditional : Callable object
Read and Write operation depend on return of this function
@param optional: If there is no enough byte in current stream
And optional is True, read type is ignored
@param constant: Check if object value doesn't change after read operation
"""
Message.__init__(self, conditional, optional, constant)
self._typeFactory = typeFactory
self._readLen = readLen
self._array = []
if not init is None:
self._array = init
super().__init__(conditional, optional, constant)
self._type_factory = type_factory
self._read_len = read_len
self._array = init or []
def __read__(self, s):
"""
@summary: Create readLen new object and read it
@param s: Stream
"""
self._array = []
i = 0
#self._readLen is None means that array will be read until end of stream
while self._readLen is None or i < self._readLen.value:
element = self._typeFactory()
element._optional = self._readLen is None
# self._read_len is None means that array will be read until end of stream
while self._read_len is None or i < self._read_len():
element = self._type_factory()
element._optional = self._read_len is None
s.read_type(element)
if not element._is_readed:
break
@@ -877,8 +844,6 @@ class ArrayType(Message):
def __write__(self, s):
"""
@summary: Just write array
@param s: Stream
"""
s.write_type(self._array)
@@ -895,6 +860,9 @@ class ArrayType(Message):
"""
return sizeof(self._array)
def __len__(self):
return len(self._array)
class FactoryType(Message):
"""
@summary: Call a factory callback at read or write time