Advertisement
Guest User

lol

a guest
Oct 2nd, 2018
172
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 59.28 KB | None | 0 0
  1. import os, sys, struct, types, logging, binascii, time
  2.     from StringIO import StringIO
  3.     from smb_constants import *
  4.      
  5.      
  6.     # Set to True if you want to enable support for extended security. Required for Windows Vista and later
  7.     SUPPORT_EXTENDED_SECURITY = True
  8.      
  9.     # Set to True if you want to enable SMB2 protocol.
  10.     SUPPORT_SMB2 = True
  11.      
  12.     # Supported dialects
  13.     DIALECTS = [ ]
  14.     for i, ( name, dialect ) in enumerate([ ( 'NT_LAN_MANAGER_DIALECT', 'NT LM 0.12' ), ]):
  15.         DIALECTS.append(dialect)
  16.         globals()[name] = i
  17.      
  18.     DIALECTS2 = [ ]
  19.     for i, ( name, dialect ) in enumerate([ ( 'SMB2_DIALECT', 'SMB 2.002' ) ]):
  20.         DIALECTS2.append(dialect)
  21.         globals()[name] = i + len(DIALECTS)
  22.      
  23.      
  24.     class UnsupportedFeature(Exception):
  25.         """
  26.        Raised when an supported feature is present/required in the protocol but is not
  27.        currently supported by pysmb
  28.        """
  29.         pass
  30.      
  31.      
  32.     class ProtocolError(Exception):
  33.      
  34.         def __init__(self, message, data_buf = None, smb_message = None):
  35.             self.message = message
  36.             self.data_buf = data_buf
  37.             self.smb_message = smb_message
  38.      
  39.         def __str__(self):
  40.             b = StringIO()
  41.             b.write(self.message + os.linesep)
  42.             if self.smb_message:
  43.                 b.write('=' * 20 + ' SMB Message ' + '=' * 20 + os.linesep)
  44.                 b.write(str(self.smb_message))
  45.      
  46.             if self.data_buf:
  47.                 b.write('=' * 20 + ' SMB Data Packet (hex) ' + '=' * 20 + os.linesep)
  48.                 b.write(binascii.hexlify(self.data_buf))
  49.                 b.write(os.linesep)
  50.      
  51.             return b.getvalue()
  52.      
  53.     class SMB2ProtocolHeaderError(ProtocolError):
  54.      
  55.         def __init__(self):
  56.             ProtocolError.__init__(self, "Packet header belongs to SMB2")
  57.      
  58.     class OperationFailure(Exception):
  59.      
  60.         def __init__(self, message, smb_messages):
  61.             self.args = [ message ]
  62.             self.message = message
  63.             self.smb_messages = smb_messages
  64.      
  65.         def __str__(self):
  66.             b = StringIO()
  67.             b.write(self.message + os.linesep)
  68.      
  69.             for idx, m in enumerate(self.smb_messages):
  70.                 b.write('=' * 20 + ' SMB Message %d ' % idx + '=' * 20 + os.linesep)
  71.                 b.write('SMB Header:' + os.linesep)
  72.                 b.write('-----------' + os.linesep)
  73.                 b.write(str(m))
  74.                 b.write('SMB Data Packet (hex):' + os.linesep)
  75.                 b.write('----------------------' + os.linesep)
  76.                 b.write(binascii.hexlify(m.raw_data))
  77.                 b.write(os.linesep)
  78.      
  79.             return b.getvalue()
  80.      
  81.      
  82.     class SMBError:
  83.      
  84.         def __init__(self):
  85.             self.reset()
  86.      
  87.         def reset(self):
  88.             self.internal_value = 0L
  89.             self.is_ntstatus = True
  90.      
  91.         def __str__(self):
  92.             if self.is_ntstatus:
  93.                 return 'NTSTATUS=0x%08X' % self.internal_value
  94.             else:
  95.                 return 'ErrorClass=0x%02X ErrorCode=0x%04X' % ( self.internal_value >> 24, self.internal_value & 0xFFFF )
  96.      
  97.         @property
  98.         def hasError(self):
  99.             return self.internal_value != 0
  100.      
  101.      
  102.     class SMBMessage:
  103.      
  104.         HEADER_STRUCT_FORMAT = "<4sBIBHHQxxHHHHB"
  105.         HEADER_STRUCT_SIZE = struct.calcsize(HEADER_STRUCT_FORMAT)
  106.      
  107.         log = logging.getLogger('SMB.SMBMessage')
  108.         protocol = 1
  109.      
  110.         def __init__(self, payload = None):
  111.             self.reset()
  112.             if payload:
  113.                 self.payload = payload
  114.                 self.payload.initMessage(self)
  115.      
  116.         def __str__(self):
  117.             b = StringIO()
  118.             b.write('Command: 0x%02X (%s) %s' % ( self.command, SMB_COMMAND_NAMES.get(self.command, '<unknown>'), os.linesep ))
  119.             b.write('Status: %s %s' % ( str(self.status), os.linesep ))
  120.             b.write('Flags: 0x%02X %s' % ( self.flags, os.linesep ))
  121.             b.write('Flags2: 0x%04X %s' % ( self.flags2, os.linesep ))
  122.             b.write('PID: %d %s' % ( self.pid, os.linesep ))
  123.             b.write('UID: %d %s' % ( self.uid, os.linesep ))
  124.             b.write('MID: %d %s' % ( self.mid, os.linesep ))
  125.             b.write('TID: %d %s' % ( self.tid, os.linesep ))
  126.             b.write('Security: 0x%016X %s' % ( self.security, os.linesep ))
  127.             b.write('Parameters: %d bytes %s%s %s' % ( len(self.parameters_data), os.linesep, binascii.hexlify(self.parameters_data), os.linesep ))
  128.             b.write('Data: %d bytes %s%s %s' % ( len(self.data), os.linesep, binascii.hexlify(self.data), os.linesep ))
  129.             return b.getvalue()
  130.      
  131.         def reset(self):
  132.             self.raw_data = ''
  133.             self.command = 0
  134.             self.status = SMBError()
  135.             self.flags = 0
  136.             self.flags2 = 0
  137.             self.pid = 0
  138.             self.tid = 0
  139.             self.uid = 0
  140.             self.mid = 0
  141.             self.security = 0L
  142.             self.parameters_data = ''
  143.             self.data = ''
  144.             self.payload = None
  145.      
  146.         @property
  147.         def isReply(self):
  148.             return bool(self.flags & SMB_FLAGS_REPLY)
  149.      
  150.         @property
  151.         def hasExtendedSecurity(self):
  152.             return bool(self.flags2 & SMB_FLAGS2_EXTENDED_SECURITY)
  153.      
  154.         def encode(self):
  155.             """
  156.            Encode this SMB message into a series of bytes suitable to be embedded with a NetBIOS session message.
  157.            AssertionError will be raised if this SMB message has not been initialized with a Payload instance
  158.    
  159.            @return: a string containing the encoded SMB message
  160.            """
  161.             assert self.payload
  162.      
  163.             self.pid = os.getpid()
  164.             self.payload.prepare(self)
  165.      
  166.             parameters_len = len(self.parameters_data)
  167.             assert parameters_len % 2 == 0
  168.      
  169.             headers_data = struct.pack(self.HEADER_STRUCT_FORMAT,
  170.                                        '\xFFSMB', self.command, self.status.internal_value, self.flags,
  171.                                        self.flags2, (self.pid >> 16) & 0xFFFF, self.security, self.tid,
  172.                                        self.pid & 0xFFFF, self.uid, self.mid, int(parameters_len / 2))
  173.             return headers_data + self.parameters_data + struct.pack('<H', len(self.data)) + self.data
  174.      
  175.         def decode(self, buf):
  176.             """
  177.            Decodes the SMB message in buf.
  178.            All fields of the SMBMessage object will be reset to default values before decoding.
  179.            On errors, do not assume that the fields will be reinstated back to what they are before
  180.            this method is invoked.
  181.    
  182.            @param buf: data containing one complete SMB message
  183.            @type buf: string
  184.            @return: a positive integer indicating the number of bytes used in buf to decode this SMB message
  185.            @raise ProtocolError: raised when decoding fails
  186.            """
  187.             buf_len = len(buf)
  188.             if buf_len < self.HEADER_STRUCT_SIZE:
  189.                 # We need at least 32 bytes (header) + 1 byte (parameter count)
  190.                 raise ProtocolError('Not enough data to decode SMB header', buf)
  191.      
  192.             self.reset()
  193.      
  194.             protocol, self.command, status, self.flags, \
  195.             self.flags2, pid_high, self.security, self.tid, \
  196.             pid_low, self.uid, self.mid, params_count = struct.unpack(self.HEADER_STRUCT_FORMAT, buf[:self.HEADER_STRUCT_SIZE])
  197.      
  198.             if protocol == '\xFESMB':
  199.                 raise SMB2ProtocolHeaderError()
  200.             if protocol != '\xFFSMB':
  201.                 raise ProtocolError('Invalid 4-byte protocol field', buf)
  202.      
  203.             self.pid = (pid_high << 16) | pid_low
  204.             self.status.internal_value = status
  205.             self.status.is_ntstatus = bool(self.flags2 & SMB_FLAGS2_NT_STATUS)
  206.      
  207.             offset = self.HEADER_STRUCT_SIZE
  208.             if buf_len < params_count * 2 + 2:
  209.                 # Not enough data in buf to decode up to body length
  210.                 raise ProtocolError('Not enough data. Parameters list decoding failed', buf)
  211.      
  212.             datalen_offset = offset + params_count*2
  213.             body_len = struct.unpack('<H', buf[datalen_offset:datalen_offset+2])[0]
  214.             if body_len > 0 and buf_len < (datalen_offset + 2 + body_len):
  215.                 # Not enough data in buf to decode body
  216.                 raise ProtocolError('Not enough data. Body decoding failed', buf)
  217.      
  218.             self.parameters_data = buf[offset:datalen_offset]
  219.      
  220.             if body_len > 0:
  221.                 self.data = buf[datalen_offset+2:datalen_offset+2+body_len]
  222.      
  223.             self.raw_data = buf
  224.             self._decodePayload()
  225.      
  226.             return self.HEADER_STRUCT_SIZE + params_count * 2 + 2 + body_len
  227.      
  228.         def _decodePayload(self):
  229.             if self.command == SMB_COM_READ_ANDX:
  230.                 self.payload = ComReadAndxResponse()
  231.             elif self.command == SMB_COM_WRITE_ANDX:
  232.                 self.payload = ComWriteAndxResponse()
  233.             elif self.command == SMB_COM_TRANSACTION:
  234.                 self.payload = ComTransactionResponse()
  235.             elif self.command == SMB_COM_TRANSACTION2:
  236.                 self.payload = ComTransaction2Response()
  237.             elif self.command == SMB_COM_OPEN_ANDX:
  238.                 self.payload = ComOpenAndxResponse()
  239.             elif self.command == SMB_COM_NT_CREATE_ANDX:
  240.                 self.payload = ComNTCreateAndxResponse()
  241.             elif self.command == SMB_COM_TREE_CONNECT_ANDX:
  242.                 self.payload = ComTreeConnectAndxResponse()
  243.             elif self.command == SMB_COM_ECHO:
  244.                 self.payload = ComEchoResponse()
  245.             elif self.command == SMB_COM_SESSION_SETUP_ANDX:
  246.                 self.payload = ComSessionSetupAndxResponse()
  247.             elif self.command == SMB_COM_NEGOTIATE:
  248.                 self.payload = ComNegotiateResponse()
  249.      
  250.             if self.payload:
  251.                 self.payload.decode(self)
  252.      
  253.      
  254.     class Payload:
  255.      
  256.         DEFAULT_ANDX_PARAM_HEADER = '\xFF\x00\x00\x00'
  257.         DEFAULT_ANDX_PARAM_SIZE = 4
  258.      
  259.         def initMessage(self, message):
  260.             # SMB_FLAGS2_UNICODE must always be enabled. Without this, almost all the Payload subclasses will need to be
  261.             # rewritten to check for OEM/Unicode strings which will be tedious. Fortunately, almost all tested CIFS services
  262.             # support SMB_FLAGS2_UNICODE by default.
  263.             assert message.payload == self
  264.             message.flags =  SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS
  265.             message.flags2 = SMB_FLAGS2_UNICODE | SMB_FLAGS2_NT_STATUS | SMB_FLAGS2_LONG_NAMES | SMB_FLAGS2_EAS
  266.      
  267.             if SUPPORT_EXTENDED_SECURITY:
  268.                 message.flags2 |= SMB_FLAGS2_EXTENDED_SECURITY | SMB_FLAGS2_SMB_SECURITY_SIGNATURE
  269.      
  270.         def prepare(self, message):
  271.             raise NotImplementedError
  272.      
  273.         def decode(self, message):
  274.             raise NotImplementedError
  275.      
  276.      
  277.     class ComNegotiateRequest(Payload):
  278.         """
  279.        References:
  280.        ===========
  281.        - [MS-CIFS]: 2.2.4.52.1
  282.        - [MS-SMB]: 2.2.4.5.1
  283.        """
  284.      
  285.         def initMessage(self, message):
  286.             Payload.initMessage(self, message)
  287.             message.command = SMB_COM_NEGOTIATE
  288.      
  289.         def prepare(self, message):
  290.             assert message.payload == self
  291.             message.parameters_data = ''
  292.             if SUPPORT_SMB2:
  293.                 message.data = ''.join(map(lambda s: '\x02'+s+'\x00', DIALECTS + DIALECTS2))
  294.             else:
  295.                 message.data = ''.join(map(lambda s: '\x02'+s+'\x00', DIALECTS))
  296.      
  297.      
  298.     class ComNegotiateResponse(Payload):
  299.         """
  300.        Contains information on the SMB_COM_NEGOTIATE response from server
  301.    
  302.        After calling the decode method, each instance will contain the following attributes,
  303.        - security_mode (integer)
  304.        - max_mpx_count (integer)
  305.        - max_number_vcs (integer)
  306.        - max_buffer_size (long)
  307.        - max_raw_size (long)
  308.        - session_key (long)
  309.        - capabilities (long)
  310.        - system_time (long)
  311.        - server_time_zone (integer)
  312.        - challenge_length (integer)
  313.    
  314.        If the underlying SMB message's flag2 does not have SMB_FLAGS2_EXTENDED_SECURITY bit enabled,
  315.        then the instance will have the following additional attributes,
  316.        - challenge (string)
  317.        - domain (unicode)
  318.    
  319.        If the underlying SMB message's flags2 has SMB_FLAGS2_EXTENDED_SECURITY bit enabled,
  320.        then the instance will have the following additional attributes,
  321.        - server_guid (string)
  322.        - security_blob (string)
  323.    
  324.        References:
  325.        ===========
  326.        - [MS-SMB]: 2.2.4.5.2.1
  327.        - [MS-CIFS]: 2.2.4.52.2
  328.        """
  329.      
  330.         PAYLOAD_STRUCT_FORMAT = '<HBHHIIIIQHB'
  331.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  332.      
  333.         def decode(self, message):
  334.             assert message.command == SMB_COM_NEGOTIATE
  335.      
  336.             if not message.isReply:
  337.                 raise ProtocolError('Not a SMB_COM_NEGOTIATE reply', message.raw_data, message)
  338.      
  339.             self.security_mode, self.max_mpx_count, self.max_number_vcs, self.max_buffer_size, \
  340.             self.max_raw_size, self.session_key, self.capabilities, self.system_time, self.server_time_zone, \
  341.             self.challenge_length = ( 0, ) * 10
  342.      
  343.             data_len = len(message.parameters_data)
  344.             if data_len < 2:
  345.                 raise ProtocolError('Not enough data to decode SMB_COM_NEGOTIATE dialect_index field', message.raw_data, message)
  346.      
  347.             self.dialect_index = struct.unpack('<H', message.parameters_data[:2])[0]
  348.             if self.dialect_index == NT_LAN_MANAGER_DIALECT:
  349.                 if data_len != (0x11 * 2):
  350.                     raise ProtocolError('NT LAN Manager dialect selected in SMB_COM_NEGOTIATE but parameters bytes count (%d) does not meet specs' % data_len,
  351.                                         message.raw_data, message)
  352.                 else:
  353.                     _, self.security_mode, self.max_mpx_count, self.max_number_vcs, self.max_buffer_size, \
  354.                     self.max_raw_size, self.session_key, self.capabilities, self.system_time, self.server_time_zone, \
  355.                     self.challenge_length = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
  356.             elif self.dialect_index == 0xFFFF:
  357.                 raise ProtocolError('Server does not support any of the pysmb dialects. Please email pysmb to add in support for your OS',
  358.                                     message.raw_data, message)
  359.             else:
  360.                 raise ProtocolError('Unknown dialect index (0x%04X)' % self.dialect_index, message.raw_data, message)
  361.      
  362.             data_len = len(message.data)
  363.             if not message.hasExtendedSecurity:
  364.                 self.challenge, self.domain = '', ''
  365.                 if self.challenge_length > 0:
  366.                     if data_len >= self.challenge_length:
  367.                         self.challenge = message.data[:self.challenge_length]
  368.      
  369.                         s = ''
  370.                         offset = self.challenge_length
  371.                         while offset < data_len:
  372.                             _s = message.data[offset:offset+2]
  373.                             if _s == '\0\0':
  374.                                 self.domain = s.decode('UTF-16LE')
  375.                                 break
  376.                             else:
  377.                                 s += _s
  378.                                 offset += 2
  379.                     else:
  380.                         raise ProtocolError('Not enough data to decode SMB_COM_NEGOTIATE (without security extensions) Challenge field', message.raw_data, message)
  381.             else:
  382.                 if data_len < 16:
  383.                     raise ProtocolError('Not enough data to decode SMB_COM_NEGOTIATE (with security extensions) ServerGUID field', message.raw_data, message)
  384.      
  385.                 self.server_guid = message.data[:16]
  386.                 self.security_blob = message.data[16:]
  387.      
  388.         @property
  389.         def supportsExtendedSecurity(self):
  390.             return bool(self.capabilities & CAP_EXTENDED_SECURITY)
  391.      
  392.      
  393.     class ComSessionSetupAndxRequest__WithSecurityExtension(Payload):
  394.         """
  395.        References:
  396.        ===========
  397.        - [MS-SMB]: 2.2.4.6.1
  398.        """
  399.      
  400.         PAYLOAD_STRUCT_FORMAT = '<HHHIHII'
  401.      
  402.         def __init__(self, session_key, security_blob):
  403.             self.session_key = session_key
  404.             self.security_blob = security_blob
  405.      
  406.         def initMessage(self, message):
  407.             Payload.initMessage(self, message)
  408.             message.command = SMB_COM_SESSION_SETUP_ANDX
  409.      
  410.         def prepare(self, message):
  411.             assert message.hasExtendedSecurity
  412.      
  413.             message.flags2 |= SMB_FLAGS2_UNICODE
  414.      
  415.             cap = CAP_UNICODE | CAP_STATUS32 | CAP_EXTENDED_SECURITY | CAP_NT_SMBS
  416.      
  417.             message.parameters_data = \
  418.                 self.DEFAULT_ANDX_PARAM_HEADER + \
  419.                 struct.pack(self.PAYLOAD_STRUCT_FORMAT,
  420.                             16644, 10, 1, self.session_key, len(self.security_blob), 0, cap)
  421.      
  422.             message.data = self.security_blob
  423.             if (SMBMessage.HEADER_STRUCT_SIZE + len(message.parameters_data) + len(message.data)) % 2 != 0:
  424.                 message.data = message.data + '\0'
  425.             message.data = message.data + '\0' * 4
  426.      
  427.      
  428.     class ComSessionSetupAndxRequest__NoSecurityExtension(Payload):
  429.         """
  430.        References:
  431.        ===========
  432.        - [MS-CIFS]: 2.2.4.53.1
  433.        """
  434.      
  435.         PAYLOAD_STRUCT_FORMAT = '<HHHIHHII'
  436.      
  437.         def __init__(self, session_key, username, password, is_unicode, domain):
  438.             self.username = username
  439.             self.session_key = session_key
  440.             self.password = password
  441.             self.is_unicode = is_unicode
  442.             self.domain = domain
  443.      
  444.         def initMessage(self, message):
  445.             Payload.initMessage(self, message)
  446.             message.command = SMB_COM_SESSION_SETUP_ANDX
  447.      
  448.         def prepare(self, message):
  449.             if self.is_unicode:
  450.                 message.flags2 |= SMB_FLAGS2_UNICODE
  451.             else:
  452.                 message.flags2 &= (~SMB_FLAGS2_UNICODE & 0xFFFF)
  453.      
  454.             password_len = len(self.password)
  455.             message.parameters_data = \
  456.                 self.DEFAULT_ANDX_PARAM_HEADER + \
  457.                 struct.pack(self.PAYLOAD_STRUCT_FORMAT,
  458.                             16644, 10, 0, self.session_key,
  459.                             (not self.is_unicode and password_len) or 0,
  460.                             (self.is_unicode and password_len) or 0,
  461.                             0,
  462.                             CAP_UNICODE | CAP_LARGE_FILES | CAP_STATUS32)
  463.      
  464.             est_offset = SMBMessage.HEADER_STRUCT_SIZE + len(message.parameters_data)  # To check if data until SMB paramaters are aligned to a 16-bit boundary
  465.      
  466.             message.data = self.password
  467.             if (est_offset + len(message.data)) % 2 != 0 and message.flags2 & SMB_FLAGS2_UNICODE:
  468.                 message.data = message.data + '\0'
  469.      
  470.             if message.flags2 & SMB_FLAGS2_UNICODE:
  471.                 message.data = message.data + self.username.encode('UTF-16LE') + '\0'
  472.             else:
  473.                 message.data = message.data + str(self.username) + '\0'
  474.      
  475.             if (est_offset + len(message.data)) % 2 != 0 and message.flags2 & SMB_FLAGS2_UNICODE:
  476.                 message.data = message.data + '\0'
  477.      
  478.             if message.flags2 & SMB_FLAGS2_UNICODE:
  479.                 message.data = message.data + self.domain.encode('UTF-16LE') + '\0\0' + 'pysmb'.encode('UTF-16LE') + '\0\0'
  480.             else:
  481.                 message.data = message.data + self.domain + '\0pysmb\0'
  482.      
  483.      
  484.     class ComSessionSetupAndxResponse(Payload):
  485.         """
  486.        Contains information on the SMB_COM_SESSION_SETUP_ANDX response from server
  487.    
  488.        If the underlying SMB message's flags2 does not have SMB_FLAGS2_EXTENDED_SECURITY bit enabled,
  489.        then the instance will have the following attributes,
  490.        - action
  491.    
  492.        If the underlying SMB message's flags2 has SMB_FLAGS2_EXTENDED_SECURITY bit enabled
  493.        and the message status is STATUS_MORE_PROCESSING_REQUIRED or equals to 0x00 (no error),
  494.        then the instance will have the following attributes,
  495.        - action
  496.        - securityblob
  497.    
  498.        If the underlying SMB message's flags2 has SMB_FLAGS2_EXTENDED_SECURITY bit enabled but
  499.        the message status is not STATUS_MORE_PROCESSING_REQUIRED
  500.    
  501.        References:
  502.        ===========
  503.        - [MS-SMB]: 2.2.4.6.2
  504.        - [MS-CIFS]: 2.2.4.53.2
  505.        """
  506.      
  507.         NOSECURE_PARAMETER_STRUCT_FORMAT = '<BBHH'
  508.         NOSECURE_PARAMETER_STRUCT_SIZE = struct.calcsize(NOSECURE_PARAMETER_STRUCT_FORMAT)
  509.      
  510.         SECURE_PARAMETER_STRUCT_FORMAT = '<BBHHH'
  511.         SECURE_PARAMETER_STRUCT_SIZE = struct.calcsize(SECURE_PARAMETER_STRUCT_FORMAT)
  512.      
  513.         def decode(self, message):
  514.             assert message.command == SMB_COM_SESSION_SETUP_ANDX
  515.             if not message.hasExtendedSecurity:
  516.                 if not message.status.hasError:
  517.                     if len(message.parameters_data) < self.NOSECURE_PARAMETER_STRUCT_SIZE:
  518.                         raise ProtocolError('Not enough data to decode SMB_COM_SESSION_SETUP_ANDX (no security extensions) parameters', message.raw_data, message)
  519.      
  520.                     _, _, _, self.action = struct.unpack(self.NOSECURE_PARAMETER_STRUCT_FORMAT, message.parameters_data[:self.NOSECURE_PARAMETER_STRUCT_SIZE])
  521.             else:
  522.                 if not message.status.hasError or message.status.internal_value == 0xc0000016:   # STATUS_MORE_PROCESSING_REQUIRED
  523.                     if len(message.parameters_data) < self.SECURE_PARAMETER_STRUCT_SIZE:
  524.                         raise ProtocolError('Not enough data to decode SMB_COM_SESSION_SETUP_ANDX (with security extensions) parameters', message.raw_data, message)
  525.      
  526.                     _, _, _, self.action, blob_length = struct.unpack(self.SECURE_PARAMETER_STRUCT_FORMAT, message.parameters_data[:self.SECURE_PARAMETER_STRUCT_SIZE])
  527.                     if len(message.data) < blob_length:
  528.                         raise ProtocolError('Not enough data to decode SMB_COM_SESSION_SETUP_ANDX (with security extensions) security blob', message.raw_data, message)
  529.      
  530.                     self.security_blob = message.data[:blob_length]
  531.      
  532.      
  533.     class ComTreeConnectAndxRequest(Payload):
  534.         """
  535.        References:
  536.        ===========
  537.        - [MS-CIFS]: 2.2.4.55.1
  538.        - [MS-SMB]: 2.2.4.7.1
  539.        """
  540.      
  541.         PAYLOAD_STRUCT_FORMAT = '<HH'
  542.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  543.      
  544.         def __init__(self, path, service, password = ''):
  545.             self.path = path
  546.             self.service = service
  547.             self.password = password + '\0'
  548.      
  549.         def initMessage(self, message):
  550.             Payload.initMessage(self, message)
  551.             message.command = SMB_COM_TREE_CONNECT_ANDX
  552.      
  553.         def prepare(self, message):
  554.             password_len = len(self.password)
  555.             message.parameters_data = \
  556.                 self.DEFAULT_ANDX_PARAM_HEADER + \
  557.                 struct.pack(self.PAYLOAD_STRUCT_FORMAT,
  558.                             0x08 | \
  559.                                 ((message.hasExtendedSecurity and 0x0004) or 0x00) | \
  560.                                 ((message.tid and message.tid != 0xFFFF and 0x0001) or 0x00),  # Disconnect tid, if message.tid must be non-zero
  561.                             password_len)
  562.      
  563.             padding = ''
  564.             if password_len % 2 == 0:
  565.                 padding = '\0'
  566.      
  567.             # Note that service field is never encoded in UTF-16LE. [MS-CIFS]: 2.2.1.1
  568.             message.data = self.password + padding + self.path.encode('UTF-16LE') + '\0\0' + self.service + '\0'
  569.      
  570.      
  571.     class ComTreeConnectAndxResponse(Payload):
  572.         """
  573.        Contains information about the SMB_COM_TREE_CONNECT_ANDX response from the server.
  574.    
  575.        If the message has no errors, each instance contains the following attributes:
  576.        - optional_support
  577.    
  578.        References:
  579.        ===========
  580.        - [MS-CIFS]: 2.2.4.55.2
  581.        """
  582.      
  583.         PAYLOAD_STRUCT_FORMAT = '<BBHH'
  584.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  585.      
  586.         def decode(self, message):
  587.             assert message.command == SMB_COM_TREE_CONNECT_ANDX
  588.      
  589.             if not message.status.hasError:
  590.                 if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
  591.                     raise ProtocolError('Not enough data to decode SMB_COM_TREE_CONNECT_ANDX parameters', message.raw_data, message)
  592.      
  593.                 _, _, _, self.optional_support = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
  594.      
  595.      
  596.     class ComNTCreateAndxRequest(Payload):
  597.         """
  598.        References:
  599.        ===========
  600.        - [MS-CIFS]: 2.2.4.64.1
  601.        - [MS-SMB]: 2.2.4.9.1
  602.        """
  603.      
  604.         PAYLOAD_STRUCT_FORMAT = '<BHIIIQIIIIIB'
  605.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  606.      
  607.         def __init__(self, filename, flags = 0, root_fid = 0, access_mask = 0, allocation_size = 0L, ext_attr = 0,
  608.                      share_access = 0, create_disp = 0, create_options = 0, impersonation = 0, security_flags = 0):
  609.             self.filename = (filename + '\0').encode('UTF-16LE')
  610.             self.flags = flags
  611.             self.root_fid = root_fid
  612.             self.access_mask = access_mask
  613.             self.allocation_size = allocation_size
  614.             self.ext_attr = ext_attr
  615.             self.share_access = share_access
  616.             self.create_disp = create_disp
  617.             self.create_options = create_options
  618.             self.impersonation = impersonation
  619.             self.security_flags = security_flags
  620.      
  621.         def initMessage(self, message):
  622.             Payload.initMessage(self, message)
  623.             message.command = SMB_COM_NT_CREATE_ANDX
  624.      
  625.         def prepare(self, message):
  626.             filename_len = len(self.filename)
  627.      
  628.             message.parameters_data = \
  629.                 self.DEFAULT_ANDX_PARAM_HEADER + \
  630.                 struct.pack(self.PAYLOAD_STRUCT_FORMAT,
  631.                             0x00,                  # reserved
  632.                             filename_len,          # NameLength
  633.                             self.flags,            # Flags
  634.                             self.root_fid,         # RootDirectoryFID
  635.                             self.access_mask,      # DesiredAccess
  636.                             self.allocation_size,  # AllocationSize
  637.                             self.ext_attr,         # ExtFileAttributes
  638.                             self.share_access,     # ShareAccess
  639.                             self.create_disp,      # CreateDisposition
  640.                             self.create_options,   # CreateOptions
  641.                             self.impersonation,    # ImpersonationLevel
  642.                             self.security_flags)   # SecurityFlags
  643.      
  644.             padding = ''
  645.             if (message.HEADER_STRUCT_SIZE + len(message.parameters_data)) % 2 != 0:
  646.                 padding = '\0'
  647.      
  648.             message.data = padding + self.filename
  649.      
  650.      
  651.     class ComNTCreateAndxResponse(Payload):
  652.         """
  653.        Contains (partial) information about the SMB_COM_NT_CREATE_ANDX response from the server.
  654.    
  655.        Each instance contains the following attributes after decoding:
  656.        - oplock_level
  657.        - fid
  658.    
  659.        References:
  660.        ===========
  661.        - [MS-CIFS]: 2.2.4.64.2
  662.        """
  663.         PAYLOAD_STRUCT_FORMAT = '<BBHBH'
  664.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  665.      
  666.         def decode(self, message):
  667.             assert message.command == SMB_COM_NT_CREATE_ANDX
  668.      
  669.             if not message.status.hasError:
  670.                 if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
  671.                     raise ProtocolError('Not enough data to decode SMB_COM_NT_CREATE_ANDX parameters', message.raw_data, message)
  672.      
  673.                 _, _, _, self.oplock_level, self.fid = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
  674.      
  675.      
  676.     class ComTransactionRequest(Payload):
  677.         """
  678.        References:
  679.        ===========
  680.        - [MS-CIFS]: 2.2.4.33.1
  681.        """
  682.      
  683.         PAYLOAD_STRUCT_FORMAT = '<HHHHBBHIHHHHHH'
  684.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  685.      
  686.         def __init__(self, max_params_count, max_data_count, max_setup_count,
  687.                      total_params_count = 0, total_data_count = 0,
  688.                      params_bytes = '', data_bytes = '', setup_bytes = '',
  689.                      flags = 0, timeout = 0, name = "\\PIPE\\"):
  690.             self.total_params_count = total_params_count or len(params_bytes)
  691.             self.total_data_count = total_data_count or len(data_bytes)
  692.             self.max_params_count = max_params_count
  693.             self.max_data_count = max_data_count
  694.             self.max_setup_count = max_setup_count
  695.             self.flags = flags
  696.             self.timeout = timeout
  697.             self.params_bytes = params_bytes
  698.             self.data_bytes = data_bytes
  699.             self.setup_bytes = setup_bytes
  700.             self.name = name
  701.      
  702.         def initMessage(self, message):
  703.             Payload.initMessage(self, message)
  704.             message.command = SMB_COM_TRANSACTION
  705.      
  706.         def prepare(self, message):
  707.             name = (self.name + '\0').encode('UTF-16LE')
  708.             name_len = len(name)
  709.             setup_bytes_len = len(self.setup_bytes)
  710.             params_bytes_len = len(self.params_bytes)
  711.             data_bytes_len = len(self.data_bytes)
  712.      
  713.             padding0 = ''
  714.             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)
  715.             if offset % 2 != 0:
  716.                 padding0 = '\0'
  717.                 offset += 1
  718.      
  719.             offset += name_len  # For the name field
  720.             padding1 = ''
  721.             if offset % 4 != 0:
  722.                 padding1 = '\0'*(4-offset%4)
  723.                 offset += (4-offset%4)
  724.      
  725.             if params_bytes_len > 0:
  726.                 params_bytes_offset = offset
  727.                 offset += params_bytes_len
  728.             else:
  729.                 params_bytes_offset = 0
  730.      
  731.             padding2 = ''
  732.             if offset % 4 != 0:
  733.                 padding2 = '\0'*(4-offset%4)
  734.                 offset += (4-offset%4)
  735.      
  736.             if data_bytes_len > 0:
  737.                 data_bytes_offset = offset
  738.             else:
  739.                 data_bytes_offset = 0
  740.      
  741.             message.parameters_data = \
  742.                 struct.pack(self.PAYLOAD_STRUCT_FORMAT,
  743.                             self.total_params_count,
  744.                             self.total_data_count,
  745.                             self.max_params_count,
  746.                             self.max_data_count,
  747.                             self.max_setup_count,
  748.                             0x00,           # Reserved1. Must be 0x00
  749.                             self.flags,
  750.                             self.timeout,
  751.                             0x0000,         # Reserved2. Must be 0x0000
  752.                             params_bytes_len,
  753.                             params_bytes_offset,
  754.                             data_bytes_len,
  755.                             data_bytes_offset,
  756.                             int(setup_bytes_len / 2)) + \
  757.                 self.setup_bytes
  758.      
  759.             message.data = padding0 + name + padding1 + self.params_bytes + padding2 + self.data_bytes
  760.      
  761.      
  762.     class ComTransactionResponse(Payload):
  763.         """
  764.        Contains information about a SMB_COM_TRANSACTION response from the server
  765.    
  766.        After decoding, each instance contains the following attributes:
  767.        - total_params_count (integer)
  768.        - total_data_count (integer)
  769.        - setup_bytes (string)
  770.        - data_bytes (string)
  771.        - params_bytes (string)
  772.    
  773.        References:
  774.        ===========
  775.        - [MS-CIFS]: 2.2.4.33.2
  776.        """
  777.      
  778.         PAYLOAD_STRUCT_FORMAT = '<HHHHHHHHHH'
  779.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  780.      
  781.         def decode(self, message):
  782.             assert message.command == SMB_COM_TRANSACTION
  783.      
  784.             if not message.status.hasError:
  785.                 if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
  786.                     raise ProtocolError('Not enough data to decode SMB_COM_TRANSACTION parameters', message.raw_data, message)
  787.      
  788.                 self.total_params_count, self.total_data_count, _, \
  789.                 params_bytes_len, params_bytes_offset, params_bytes_displ, \
  790.                 data_bytes_len, data_bytes_offset, data_bytes_displ, \
  791.                 setup_count = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
  792.      
  793.                 if setup_count > 0:
  794.                     setup_bytes_len = setup_count * 2
  795.      
  796.                     if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE + setup_bytes_len:
  797.                         raise ProtocolError('Not enough data to decode SMB_COM_TRANSACTION parameters', message.raw_data, message)
  798.      
  799.                     self.setup_bytes = message.parameters_data[self.PAYLOAD_STRUCT_SIZE:self.PAYLOAD_STRUCT_SIZE+setup_bytes_len]
  800.                 else:
  801.                     self.setup_bytes = ''
  802.      
  803.                 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)
  804.      
  805.                 if params_bytes_len > 0:
  806.                     self.params_bytes = message.data[params_bytes_offset-offset:params_bytes_offset-offset+params_bytes_len]
  807.                 else:
  808.                     self.params_bytes = ''
  809.      
  810.                 if data_bytes_len > 0:
  811.                     self.data_bytes = message.data[data_bytes_offset-offset:data_bytes_offset-offset+data_bytes_len]
  812.                 else:
  813.                     self.data_bytes = ''
  814.      
  815.      
  816.     class ComTransaction2Request(Payload):
  817.         """
  818.        References:
  819.        ===========
  820.        - [MS-CIFS]: 2.2.4.46.1
  821.        """
  822.      
  823.         PAYLOAD_STRUCT_FORMAT = 'HHHHBBHIHHHHHH'
  824.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  825.      
  826.         def __init__(self, max_params_count, max_data_count, max_setup_count,
  827.                      total_params_count = 0, total_data_count = 0,
  828.                      params_bytes = '', data_bytes = '', setup_bytes = '',
  829.                      flags = 0, timeout = 0):
  830.             self.total_params_count = total_params_count or len(params_bytes)
  831.             self.total_data_count = total_data_count or len(data_bytes)
  832.             self.max_params_count = max_params_count
  833.             self.max_data_count = max_data_count
  834.             self.max_setup_count = max_setup_count
  835.             self.flags = flags
  836.             self.timeout = timeout
  837.             self.params_bytes = params_bytes
  838.             self.data_bytes = data_bytes
  839.             self.setup_bytes = setup_bytes
  840.      
  841.         def initMessage(self, message):
  842.             Payload.initMessage(self, message)
  843.             message.command = SMB_COM_TRANSACTION2
  844.      
  845.         def prepare(self, message):
  846.             setup_bytes_len = len(self.setup_bytes)
  847.             params_bytes_len = len(self.params_bytes)
  848.             data_bytes_len = len(self.data_bytes)
  849.             name = '\0\0'
  850.      
  851.             padding0 = ''
  852.             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)
  853.             if offset % 2 != 0:
  854.                 padding0 = '\0'
  855.                 offset += 1
  856.      
  857.             offset += 2  # For the name field
  858.             padding1 = ''
  859.             if offset % 4 != 0:
  860.                 padding1 = '\0'*(4-offset%4)
  861.      
  862.             if params_bytes_len > 0:
  863.                 params_bytes_offset = offset
  864.                 offset += params_bytes_len
  865.             else:
  866.                 params_bytes_offset = 0
  867.      
  868.             padding2 = ''
  869.             if offset % 4 != 0:
  870.                 padding2 = '\0'*(4-offset%4)
  871.      
  872.             if data_bytes_len > 0:
  873.                 data_bytes_offset = offset
  874.             else:
  875.                 data_bytes_offset = 0
  876.      
  877.             message.parameters_data = \
  878.                 struct.pack(self.PAYLOAD_STRUCT_FORMAT,
  879.                             self.total_params_count,
  880.                             self.total_data_count,
  881.                             self.max_params_count,
  882.                             self.max_data_count,
  883.                             self.max_setup_count,
  884.                             0x00,           # Reserved1. Must be 0x00
  885.                             self.flags,
  886.                             self.timeout,
  887.                             0x0000,         # Reserved2. Must be 0x0000
  888.                             params_bytes_len,
  889.                             params_bytes_offset,
  890.                             data_bytes_len,
  891.                             data_bytes_offset,
  892.                             int(setup_bytes_len / 2)) + \
  893.                 self.setup_bytes
  894.      
  895.             message.data = padding0 + name + padding1 + self.params_bytes + padding2 + self.data_bytes
  896.      
  897.      
  898.     class ComTransaction2Response(Payload):
  899.         """
  900.        Contains information about a SMB_COM_TRANSACTION2 response from the server
  901.    
  902.        After decoding, each instance contains the following attributes:
  903.        - total_params_count (integer)
  904.        - total_data_count (integer)
  905.        - setup_bytes (string)
  906.        - data_bytes (string)
  907.        - params_bytes (string)
  908.    
  909.        References:
  910.        ===========
  911.        - [MS-CIFS]: 2.2.4.46.2
  912.        """
  913.      
  914.         PAYLOAD_STRUCT_FORMAT = '<HHHHHHHHHBB'
  915.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  916.      
  917.         def decode(self, message):
  918.             assert message.command == SMB_COM_TRANSACTION2
  919.      
  920.             if not message.status.hasError:
  921.                 if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
  922.                     raise ProtocolError('Not enough data to decode SMB_COM_TRANSACTION2 parameters', message.raw_data, message)
  923.      
  924.                 self.total_params_count, self.total_data_count, _, \
  925.                 params_bytes_len, params_bytes_offset, params_bytes_displ, \
  926.                 data_bytes_len, data_bytes_offset, data_bytes_displ, \
  927.                 setup_count, _ = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
  928.      
  929.                 if setup_count > 0:
  930.                     setup_bytes_len = setup_count * 2
  931.      
  932.                     if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE + setup_bytes_len:
  933.                         raise ProtocolError('Not enough data to decode SMB_COM_TRANSACTION parameters', message.raw_data, message)
  934.      
  935.                     self.setup_bytes = message.parameters_data[self.PAYLOAD_STRUCT_SIZE:self.PAYLOAD_STRUCT_SIZE+setup_bytes_len]
  936.                 else:
  937.                     self.setup_bytes = ''
  938.      
  939.                 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)
  940.      
  941.                 if params_bytes_len > 0:
  942.                     self.params_bytes = message.data[params_bytes_offset-offset:params_bytes_offset-offset+params_bytes_len]
  943.                 else:
  944.                     self.params_bytes = ''
  945.      
  946.                 if data_bytes_len > 0:
  947.                     self.data_bytes = message.data[data_bytes_offset-offset:data_bytes_offset-offset+data_bytes_len]
  948.                 else:
  949.                     self.data_bytes = ''
  950.      
  951.      
  952.     class ComCloseRequest(Payload):
  953.         """
  954.        References:
  955.        ===========
  956.        - [MS-CIFS]: 2.2.4.5.1
  957.        """
  958.      
  959.         PAYLOAD_STRUCT_FORMAT = '<HI'
  960.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  961.      
  962.         def __init__(self, fid, last_modified_time = 0xFFFFFFFF):
  963.             self.fid = fid
  964.             self.last_modified_time = last_modified_time
  965.      
  966.         def initMessage(self, message):
  967.             Payload.initMessage(self, message)
  968.             message.command = SMB_COM_CLOSE
  969.      
  970.         def prepare(self, message):
  971.             message.parameters_data = struct.pack(self.PAYLOAD_STRUCT_FORMAT, self.fid, self.last_modified_time)
  972.             message.data = ''
  973.      
  974.      
  975.     class ComOpenAndxRequest(Payload):
  976.         """
  977.        References:
  978.        ===========
  979.        - [MS-CIFS]: 2.2.4.41.1
  980.        """
  981.      
  982.         PAYLOAD_STRUCT_FORMAT = '<HHHHIHIII'
  983.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  984.      
  985.         def __init__(self, filename, access_mode, open_mode, flags = 0x0000, search_attributes = 0, file_attributes = 0, create_time = 0, timeout = 0):
  986.             """
  987.            @param create_time: Epoch time value to indicate the time of creation for this file. If zero, we will automatically assign the current time
  988.            @type create_time: int
  989.            @param timeout: Number of milliseconds to wait for blocked open request before failing
  990.            @type timeout: int
  991.            """
  992.             self.filename = filename
  993.             self.access_mode = access_mode
  994.             self.open_mode = open_mode
  995.             self.flags = flags
  996.             self.search_attributes = search_attributes
  997.             self.file_attributes = file_attributes
  998.             self.create_time = create_time or int(time.time())
  999.             self.timeout = timeout
  1000.      
  1001.         def initMessage(self, message):
  1002.             Payload.initMessage(self, message)
  1003.             message.command = SMB_COM_OPEN_ANDX
  1004.      
  1005.         def prepare(self, message):
  1006.             message.parameters_data = \
  1007.                 self.DEFAULT_ANDX_PARAM_HEADER + \
  1008.                 struct.pack(self.PAYLOAD_STRUCT_FORMAT,
  1009.                             self.flags,
  1010.                             self.access_mode,
  1011.                             self.search_attributes,
  1012.                             self.file_attributes,
  1013.                             self.create_time,
  1014.                             self.open_mode,
  1015.                             0,  # AllocationSize
  1016.                             0,  # Timeout (in milli-secs)
  1017.                             0)  # Reserved
  1018.      
  1019.             message.data = '\0' + self.filename.encode('UTF-16LE') + '\0\0'
  1020.      
  1021.      
  1022.     class ComOpenAndxResponse(Payload):
  1023.         """
  1024.        Contains information about a SMB_COM_OPEN_ANDX response from the server
  1025.    
  1026.        After decoding, each instance will contain the following attributes:
  1027.        - fid (integer)
  1028.        - file_attributes (integer)
  1029.        - last_write_time (long)
  1030.        - access_rights (integer)
  1031.        - resource_type (integer)
  1032.        - open_results (integer)
  1033.    
  1034.        References:
  1035.        ===========
  1036.        - [MS-CIFS]: 2.2.4.41.2
  1037.        - [MS-SMB]: 2.2.4.1.2
  1038.        """
  1039.      
  1040.         PAYLOAD_STRUCT_FORMAT = '<BBHHHIIHHHHHHH'
  1041.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  1042.      
  1043.         def decode(self, message):
  1044.             assert message.command == SMB_COM_OPEN_ANDX
  1045.      
  1046.             if not message.status.hasError:
  1047.                 if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
  1048.                     raise ProtocolError('Not enough data to decode SMB_COM_OPEN_ANDX parameters', message.raw_data, message)
  1049.      
  1050.                 _, _, _, self.fid, self.file_attributes, self.last_write_time, _, \
  1051.                 self.access_rights, self.resource_type, _, self.open_results, _, _, _ = struct.unpack(self.PAYLOAD_STRUCT_FORMAT,
  1052.                                                                                                       message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
  1053.      
  1054.      
  1055.     class ComWriteAndxRequest(Payload):
  1056.         """
  1057.        References:
  1058.        ===========
  1059.        - [MS-CIFS]: 2.2.4.43.1
  1060.        - [MS-SMB]: 2.2.4.3.1
  1061.        """
  1062.      
  1063.         PAYLOAD_STRUCT_FORMAT = '<HIIHHHHHI'
  1064.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  1065.      
  1066.         def __init__(self, fid, data_bytes, offset, write_mode = 0, timeout = 0):
  1067.             """
  1068.            @param timeout: Number of milliseconds to wait for blocked write request before failing. Must be zero for writing to regular file
  1069.            @type timeout: int
  1070.            """
  1071.             self.fid = fid
  1072.             self.offset = offset
  1073.             self.data_bytes = data_bytes
  1074.             self.timeout = timeout
  1075.             self.write_mode = write_mode
  1076.      
  1077.         def initMessage(self, message):
  1078.             Payload.initMessage(self, message)
  1079.             message.command = SMB_COM_WRITE_ANDX
  1080.      
  1081.         def prepare(self, message):
  1082.             # constant 1 is to account for the pad byte in the message.data
  1083.             # constant 2 is for the ByteCount field in the SMB header (i.e. field which indicates number of data bytes after the SMB parameters)
  1084.             data_offset = message.HEADER_STRUCT_SIZE + self.DEFAULT_ANDX_PARAM_SIZE + self.PAYLOAD_STRUCT_SIZE + 1 + 2
  1085.             data_len = len(self.data_bytes)
  1086.      
  1087.             message.parameters_data = \
  1088.                 self.DEFAULT_ANDX_PARAM_HEADER + \
  1089.                 struct.pack(self.PAYLOAD_STRUCT_FORMAT,
  1090.                             self.fid,
  1091.                             self.offset & 0xFFFFFFFF,
  1092.                             self.timeout,
  1093.                             self.write_mode,
  1094.                             data_len,   # Remaining
  1095.                             0x0000,     # Reserved
  1096.                             len(self.data_bytes),  # DataLength
  1097.                             data_offset,           # DataOffset
  1098.                             self.offset >> 32)     # OffsetHigh field defined in [MS-SMB]: 2.2.4.3.1
  1099.      
  1100.             message.data = '\0' + self.data_bytes
  1101.      
  1102.      
  1103.     class ComWriteAndxResponse(Payload):
  1104.         """
  1105.        References:
  1106.        ===========
  1107.        - [MS-CIFS]: 2.2.4.43.2
  1108.        - [MS-SMB]: 2.2.4.3.2
  1109.        """
  1110.      
  1111.         PAYLOAD_STRUCT_FORMAT = '<BBHHHHH'  # We follow the SMB_COM_WRITEX_ANDX server extensions in [MS-SMB]: 2.2.4.3.2
  1112.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  1113.      
  1114.         def decode(self, message):
  1115.             assert message.command == SMB_COM_WRITE_ANDX
  1116.      
  1117.             if not message.status.hasError:
  1118.                 if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
  1119.                     raise ProtocolError('Not enough data to decode SMB_COM_WRITE_ANDX parameters', message.raw_data, message)
  1120.      
  1121.                 _, _, _, count, self.available, high_count, _ = struct.unpack(self.PAYLOAD_STRUCT_FORMAT, message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
  1122.                 self.count = (count & 0xFFFF) | (high_count << 16)
  1123.      
  1124.      
  1125.     class ComReadAndxRequest(Payload):
  1126.         """
  1127.        References:
  1128.        ===========
  1129.        - [MS-CIFS]: 2.2.4.42.1
  1130.        - [MS-SMB]: 2.2.4.2.1
  1131.        """
  1132.      
  1133.         PAYLOAD_STRUCT_FORMAT = '<HIHHIHI'
  1134.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  1135.      
  1136.         def __init__(self, fid, offset, max_return_bytes_count, min_return_bytes_count, timeout = 0, remaining = 0):
  1137.             """
  1138.            @param timeout: If reading from a regular file, this parameter must be 0.
  1139.            @type timeout: int
  1140.            """
  1141.             self.fid = fid
  1142.             self.remaining = remaining
  1143.             self.max_return_bytes_count = max_return_bytes_count
  1144.             self.min_return_bytes_count = min_return_bytes_count
  1145.             self.offset = offset
  1146.             self.timeout = timeout
  1147.      
  1148.         def initMessage(self, message):
  1149.             Payload.initMessage(self, message)
  1150.             message.command = SMB_COM_READ_ANDX
  1151.      
  1152.         def prepare(self, message):
  1153.             message.parameters_data = \
  1154.                 self.DEFAULT_ANDX_PARAM_HEADER + \
  1155.                 struct.pack(self.PAYLOAD_STRUCT_FORMAT,
  1156.                             self.fid,
  1157.                             self.offset & 0xFFFFFFFF,
  1158.                             self.max_return_bytes_count,
  1159.                             self.min_return_bytes_count,
  1160.                             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
  1161.                             self.remaining, # In [MS-CIFS]: 2.2.4.42.1, this field must be set to 0x0000
  1162.                             self.offset >> 32)
  1163.      
  1164.             message.data = ''
  1165.      
  1166.      
  1167.     class ComReadAndxResponse(Payload):
  1168.         """
  1169.        References:
  1170.        ===========
  1171.        - [MS-CIFS]: 2.2.4.42.2
  1172.        - [MS-SMB]: 2.2.4.2.2
  1173.        """
  1174.      
  1175.         PAYLOAD_STRUCT_FORMAT = '<BBHHHHHHHHHHH'
  1176.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  1177.      
  1178.         def decode(self, message):
  1179.             assert message.command == SMB_COM_READ_ANDX
  1180.      
  1181.             if not message.status.hasError:
  1182.                 if len(message.parameters_data) < self.PAYLOAD_STRUCT_SIZE:
  1183.                     raise ProtocolError('Not enough data to decode SMB_COM_READ_ANDX parameters', message.raw_data, message)
  1184.      
  1185.                 _, _, _, _, _, _, self.data_length, data_offset, _, _, _, _, _ = struct.unpack(self.PAYLOAD_STRUCT_FORMAT,
  1186.                                                                                                message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
  1187.      
  1188.                 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)
  1189.                 self.data = message.data[offset:offset+self.data_length]
  1190.                 assert len(self.data) == self.data_length
  1191.      
  1192.      
  1193.     class ComDeleteRequest(Payload):
  1194.         """
  1195.        References:
  1196.        ===========
  1197.        - [MS-CIFS]: 2.2.4.7.1
  1198.        """
  1199.      
  1200.         def __init__(self, filename_pattern, search_attributes = 0):
  1201.             self.filename_pattern = filename_pattern
  1202.             self.search_attributes = search_attributes
  1203.      
  1204.         def initMessage(self, message):
  1205.             Payload.initMessage(self, message)
  1206.             message.command = SMB_COM_DELETE
  1207.      
  1208.         def prepare(self, message):
  1209.             message.parameters_data = struct.pack('<H', self.search_attributes)
  1210.             message.data = '\x04' + self.filename_pattern.encode('UTF-16LE') + '\0\0'
  1211.      
  1212.      
  1213.     class ComCreateDirectoryRequest(Payload):
  1214.         """
  1215.        Although this command has been marked deprecated in [MS-CIFS], we continue to use it for its simplicity
  1216.        as compared to its replacement TRANS2_CREATE_DIRECTORY sub-command [MS-CIFS]: 2.2.6.14
  1217.    
  1218.        References:
  1219.        ===========
  1220.        - [MS-CIFS]: 2.2.4.1.1
  1221.        """
  1222.      
  1223.         def __init__(self, path):
  1224.             self.path = path
  1225.      
  1226.         def initMessage(self, message):
  1227.             Payload.initMessage(self, message)
  1228.             message.command = SMB_COM_CREATE_DIRECTORY
  1229.      
  1230.         def prepare(self, message):
  1231.             message.parameters_data = ''
  1232.             message.data = '\x04' + self.path.encode('UTF-16LE') + '\0\0'
  1233.      
  1234.      
  1235.     class ComDeleteDirectoryRequest(Payload):
  1236.         """
  1237.        References:
  1238.        ===========
  1239.        - [MS-CIFS]: 2.2.4.2.1
  1240.        """
  1241.      
  1242.         def __init__(self, path):
  1243.             self.path = path
  1244.      
  1245.         def initMessage(self, message):
  1246.             Payload.initMessage(self, message)
  1247.             message.command = SMB_COM_DELETE_DIRECTORY
  1248.      
  1249.         def prepare(self, message):
  1250.             message.parameters_data = ''
  1251.             message.data = '\x04' + self.path.encode('UTF-16LE') + '\0\0'
  1252.      
  1253.      
  1254.     class ComRenameRequest(Payload):
  1255.         """
  1256.        References:
  1257.        ===========
  1258.        - [MS-CIFS]: 2.2.4.8.1
  1259.        """
  1260.      
  1261.         def __init__(self, old_path, new_path, search_attributes = 0):
  1262.             self.old_path = old_path
  1263.             self.new_path = new_path
  1264.             self.search_attributes = search_attributes
  1265.      
  1266.         def initMessage(self, message):
  1267.             Payload.initMessage(self, message)
  1268.             message.command = SMB_COM_RENAME
  1269.      
  1270.         def prepare(self, message):
  1271.             message.parameters_data = struct.pack('<H', self.search_attributes)
  1272.             message.data = '\x04' + self.old_path.encode('UTF-16LE') + '\x00\x00\x04\x00' + self.new_path.encode('UTF-16LE') + '\x00\x00'
  1273.      
  1274.      
  1275.     class ComEchoRequest(Payload):
  1276.         """
  1277.        References:
  1278.        ===========
  1279.        - [MS-CIFS]: 2.2.4.39.1
  1280.        """
  1281.      
  1282.         def __init__(self, echo_data = '', echo_count = 1):
  1283.             self.echo_count = echo_count
  1284.             self.echo_data = echo_data
  1285.      
  1286.         def initMessage(self, message):
  1287.             Payload.initMessage(self, message)
  1288.             message.command = SMB_COM_ECHO
  1289.             message.tid = 0xFFFF
  1290.      
  1291.         def prepare(self, message):
  1292.             message.parameters_data = struct.pack('<H', self.echo_count)
  1293.             message.data = self.echo_data
  1294.      
  1295.      
  1296.     class ComEchoResponse(Payload):
  1297.         """
  1298.        References:
  1299.        ===========
  1300.        - [MS-CIFS]: 2.2.4.39.2
  1301.        """
  1302.      
  1303.         def decode(self, message):
  1304.             self.sequence_number = struct.unpack('<H', message.parameters_data[:2])[0]
  1305.             self.data = message.data
  1306.      
  1307.      
  1308.     class ComNTTransactRequest(Payload):
  1309.         """
  1310.        References:
  1311.        ===========
  1312.        - [MS-CIFS]: 2.2.4.62.1
  1313.        """
  1314.         PAYLOAD_STRUCT_FORMAT = '<BHIIIIIIIIBH'
  1315.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  1316.      
  1317.         def __init__(self, function, max_params_count, max_data_count, max_setup_count,
  1318.                      total_params_count = 0, total_data_count = 0,
  1319.                      params_bytes = '', setup_bytes = '', data_bytes = ''):
  1320.             self.function = function
  1321.             self.total_params_count = total_params_count or len(params_bytes)
  1322.             self.total_data_count = total_data_count or len(data_bytes)
  1323.             self.max_params_count = max_params_count
  1324.             self.max_data_count = max_data_count
  1325.             self.max_setup_count = max_setup_count
  1326.             self.params_bytes = params_bytes
  1327.             self.setup_bytes = setup_bytes
  1328.             self.data_bytes = data_bytes
  1329.      
  1330.         def initMessage(self, message):
  1331.             Payload.initMessage(self, message)
  1332.             message.command = SMB_COM_NT_TRANSACT
  1333.      
  1334.         def prepare(self, message):
  1335.             setup_bytes_len = len(self.setup_bytes)
  1336.             params_bytes_len = len(self.params_bytes)
  1337.             data_bytes_len = len(self.data_bytes)
  1338.      
  1339.             padding0 = ''
  1340.             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)
  1341.             if offset % 4 != 0:
  1342.                 padding0 = '\0'*(4-offset%4)
  1343.                 offset += (4-offset%4)
  1344.      
  1345.             if params_bytes_len > 0:
  1346.                 params_bytes_offset = offset
  1347.             else:
  1348.                 params_bytes_offset = 0
  1349.      
  1350.             offset += params_bytes_len
  1351.             padding1 = ''
  1352.             if offset % 4 != 0:
  1353.                 padding1 = '\0'*(4-offset%4)
  1354.                 offset += (4-offset%4)
  1355.      
  1356.             if data_bytes_len > 0:
  1357.                 data_bytes_offset = offset
  1358.             else:
  1359.                 data_bytes_offset = 0
  1360.      
  1361.             message.parameters_data = \
  1362.                 struct.pack(self.PAYLOAD_STRUCT_FORMAT,
  1363.                             self.max_setup_count,
  1364.                             0x00,           # Reserved1. Must be 0x00
  1365.                             self.total_params_count,
  1366.                             self.total_data_count,
  1367.                             self.max_params_count,
  1368.                             self.max_data_count,
  1369.                             params_bytes_len,
  1370.                             params_bytes_offset,
  1371.                             data_bytes_len,
  1372.                             data_bytes_offset,
  1373.                             int(setup_bytes_len / 2),
  1374.                             self.function) + \
  1375.                 self.setup_bytes
  1376.      
  1377.             message.data = padding0 + self.params_bytes + padding1 + self.data_bytes
  1378.      
  1379.      
  1380.     class ComNTTransactResponse(Payload):
  1381.         """
  1382.        Contains information about a SMB_COM_NT_TRANSACT response from the server
  1383.    
  1384.        After decoding, each instance contains the following attributes:
  1385.        - total_params_count (integer)
  1386.        - total_data_count (integer)
  1387.        - setup_bytes (string)
  1388.        - data_bytes (string)
  1389.        - params_bytes (string)
  1390.    
  1391.        References:
  1392.        ===========
  1393.        - [MS-CIFS]: 2.2.4.62.2
  1394.        """
  1395.         PAYLOAD_STRUCT_FORMAT = '<3sIIIIIIIIBH'
  1396.         PAYLOAD_STRUCT_SIZE = struct.calcsize(PAYLOAD_STRUCT_FORMAT)
  1397.      
  1398.         def decode(self, message):
  1399.             assert message.command == SMB_COM_NT_TRANSACT
  1400.      
  1401.             if not message.status.hasError:
  1402.                 _, self.total_params_count, self.total_data_count, \
  1403.                 params_count, params_offset, params_displ, \
  1404.                 data_count, data_offset, data_displ, setup_count = struct.unpack(self.PAYLOAD_STRUCT_FORMAT,
  1405.                                                                                  message.parameters_data[:self.PAYLOAD_STRUCT_SIZE])
  1406.      
  1407.                 self.setup_bytes = message.parameters_data[self.PAYLOAD_STRUCT_SIZE:self.PAYLOAD_STRUCT_SIZE+setup_count*2]
  1408.      
  1409.                 if params_count > 0:
  1410.                     params_offset -= message.HEADER_STRUCT_SIZE + self.PAYLOAD_STRUCT_SIZE + setup_count*2 + 2
  1411.                     self.params_bytes = message.data[params_offset:params_offset+params_count]
  1412.                 else:
  1413.                     self.params_bytes = ''
  1414.      
  1415.                 if data_count > 0:
  1416.                     data_offset -= message.HEADER_STRUCT_SIZE + self.PAYLOAD_STRUCT_SIZE + setup_count*2 + 2
  1417.                     self.data_bytes = message.data[data_offset:data_offset+data_count]
  1418.                 else:
  1419.                     self.data_bytes = ''
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement