Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import os, sys, struct, types, logging, binascii, time
- from StringIO import StringIO
- from smb_constants import *
- # Set to True if you want to enable support for extended security. Required for Windows Vista and later
- SUPPORT_EXTENDED_SECURITY = True
- # Set to True if you want to enable SMB2 protocol.
- SUPPORT_SMB2 = True
- # Supported dialects
- DIALECTS = [ ]
- for i, ( name, dialect ) in enumerate([ ( 'NT_LAN_MANAGER_DIALECT', 'NT LM 0.12' ), ]):
- DIALECTS.append(dialect)
- globals()[name] = i
- DIALECTS2 = [ ]
- for i, ( name, dialect ) in enumerate([ ( 'SMB2_DIALECT', 'SMB 2.002' ) ]):
- DIALECTS2.append(dialect)
- globals()[name] = i + len(DIALECTS)
- class UnsupportedFeature(Exception):
- """
- Raised when an supported feature is present/required in the protocol but is not
- currently supported by pysmb
- """
- pass
- class ProtocolError(Exception):
- def __init__(self, message, data_buf = None, smb_message = None):
- self.message = message
- self.data_buf = data_buf
- self.smb_message = smb_message
- def __str__(self):
- b = StringIO()
- b.write(self.message + os.linesep)
- if self.smb_message:
- b.write('=' * 20 + ' SMB Message ' + '=' * 20 + os.linesep)
- b.write(str(self.smb_message))
- if self.data_buf:
- b.write('=' * 20 + ' SMB Data Packet (hex) ' + '=' * 20 + os.linesep)
- b.write(binascii.hexlify(self.data_buf))
- b.write(os.linesep)
- return b.getvalue()
- class SMB2ProtocolHeaderError(ProtocolError):
- def __init__(self):
- ProtocolError.__init__(self, "Packet header belongs to SMB2")
- class OperationFailure(Exception):
- def __init__(self, message, smb_messages):
- self.args = [ message ]
- self.message = message
- self.smb_messages = smb_messages
- def __str__(self):
- b = StringIO()
- b.write(self.message + os.linesep)
- for idx, m in enumerate(self.smb_messages):
- b.write('=' * 20 + ' SMB Message %d ' % idx + '=' * 20 + os.linesep)
- b.write('SMB Header:' + os.linesep)
- b.write('-----------' + os.linesep)
- b.write(str(m))
- b.write('SMB Data Packet (hex):' + os.linesep)
- b.write('----------------------' + os.linesep)
- b.write(binascii.hexlify(m.raw_data))
- b.write(os.linesep)
- return b.getvalue()
- class SMBError:
- def __init__(self):
- self.reset()
- def reset(self):
- self.internal_value = 0L
- self.is_ntstatus = True
- def __str__(self):
- if self.is_ntstatus:
- return 'NTSTATUS=0x%08X' % self.internal_value
- else:
- return 'ErrorClass=0x%02X ErrorCode=0x%04X' % ( self.internal_value >> 24, self.internal_value & 0xFFFF )
- @property
- def hasError(self):
- return self.internal_value != 0
- class SMBMessage:
- HEADER_STRUCT_FORMAT = "<4sBIBHHQxxHHHHB"
- HEADER_STRUCT_SIZE = struct.calcsize(HEADER_STRUCT_FORMAT)
- log = logging.getLogger('SMB.SMBMessage')
- protocol = 1
- def __init__(self, payload = None):
- self.reset()
- if payload:
- self.payload = payload
- self.payload.initMessage(self)
- def __str__(self):
- b = StringIO()
- b.write('Command: 0x%02X (%s) %s' % ( self.command, SMB_COMMAND_NAMES.get(self.command, '<unknown>'), os.linesep ))
- b.write('Status: %s %s' % ( str(self.status), os.linesep ))
- b.write('Flags: 0x%02X %s' % ( self.flags, os.linesep ))
- b.write('Flags2: 0x%04X %s' % ( self.flags2, os.linesep ))
- b.write('PID: %d %s' % ( self.pid, os.linesep ))
- b.write('UID: %d %s' % ( self.uid, os.linesep ))
- b.write('MID: %d %s' % ( self.mid, os.linesep ))
- b.write('TID: %d %s' % ( self.tid, os.linesep ))
- b.write('Security: 0x%016X %s' % ( self.security, os.linesep ))
- b.write('Parameters: %d bytes %s%s %s' % ( len(self.parameters_data), os.linesep, binascii.hexlify(self.parameters_data), os.linesep ))
- b.write('Data: %d bytes %s%s %s' % ( len(self.data), os.linesep, binascii.hexlify(self.data), os.linesep ))
- return b.getvalue()
- def reset(self):
- self.raw_data = ''
- self.command = 0
- self.status = SMBError()
- self.flags = 0
- self.flags2 = 0
- self.pid = 0
- self.tid = 0
- self.uid = 0
- self.mid = 0
- self.security = 0L
- self.parameters_data = ''
- self.data = ''
- self.payload = None
- @property
- def isReply(self):
- return bool(self.flags & SMB_FLAGS_REPLY)
- @property
- def hasExtendedSecurity(self):
- return bool(self.flags2 & SMB_FLAGS2_EXTENDED_SECURITY)
- def encode(self):
- """
- Encode this SMB message into a series of bytes suitable to be embedded with a NetBIOS session message.
- AssertionError will be raised if this SMB message has not been initialized with a Payload instance
- @return: a string containing the encoded SMB message
- """
- assert self.payload
- self.pid = os.getpid()
- self.payload.prepare(self)
- parameters_len = len(self.parameters_data)
- assert parameters_len % 2 == 0
- headers_data = struct.pack(self.HEADER_STRUCT_FORMAT,
- '\xFFSMB', self.command, self.status.internal_value, self.flags,
- self.flags2, (self.pid >> 16) & 0xFFFF, self.security, self.tid,
- self.pid & 0xFFFF, self.uid, self.mid, int(parameters_len / 2))
- return headers_data + self.parameters_data + struct.pack('<H', len(self.data)) + self.data
- def decode(self, buf):
- """
- Decodes the SMB message in buf.
- All fields of the SMBMessage object will be reset to default values before decoding.
- On errors, do not assume that the fields will be reinstated back to what they are before
- this method is invoked.
- @param buf: data containing one complete SMB message
- @type buf: string
- @return: a positive integer indicating the number of bytes used in buf to decode this SMB message
- @raise ProtocolError: raised when decoding fails
- """
- buf_len = len(buf)
- if buf_len < self.HEADER_STRUCT_SIZE:
- # We need at least 32 bytes (header) + 1 byte (parameter count)
- raise ProtocolError('Not enough data to decode SMB header', buf)
- self.reset()
- protocol, self.command, status, self.flags, \
- self.flags2, pid_high, self.security, self.tid, \
- pid_low, self.uid, self.mid, params_count = struct.unpack(self.HEADER_STRUCT_FORMAT, buf[:self.HEADER_STRUCT_SIZE])
- if protocol == '\xFESMB':
- raise SMB2ProtocolHeaderError()
- if protocol != '\xFFSMB':
- raise ProtocolError('Invalid 4-byte protocol field', buf)
- self.pid = (pid_high << 16) | pid_low
- self.status.internal_value = status
- self.status.is_ntstatus = bool(self.flags2 & SMB_FLAGS2_NT_STATUS)
- offset = self.HEADER_STRUCT_SIZE
- if buf_len < params_count * 2 + 2:
- # Not enough data in buf to decode up to body length
- raise ProtocolError('Not enough data. Parameters list decoding failed', buf)
- datalen_offset = offset + params_count*2
- body_len = struct.unpack('<H', buf[datalen_offset:datalen_offset+2])[0]
- if body_len > 0 and buf_len < (datalen_offset + 2 + body_len):
- # Not enough data in buf to decode body
- raise ProtocolError('Not enough data. Body decoding failed', buf)
- self.parameters_data = buf[offset:datalen_offset]
- if body_len > 0:
- self.data = buf[datalen_offset+2:datalen_offset+2+body_len]
- self.raw_data = buf
- self._decodePayload()
- return self.HEADER_STRUCT_SIZE + params_count * 2 + 2 + body_len
- def _decodePayload(self):
- if self.command == SMB_COM_READ_ANDX:
- self.payload = ComReadAndxResponse()
- elif self.command == SMB_COM_WRITE_ANDX:
- self.payload = ComWriteAndxResponse()
- elif self.command == SMB_COM_TRANSACTION:
- self.payload = ComTransactionResponse()
- elif self.command == SMB_COM_TRANSACTION2:
- self.payload = ComTransaction2Response()
- elif self.command == SMB_COM_OPEN_ANDX:
- self.payload = ComOpenAndxResponse()
- elif self.command == SMB_COM_NT_CREATE_ANDX:
- self.payload = ComNTCreateAndxResponse()
- elif self.command == SMB_COM_TREE_CONNECT_ANDX:
- self.payload = ComTreeConnectAndxResponse()
- elif self.command == SMB_COM_ECHO:
- self.payload = ComEchoResponse()
- elif self.command == SMB_COM_SESSION_SETUP_ANDX:
- self.payload = ComSessionSetupAndxResponse()
- elif self.command == SMB_COM_NEGOTIATE:
- self.payload = ComNegotiateResponse()
- if self.payload:
- self.payload.decode(self)
- class Payload:
- DEFAULT_ANDX_PARAM_HEADER = '\xFF\x00\x00\x00'
- DEFAULT_ANDX_PARAM_SIZE = 4
- def initMessage(self, message):
- # SMB_FLAGS2_UNICODE must always be enabled. Without this, almost all the Payload subclasses will need to be
- # rewritten to check for OEM/Unicode strings which will be tedious. Fortunately, almost all tested CIFS services
- # support SMB_FLAGS2_UNICODE by default.
- assert message.payload == self
- message.flags = SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS
- message.flags2 = SMB_FLAGS2_UNICODE | SMB_FLAGS2_NT_STATUS | SMB_FLAGS2_LONG_NAMES | SMB_FLAGS2_EAS
- if SUPPORT_EXTENDED_SECURITY:
- message.flags2 |= SMB_FLAGS2_EXTENDED_SECURITY | SMB_FLAGS2_SMB_SECURITY_SIGNATURE
- def prepare(self, message):
- raise NotImplementedError
- def decode(self, message):
- raise NotImplementedError
- class ComNegotiateRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.52.1
- - [MS-SMB]: 2.2.4.5.1
- """
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_NEGOTIATE
- def prepare(self, message):
- assert message.payload == self
- message.parameters_data = ''
- if SUPPORT_SMB2:
- message.data = ''.join(map(lambda s: '\x02'+s+'\x00', DIALECTS + DIALECTS2))
- else:
- message.data = ''.join(map(lambda s: '\x02'+s+'\x00', DIALECTS))
- class ComNegotiateResponse(Payload):
- """
- Contains information on the SMB_COM_NEGOTIATE response from server
- After calling the decode method, each instance will contain the following attributes,
- - security_mode (integer)
- - max_mpx_count (integer)
- - max_number_vcs (integer)
- - max_buffer_size (long)
- - max_raw_size (long)
- - session_key (long)
- - capabilities (long)
- - system_time (long)
- - server_time_zone (integer)
- - challenge_length (integer)
- If the underlying SMB message's flag2 does not have SMB_FLAGS2_EXTENDED_SECURITY bit enabled,
- then the instance will have the following additional attributes,
- - challenge (string)
- - domain (unicode)
- If the underlying SMB message's flags2 has SMB_FLAGS2_EXTENDED_SECURITY bit enabled,
- then the instance will have the following additional attributes,
- - server_guid (string)
- - security_blob (string)
- References:
- ===========
- - [MS-SMB]: 2.2.4.5.2.1
- - [MS-CIFS]: 2.2.4.52.2
- """
- PAYLOAD_STRUCT_FORMAT = '<HBHHIIIIQHB'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def decode(self, message):
- assert message.command == SMB_COM_NEGOTIATE
- if not message.isReply:
- raise ProtocolError('Not a SMB_COM_NEGOTIATE reply', message.raw_data, message)
- self.security_mode, self.max_mpx_count, self.max_number_vcs, self.max_buffer_size, \
- self.max_raw_size, self.session_key, self.capabilities, self.system_time, self.server_time_zone, \
- self.challenge_length = ( 0, ) * 10
- data_len = len(message.parameters_data)
- if data_len < 2:
- raise ProtocolError('Not enough data to decode SMB_COM_NEGOTIATE dialect_index field', message.raw_data, message)
- self.dialect_index = struct.unpack('<H', message.parameters_data[:2])[0]
- if self.dialect_index == NT_LAN_MANAGER_DIALECT:
- if data_len != (0x11 * 2):
- raise ProtocolError('NT LAN Manager dialect selected in SMB_COM_NEGOTIATE but parameters bytes count (%d) does not meet specs' % data_len,
- message.raw_data, message)
- else:
- _, self.security_mode, self.max_mpx_count, self.max_number_vcs, self.max_buffer_size, \
- self.max_raw_size, self.session_key, self.capabilities, self.system_time, self.server_time_zone, \
- self.challenge_length = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
- elif self.dialect_index == 0xFFFF:
- raise ProtocolError('Server does not support any of the pysmb dialects. Please email pysmb to add in support for your OS',
- message.raw_data, message)
- else:
- raise ProtocolError('Unknown dialect index (0x%04X)' % self.dialect_index, message.raw_data, message)
- data_len = len(message.data)
- if not message.hasExtendedSecurity:
- self.challenge, self.domain = '', ''
- if self.challenge_length > 0:
- if data_len >= self.challenge_length:
- self.challenge = message.data[:self.challenge_length]
- s = ''
- offset = self.challenge_length
- while offset < data_len:
- _s = message.data[offset:offset+2]
- if _s == '\0\0':
- self.domain = s.decode('UTF-16LE')
- break
- else:
- s += _s
- offset += 2
- else:
- raise ProtocolError('Not enough data to decode SMB_COM_NEGOTIATE (without security extensions) Challenge field', message.raw_data, message)
- else:
- if data_len < 16:
- raise ProtocolError('Not enough data to decode SMB_COM_NEGOTIATE (with security extensions) ServerGUID field', message.raw_data, message)
- self.server_guid = message.data[:16]
- self.security_blob = message.data[16:]
- @property
- def supportsExtendedSecurity(self):
- return bool(self.capabilities & CAP_EXTENDED_SECURITY)
- class ComSessionSetupAndxRequest__WithSecurityExtension(Payload):
- """
- References:
- ===========
- - [MS-SMB]: 2.2.4.6.1
- """
- PAYLOAD_STRUCT_FORMAT = '<HHHIHII'
- def __init__(self, session_key, security_blob):
- self.session_key = session_key
- self.security_blob = security_blob
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_SESSION_SETUP_ANDX
- def prepare(self, message):
- assert message.hasExtendedSecurity
- message.flags2 |= SMB_FLAGS2_UNICODE
- cap = CAP_UNICODE | CAP_STATUS32 | CAP_EXTENDED_SECURITY | CAP_NT_SMBS
- message.parameters_data = \
- self.DEFAULT_ANDX_PARAM_HEADER + \
- struct.pack(self.PAYLOAD_STRUCT_FORMAT,
- 16644, 10, 1, self.session_key, len(self.security_blob), 0, cap)
- message.data = self.security_blob
- if (SMBMessage.HEADER_STRUCT_SIZE + len(message.parameters_data) + len(message.data)) % 2 != 0:
- message.data = message.data + '\0'
- message.data = message.data + '\0' * 4
- class ComSessionSetupAndxRequest__NoSecurityExtension(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.53.1
- """
- PAYLOAD_STRUCT_FORMAT = '<HHHIHHII'
- def __init__(self, session_key, username, password, is_unicode, domain):
- self.username = username
- self.session_key = session_key
- self.password = password
- self.is_unicode = is_unicode
- self.domain = domain
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_SESSION_SETUP_ANDX
- def prepare(self, message):
- if self.is_unicode:
- message.flags2 |= SMB_FLAGS2_UNICODE
- else:
- message.flags2 &= (~SMB_FLAGS2_UNICODE & 0xFFFF)
- password_len = len(self.password)
- message.parameters_data = \
- self.DEFAULT_ANDX_PARAM_HEADER + \
- struct.pack(self.PAYLOAD_STRUCT_FORMAT,
- 16644, 10, 0, self.session_key,
- (not self.is_unicode and password_len) or 0,
- (self.is_unicode and password_len) or 0,
- 0,
- CAP_UNICODE | CAP_LARGE_FILES | CAP_STATUS32)
- est_offset = SMBMessage.HEADER_STRUCT_SIZE + len(message.parameters_data) # To check if data until SMB paramaters are aligned to a 16-bit boundary
- message.data = self.password
- if (est_offset + len(message.data)) % 2 != 0 and message.flags2 & SMB_FLAGS2_UNICODE:
- message.data = message.data + '\0'
- if message.flags2 & SMB_FLAGS2_UNICODE:
- message.data = message.data + self.username.encode('UTF-16LE') + '\0'
- else:
- message.data = message.data + str(self.username) + '\0'
- if (est_offset + len(message.data)) % 2 != 0 and message.flags2 & SMB_FLAGS2_UNICODE:
- message.data = message.data + '\0'
- if message.flags2 & SMB_FLAGS2_UNICODE:
- message.data = message.data + self.domain.encode('UTF-16LE') + '\0\0' + 'pysmb'.encode('UTF-16LE') + '\0\0'
- else:
- message.data = message.data + self.domain + '\0pysmb\0'
- class ComSessionSetupAndxResponse(Payload):
- """
- Contains information on the SMB_COM_SESSION_SETUP_ANDX response from server
- If the underlying SMB message's flags2 does not have SMB_FLAGS2_EXTENDED_SECURITY bit enabled,
- then the instance will have the following attributes,
- - action
- If the underlying SMB message's flags2 has SMB_FLAGS2_EXTENDED_SECURITY bit enabled
- and the message status is STATUS_MORE_PROCESSING_REQUIRED or equals to 0x00 (no error),
- then the instance will have the following attributes,
- - action
- - securityblob
- If the underlying SMB message's flags2 has SMB_FLAGS2_EXTENDED_SECURITY bit enabled but
- the message status is not STATUS_MORE_PROCESSING_REQUIRED
- References:
- ===========
- - [MS-SMB]: 2.2.4.6.2
- - [MS-CIFS]: 2.2.4.53.2
- """
- NOSECURE_PARAMETER_STRUCT_FORMAT = '<BBHH'
- NOSECURE_PARAMETER_STRUCT_SIZE = struct.calcsize(NOSECURE_PARAMETER_STRUCT_FORMAT)
- SECURE_PARAMETER_STRUCT_FORMAT = '<BBHHH'
- SECURE_PARAMETER_STRUCT_SIZE = struct.calcsize(SECURE_PARAMETER_STRUCT_FORMAT)
- def decode(self, message):
- assert message.command == SMB_COM_SESSION_SETUP_ANDX
- if not message.hasExtendedSecurity:
- if not message.status.hasError:
- if len(message.parameters_data) < self.NOSECURE_PARAMETER_STRUCT_SIZE:
- raise ProtocolError('Not enough data to decode SMB_COM_SESSION_SETUP_ANDX (no security extensions) parameters', message.raw_data, message)
- _, _, _, self.action = struct.unpack(self.NOSECURE_PARAMETER_STRUCT_FORMAT, message.parameters_data[:self.NOSECURE_PARAMETER_STRUCT_SIZE])
- else:
- if not message.status.hasError or message.status.internal_value == 0xc0000016: # STATUS_MORE_PROCESSING_REQUIRED
- if len(message.parameters_data) < self.SECURE_PARAMETER_STRUCT_SIZE:
- raise ProtocolError('Not enough data to decode SMB_COM_SESSION_SETUP_ANDX (with security extensions) parameters', message.raw_data, message)
- _, _, _, self.action, blob_length = struct.unpack(self.SECURE_PARAMETER_STRUCT_FORMAT, message.parameters_data[:self.SECURE_PARAMETER_STRUCT_SIZE])
- if len(message.data) < blob_length:
- raise ProtocolError('Not enough data to decode SMB_COM_SESSION_SETUP_ANDX (with security extensions) security blob', message.raw_data, message)
- self.security_blob = message.data[:blob_length]
- class ComTreeConnectAndxRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.55.1
- - [MS-SMB]: 2.2.4.7.1
- """
- PAYLOAD_STRUCT_FORMAT = '<HH'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def __init__(self, path, service, password = ''):
- self.path = path
- self.service = service
- self.password = password + '\0'
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_TREE_CONNECT_ANDX
- def prepare(self, message):
- password_len = len(self.password)
- message.parameters_data = \
- self.DEFAULT_ANDX_PARAM_HEADER + \
- struct.pack(self.PAYLOAD_STRUCT_FORMAT,
- 0x08 | \
- ((message.hasExtendedSecurity and 0x0004) or 0x00) | \
- ((message.tid and message.tid != 0xFFFF and 0x0001) or 0x00), # Disconnect tid, if message.tid must be non-zero
- password_len)
- padding = ''
- if password_len % 2 == 0:
- padding = '\0'
- # Note that service field is never encoded in UTF-16LE. [MS-CIFS]: 2.2.1.1
- message.data = self.password + padding + self.path.encode('UTF-16LE') + '\0\0' + self.service + '\0'
- class ComTreeConnectAndxResponse(Payload):
- """
- Contains information about the SMB_COM_TREE_CONNECT_ANDX response from the server.
- If the message has no errors, each instance contains the following attributes:
- - optional_support
- References:
- ===========
- - [MS-CIFS]: 2.2.4.55.2
- """
- PAYLOAD_STRUCT_FORMAT = '<BBHH'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def decode(self, message):
- assert message.command == SMB_COM_TREE_CONNECT_ANDX
- if not message.status.hasError:
- if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
- raise ProtocolError('Not enough data to decode SMB_COM_TREE_CONNECT_ANDX parameters', message.raw_data, message)
- _, _, _, self.optional_support = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
- class ComNTCreateAndxRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.64.1
- - [MS-SMB]: 2.2.4.9.1
- """
- PAYLOAD_STRUCT_FORMAT = '<BHIIIQIIIIIB'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def __init__(self, filename, flags = 0, root_fid = 0, access_mask = 0, allocation_size = 0L, ext_attr = 0,
- share_access = 0, create_disp = 0, create_options = 0, impersonation = 0, security_flags = 0):
- self.filename = (filename + '\0').encode('UTF-16LE')
- self.flags = flags
- self.root_fid = root_fid
- self.access_mask = access_mask
- self.allocation_size = allocation_size
- self.ext_attr = ext_attr
- self.share_access = share_access
- self.create_disp = create_disp
- self.create_options = create_options
- self.impersonation = impersonation
- self.security_flags = security_flags
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_NT_CREATE_ANDX
- def prepare(self, message):
- filename_len = len(self.filename)
- message.parameters_data = \
- self.DEFAULT_ANDX_PARAM_HEADER + \
- struct.pack(self.PAYLOAD_STRUCT_FORMAT,
- 0x00, # reserved
- filename_len, # NameLength
- self.flags, # Flags
- self.root_fid, # RootDirectoryFID
- self.access_mask, # DesiredAccess
- self.allocation_size, # AllocationSize
- self.ext_attr, # ExtFileAttributes
- self.share_access, # ShareAccess
- self.create_disp, # CreateDisposition
- self.create_options, # CreateOptions
- self.impersonation, # ImpersonationLevel
- self.security_flags) # SecurityFlags
- padding = ''
- if (message.HEADER_STRUCT_SIZE + len(message.parameters_data)) % 2 != 0:
- padding = '\0'
- message.data = padding + self.filename
- class ComNTCreateAndxResponse(Payload):
- """
- Contains (partial) information about the SMB_COM_NT_CREATE_ANDX response from the server.
- Each instance contains the following attributes after decoding:
- - oplock_level
- - fid
- References:
- ===========
- - [MS-CIFS]: 2.2.4.64.2
- """
- PAYLOAD_STRUCT_FORMAT = '<BBHBH'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def decode(self, message):
- assert message.command == SMB_COM_NT_CREATE_ANDX
- if not message.status.hasError:
- if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
- raise ProtocolError('Not enough data to decode SMB_COM_NT_CREATE_ANDX parameters', message.raw_data, message)
- _, _, _, self.oplock_level, self.fid = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
- class ComTransactionRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.33.1
- """
- PAYLOAD_STRUCT_FORMAT = '<HHHHBBHIHHHHHH'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def __init__(self, max_params_count, max_data_count, max_setup_count,
- total_params_count = 0, total_data_count = 0,
- params_bytes = '', data_bytes = '', setup_bytes = '',
- flags = 0, timeout = 0, name = "\\PIPE\\"):
- self.total_params_count = total_params_count or len(params_bytes)
- self.total_data_count = total_data_count or len(data_bytes)
- self.max_params_count = max_params_count
- self.max_data_count = max_data_count
- self.max_setup_count = max_setup_count
- self.flags = flags
- self.timeout = timeout
- self.params_bytes = params_bytes
- self.data_bytes = data_bytes
- self.setup_bytes = setup_bytes
- self.name = name
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_TRANSACTION
- def prepare(self, message):
- name = (self.name + '\0').encode('UTF-16LE')
- name_len = len(name)
- setup_bytes_len = len(self.setup_bytes)
- params_bytes_len = len(self.params_bytes)
- data_bytes_len = len(self.data_bytes)
- padding0 = ''
- offset = message.HEADER_STRUCT_SIZE + self.PAYLOAD_STRUCT_SIZE + setup_bytes_len + 2 # constant 2 is for the ByteCount field in the SMB header (i.e. field which indicates number of data bytes after the SMB parameters)
- if offset % 2 != 0:
- padding0 = '\0'
- offset += 1
- offset += name_len # For the name field
- padding1 = ''
- if offset % 4 != 0:
- padding1 = '\0'*(4-offset%4)
- offset += (4-offset%4)
- if params_bytes_len > 0:
- params_bytes_offset = offset
- offset += params_bytes_len
- else:
- params_bytes_offset = 0
- padding2 = ''
- if offset % 4 != 0:
- padding2 = '\0'*(4-offset%4)
- offset += (4-offset%4)
- if data_bytes_len > 0:
- data_bytes_offset = offset
- else:
- data_bytes_offset = 0
- message.parameters_data = \
- struct.pack(self.PAYLOAD_STRUCT_FORMAT,
- self.total_params_count,
- self.total_data_count,
- self.max_params_count,
- self.max_data_count,
- self.max_setup_count,
- 0x00, # Reserved1. Must be 0x00
- self.flags,
- self.timeout,
- 0x0000, # Reserved2. Must be 0x0000
- params_bytes_len,
- params_bytes_offset,
- data_bytes_len,
- data_bytes_offset,
- int(setup_bytes_len / 2)) + \
- self.setup_bytes
- message.data = padding0 + name + padding1 + self.params_bytes + padding2 + self.data_bytes
- class ComTransactionResponse(Payload):
- """
- Contains information about a SMB_COM_TRANSACTION response from the server
- After decoding, each instance contains the following attributes:
- - total_params_count (integer)
- - total_data_count (integer)
- - setup_bytes (string)
- - data_bytes (string)
- - params_bytes (string)
- References:
- ===========
- - [MS-CIFS]: 2.2.4.33.2
- """
- PAYLOAD_STRUCT_FORMAT = '<HHHHHHHHHH'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def decode(self, message):
- assert message.command == SMB_COM_TRANSACTION
- if not message.status.hasError:
- if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
- raise ProtocolError('Not enough data to decode SMB_COM_TRANSACTION parameters', message.raw_data, message)
- self.total_params_count, self.total_data_count, _, \
- params_bytes_len, params_bytes_offset, params_bytes_displ, \
- data_bytes_len, data_bytes_offset, data_bytes_displ, \
- setup_count = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
- if setup_count > 0:
- setup_bytes_len = setup_count * 2
- if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE + setup_bytes_len:
- raise ProtocolError('Not enough data to decode SMB_COM_TRANSACTION parameters', message.raw_data, message)
- self.setup_bytes = message.parameters_data[self.PAYLOAD_STRUCT_SIZE:self.PAYLOAD_STRUCT_SIZE+setup_bytes_len]
- else:
- self.setup_bytes = ''
- offset = message.HEADER_STRUCT_SIZE + self.PAYLOAD_STRUCT_SIZE + setup_count * 2 + 2 # constant 2 is for the ByteCount field in the SMB header (i.e. field which indicates number of data bytes after the SMB parameters)
- if params_bytes_len > 0:
- self.params_bytes = message.data[params_bytes_offset-offset:params_bytes_offset-offset+params_bytes_len]
- else:
- self.params_bytes = ''
- if data_bytes_len > 0:
- self.data_bytes = message.data[data_bytes_offset-offset:data_bytes_offset-offset+data_bytes_len]
- else:
- self.data_bytes = ''
- class ComTransaction2Request(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.46.1
- """
- PAYLOAD_STRUCT_FORMAT = 'HHHHBBHIHHHHHH'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def __init__(self, max_params_count, max_data_count, max_setup_count,
- total_params_count = 0, total_data_count = 0,
- params_bytes = '', data_bytes = '', setup_bytes = '',
- flags = 0, timeout = 0):
- self.total_params_count = total_params_count or len(params_bytes)
- self.total_data_count = total_data_count or len(data_bytes)
- self.max_params_count = max_params_count
- self.max_data_count = max_data_count
- self.max_setup_count = max_setup_count
- self.flags = flags
- self.timeout = timeout
- self.params_bytes = params_bytes
- self.data_bytes = data_bytes
- self.setup_bytes = setup_bytes
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_TRANSACTION2
- def prepare(self, message):
- setup_bytes_len = len(self.setup_bytes)
- params_bytes_len = len(self.params_bytes)
- data_bytes_len = len(self.data_bytes)
- name = '\0\0'
- padding0 = ''
- offset = message.HEADER_STRUCT_SIZE + self.PAYLOAD_STRUCT_SIZE + setup_bytes_len + 2 # constant 2 is for the ByteCount field in the SMB header (i.e. field which indicates number of data bytes after the SMB parameters)
- if offset % 2 != 0:
- padding0 = '\0'
- offset += 1
- offset += 2 # For the name field
- padding1 = ''
- if offset % 4 != 0:
- padding1 = '\0'*(4-offset%4)
- if params_bytes_len > 0:
- params_bytes_offset = offset
- offset += params_bytes_len
- else:
- params_bytes_offset = 0
- padding2 = ''
- if offset % 4 != 0:
- padding2 = '\0'*(4-offset%4)
- if data_bytes_len > 0:
- data_bytes_offset = offset
- else:
- data_bytes_offset = 0
- message.parameters_data = \
- struct.pack(self.PAYLOAD_STRUCT_FORMAT,
- self.total_params_count,
- self.total_data_count,
- self.max_params_count,
- self.max_data_count,
- self.max_setup_count,
- 0x00, # Reserved1. Must be 0x00
- self.flags,
- self.timeout,
- 0x0000, # Reserved2. Must be 0x0000
- params_bytes_len,
- params_bytes_offset,
- data_bytes_len,
- data_bytes_offset,
- int(setup_bytes_len / 2)) + \
- self.setup_bytes
- message.data = padding0 + name + padding1 + self.params_bytes + padding2 + self.data_bytes
- class ComTransaction2Response(Payload):
- """
- Contains information about a SMB_COM_TRANSACTION2 response from the server
- After decoding, each instance contains the following attributes:
- - total_params_count (integer)
- - total_data_count (integer)
- - setup_bytes (string)
- - data_bytes (string)
- - params_bytes (string)
- References:
- ===========
- - [MS-CIFS]: 2.2.4.46.2
- """
- PAYLOAD_STRUCT_FORMAT = '<HHHHHHHHHBB'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def decode(self, message):
- assert message.command == SMB_COM_TRANSACTION2
- if not message.status.hasError:
- if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
- raise ProtocolError('Not enough data to decode SMB_COM_TRANSACTION2 parameters', message.raw_data, message)
- self.total_params_count, self.total_data_count, _, \
- params_bytes_len, params_bytes_offset, params_bytes_displ, \
- data_bytes_len, data_bytes_offset, data_bytes_displ, \
- setup_count, _ = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
- if setup_count > 0:
- setup_bytes_len = setup_count * 2
- if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE + setup_bytes_len:
- raise ProtocolError('Not enough data to decode SMB_COM_TRANSACTION parameters', message.raw_data, message)
- self.setup_bytes = message.parameters_data[self.PAYLOAD_STRUCT_SIZE:self.PAYLOAD_STRUCT_SIZE+setup_bytes_len]
- else:
- self.setup_bytes = ''
- offset = message.HEADER_STRUCT_SIZE + self.PAYLOAD_STRUCT_SIZE + setup_count * 2 + 2 # constant 2 is for the ByteCount field in the SMB header (i.e. field which indicates number of data bytes after the SMB parameters)
- if params_bytes_len > 0:
- self.params_bytes = message.data[params_bytes_offset-offset:params_bytes_offset-offset+params_bytes_len]
- else:
- self.params_bytes = ''
- if data_bytes_len > 0:
- self.data_bytes = message.data[data_bytes_offset-offset:data_bytes_offset-offset+data_bytes_len]
- else:
- self.data_bytes = ''
- class ComCloseRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.5.1
- """
- PAYLOAD_STRUCT_FORMAT = '<HI'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def __init__(self, fid, last_modified_time = 0xFFFFFFFF):
- self.fid = fid
- self.last_modified_time = last_modified_time
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_CLOSE
- def prepare(self, message):
- message.parameters_data = struct.pack(self.PAYLOAD_STRUCT_FORMAT, self.fid, self.last_modified_time)
- message.data = ''
- class ComOpenAndxRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.41.1
- """
- PAYLOAD_STRUCT_FORMAT = '<HHHHIHIII'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def __init__(self, filename, access_mode, open_mode, flags = 0x0000, search_attributes = 0, file_attributes = 0, create_time = 0, timeout = 0):
- """
- @param create_time: Epoch time value to indicate the time of creation for this file. If zero, we will automatically assign the current time
- @type create_time: int
- @param timeout: Number of milliseconds to wait for blocked open request before failing
- @type timeout: int
- """
- self.filename = filename
- self.access_mode = access_mode
- self.open_mode = open_mode
- self.flags = flags
- self.search_attributes = search_attributes
- self.file_attributes = file_attributes
- self.create_time = create_time or int(time.time())
- self.timeout = timeout
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_OPEN_ANDX
- def prepare(self, message):
- message.parameters_data = \
- self.DEFAULT_ANDX_PARAM_HEADER + \
- struct.pack(self.PAYLOAD_STRUCT_FORMAT,
- self.flags,
- self.access_mode,
- self.search_attributes,
- self.file_attributes,
- self.create_time,
- self.open_mode,
- 0, # AllocationSize
- 0, # Timeout (in milli-secs)
- 0) # Reserved
- message.data = '\0' + self.filename.encode('UTF-16LE') + '\0\0'
- class ComOpenAndxResponse(Payload):
- """
- Contains information about a SMB_COM_OPEN_ANDX response from the server
- After decoding, each instance will contain the following attributes:
- - fid (integer)
- - file_attributes (integer)
- - last_write_time (long)
- - access_rights (integer)
- - resource_type (integer)
- - open_results (integer)
- References:
- ===========
- - [MS-CIFS]: 2.2.4.41.2
- - [MS-SMB]: 2.2.4.1.2
- """
- PAYLOAD_STRUCT_FORMAT = '<BBHHHIIHHHHHHH'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def decode(self, message):
- assert message.command == SMB_COM_OPEN_ANDX
- if not message.status.hasError:
- if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
- raise ProtocolError('Not enough data to decode SMB_COM_OPEN_ANDX parameters', message.raw_data, message)
- _, _, _, self.fid, self.file_attributes, self.last_write_time, _, \
- self.access_rights, self.resource_type, _, self.open_results, _, _, _ = struct.unpack(self.PAYLOAD_STRUCT_FORMAT,
- message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
- class ComWriteAndxRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.43.1
- - [MS-SMB]: 2.2.4.3.1
- """
- PAYLOAD_STRUCT_FORMAT = '<HIIHHHHHI'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def __init__(self, fid, data_bytes, offset, write_mode = 0, timeout = 0):
- """
- @param timeout: Number of milliseconds to wait for blocked write request before failing. Must be zero for writing to regular file
- @type timeout: int
- """
- self.fid = fid
- self.offset = offset
- self.data_bytes = data_bytes
- self.timeout = timeout
- self.write_mode = write_mode
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_WRITE_ANDX
- def prepare(self, message):
- # constant 1 is to account for the pad byte in the message.data
- # constant 2 is for the ByteCount field in the SMB header (i.e. field which indicates number of data bytes after the SMB parameters)
- data_offset = message.HEADER_STRUCT_SIZE + self.DEFAULT_ANDX_PARAM_SIZE + self.PAYLOAD_STRUCT_SIZE + 1 + 2
- data_len = len(self.data_bytes)
- message.parameters_data = \
- self.DEFAULT_ANDX_PARAM_HEADER + \
- struct.pack(self.PAYLOAD_STRUCT_FORMAT,
- self.fid,
- self.offset & 0xFFFFFFFF,
- self.timeout,
- self.write_mode,
- data_len, # Remaining
- 0x0000, # Reserved
- len(self.data_bytes), # DataLength
- data_offset, # DataOffset
- self.offset >> 32) # OffsetHigh field defined in [MS-SMB]: 2.2.4.3.1
- message.data = '\0' + self.data_bytes
- class ComWriteAndxResponse(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.43.2
- - [MS-SMB]: 2.2.4.3.2
- """
- PAYLOAD_STRUCT_FORMAT = '<BBHHHHH' # We follow the SMB_COM_WRITEX_ANDX server extensions in [MS-SMB]: 2.2.4.3.2
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def decode(self, message):
- assert message.command == SMB_COM_WRITE_ANDX
- if not message.status.hasError:
- if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
- raise ProtocolError('Not enough data to decode SMB_COM_WRITE_ANDX parameters', message.raw_data, message)
- _, _, _, count, self.available, high_count, _ = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
- self.count = (count & 0xFFFF) | (high_count << 16)
- class ComReadAndxRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.42.1
- - [MS-SMB]: 2.2.4.2.1
- """
- PAYLOAD_STRUCT_FORMAT = '<HIHHIHI'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def __init__(self, fid, offset, max_return_bytes_count, min_return_bytes_count, timeout = 0, remaining = 0):
- """
- @param timeout: If reading from a regular file, this parameter must be 0.
- @type timeout: int
- """
- self.fid = fid
- self.remaining = remaining
- self.max_return_bytes_count = max_return_bytes_count
- self.min_return_bytes_count = min_return_bytes_count
- self.offset = offset
- self.timeout = timeout
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_READ_ANDX
- def prepare(self, message):
- message.parameters_data = \
- self.DEFAULT_ANDX_PARAM_HEADER + \
- struct.pack(self.PAYLOAD_STRUCT_FORMAT,
- self.fid,
- self.offset & 0xFFFFFFFF,
- self.max_return_bytes_count,
- self.min_return_bytes_count,
- self.timeout or (self.max_return_bytes_count >> 32), # Note that in [MS-SMB]: 2.2.4.2.1, this field can also act as MaxCountHigh field
- self.remaining, # In [MS-CIFS]: 2.2.4.42.1, this field must be set to 0x0000
- self.offset >> 32)
- message.data = ''
- class ComReadAndxResponse(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.42.2
- - [MS-SMB]: 2.2.4.2.2
- """
- PAYLOAD_STRUCT_FORMAT = '<BBHHHHHHHHHHH'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def decode(self, message):
- assert message.command == SMB_COM_READ_ANDX
- if not message.status.hasError:
- if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
- raise ProtocolError('Not enough data to decode SMB_COM_READ_ANDX parameters', message.raw_data, message)
- _, _, _, _, _, _, self.data_length, data_offset, _, _, _, _, _ = struct.unpack(self.PAYLOAD_STRUCT_FORMAT,
- message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
- offset = data_offset - message.HEADER_STRUCT_SIZE - self.PAYLOAD_STRUCT_SIZE - 2 # constant 2 is for the ByteCount field in the SMB header (i.e. field which indicates number of data bytes after the SMB parameters)
- self.data = message.data[offset:offset+self.data_length]
- assert len(self.data) == self.data_length
- class ComDeleteRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.7.1
- """
- def __init__(self, filename_pattern, search_attributes = 0):
- self.filename_pattern = filename_pattern
- self.search_attributes = search_attributes
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_DELETE
- def prepare(self, message):
- message.parameters_data = struct.pack('<H', self.search_attributes)
- message.data = '\x04' + self.filename_pattern.encode('UTF-16LE') + '\0\0'
- class ComCreateDirectoryRequest(Payload):
- """
- Although this command has been marked deprecated in [MS-CIFS], we continue to use it for its simplicity
- as compared to its replacement TRANS2_CREATE_DIRECTORY sub-command [MS-CIFS]: 2.2.6.14
- References:
- ===========
- - [MS-CIFS]: 2.2.4.1.1
- """
- def __init__(self, path):
- self.path = path
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_CREATE_DIRECTORY
- def prepare(self, message):
- message.parameters_data = ''
- message.data = '\x04' + self.path.encode('UTF-16LE') + '\0\0'
- class ComDeleteDirectoryRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.2.1
- """
- def __init__(self, path):
- self.path = path
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_DELETE_DIRECTORY
- def prepare(self, message):
- message.parameters_data = ''
- message.data = '\x04' + self.path.encode('UTF-16LE') + '\0\0'
- class ComRenameRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.8.1
- """
- def __init__(self, old_path, new_path, search_attributes = 0):
- self.old_path = old_path
- self.new_path = new_path
- self.search_attributes = search_attributes
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_RENAME
- def prepare(self, message):
- message.parameters_data = struct.pack('<H', self.search_attributes)
- message.data = '\x04' + self.old_path.encode('UTF-16LE') + '\x00\x00\x04\x00' + self.new_path.encode('UTF-16LE') + '\x00\x00'
- class ComEchoRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.39.1
- """
- def __init__(self, echo_data = '', echo_count = 1):
- self.echo_count = echo_count
- self.echo_data = echo_data
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_ECHO
- message.tid = 0xFFFF
- def prepare(self, message):
- message.parameters_data = struct.pack('<H', self.echo_count)
- message.data = self.echo_data
- class ComEchoResponse(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.39.2
- """
- def decode(self, message):
- self.sequence_number = struct.unpack('<H', message.parameters_data[:2])[0]
- self.data = message.data
- class ComNTTransactRequest(Payload):
- """
- References:
- ===========
- - [MS-CIFS]: 2.2.4.62.1
- """
- PAYLOAD_STRUCT_FORMAT = '<BHIIIIIIIIBH'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def __init__(self, function, max_params_count, max_data_count, max_setup_count,
- total_params_count = 0, total_data_count = 0,
- params_bytes = '', setup_bytes = '', data_bytes = ''):
- self.function = function
- self.total_params_count = total_params_count or len(params_bytes)
- self.total_data_count = total_data_count or len(data_bytes)
- self.max_params_count = max_params_count
- self.max_data_count = max_data_count
- self.max_setup_count = max_setup_count
- self.params_bytes = params_bytes
- self.setup_bytes = setup_bytes
- self.data_bytes = data_bytes
- def initMessage(self, message):
- Payload.initMessage(self, message)
- message.command = SMB_COM_NT_TRANSACT
- def prepare(self, message):
- setup_bytes_len = len(self.setup_bytes)
- params_bytes_len = len(self.params_bytes)
- data_bytes_len = len(self.data_bytes)
- padding0 = ''
- offset = message.HEADER_STRUCT_SIZE + self.PAYLOAD_STRUCT_SIZE + setup_bytes_len + 2 # constant 2 is for the ByteCount field in the SMB header (i.e. field which indicates number of data bytes after the SMB parameters)
- if offset % 4 != 0:
- padding0 = '\0'*(4-offset%4)
- offset += (4-offset%4)
- if params_bytes_len > 0:
- params_bytes_offset = offset
- else:
- params_bytes_offset = 0
- offset += params_bytes_len
- padding1 = ''
- if offset % 4 != 0:
- padding1 = '\0'*(4-offset%4)
- offset += (4-offset%4)
- if data_bytes_len > 0:
- data_bytes_offset = offset
- else:
- data_bytes_offset = 0
- message.parameters_data = \
- struct.pack(self.PAYLOAD_STRUCT_FORMAT,
- self.max_setup_count,
- 0x00, # Reserved1. Must be 0x00
- self.total_params_count,
- self.total_data_count,
- self.max_params_count,
- self.max_data_count,
- params_bytes_len,
- params_bytes_offset,
- data_bytes_len,
- data_bytes_offset,
- int(setup_bytes_len / 2),
- self.function) + \
- self.setup_bytes
- message.data = padding0 + self.params_bytes + padding1 + self.data_bytes
- class ComNTTransactResponse(Payload):
- """
- Contains information about a SMB_COM_NT_TRANSACT response from the server
- After decoding, each instance contains the following attributes:
- - total_params_count (integer)
- - total_data_count (integer)
- - setup_bytes (string)
- - data_bytes (string)
- - params_bytes (string)
- References:
- ===========
- - [MS-CIFS]: 2.2.4.62.2
- """
- PAYLOAD_STRUCT_FORMAT = '<3sIIIIIIIIBH'
- PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
- def decode(self, message):
- assert message.command == SMB_COM_NT_TRANSACT
- if not message.status.hasError:
- _, self.total_params_count, self.total_data_count, \
- params_count, params_offset, params_displ, \
- data_count, data_offset, data_displ, setup_count = struct.unpack(self.PAYLOAD_STRUCT_FORMAT,
- message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
- self.setup_bytes = message.parameters_data[self.PAYLOAD_STRUCT_SIZE:self.PAYLOAD_STRUCT_SIZE+setup_count*2]
- if params_count > 0:
- params_offset -= message.HEADER_STRUCT_SIZE + self.PAYLOAD_STRUCT_SIZE + setup_count*2 + 2
- self.params_bytes = message.data[params_offset:params_offset+params_count]
- else:
- self.params_bytes = ''
- if data_count > 0:
- data_offset -= message.HEADER_STRUCT_SIZE + self.PAYLOAD_STRUCT_SIZE + setup_count*2 + 2
- self.data_bytes = message.data[data_offset:data_offset+data_count]
- else:
- self.data_bytes = ''
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement