123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629 |
- # Copyright (c) 2003-2016 CORE Security Technologies
- #
- # This software is provided under under a slightly modified version
- # of the Apache Software License. See the accompanying LICENSE file
- # for more information.
- #
- # Author: Alberto Solino (@agsolino)
- #
- # Description:
- # [MS-SMB2] Protocol Implementation (SMB2 and SMB3)
- # As you might see in the code, it's implemented strictly following
- # the structures defined in the protocol specification. This may
- # not be the most efficient way (e.g. self._Connection is the
- # same to self._Session in the context of this library ) but
- # it certainly helps following the document way easier.
- #
- # ToDo:
- # [X] Implement SMB2_CHANGE_NOTIFY
- # [X] Implement SMB2_QUERY_INFO
- # [X] Implement SMB2_SET_INFO
- # [ ] Implement SMB2_OPLOCK_BREAK
- # [X] Implement SMB3 signing
- # [ ] Implement SMB3 encryption
- # [ ] Add more backward compatible commands from the smb.py code
- # [ ] Fix up all the 'ToDo' comments inside the code
- #
- import socket
- import ntpath
- import random
- import string
- import struct
- from binascii import a2b_hex
- from contextlib import contextmanager
- from impacket import nmb, ntlm, uuid, crypto, LOG
- from impacket.smb3structs import *
- from impacket.nt_errors import STATUS_SUCCESS, STATUS_MORE_PROCESSING_REQUIRED, STATUS_INVALID_PARAMETER, \
- STATUS_NO_MORE_FILES, STATUS_PENDING, STATUS_NOT_IMPLEMENTED, ERROR_MESSAGES
- from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp
- # For signing
- import hashlib, hmac, copy
- # Structs to be used
- TREE_CONNECT = {
- 'ShareName' : '',
- 'TreeConnectId' : 0,
- 'Session' : 0,
- 'IsDfsShare' : False,
- # If the client implements the SMB 3.0 dialect,
- # the client MUST also implement the following
- 'IsCAShare' : False,
- 'EncryptData' : False,
- 'IsScaleoutShare' : False,
- # Outside the protocol
- 'NumberOfUses' : 0,
- }
- FILE = {
- 'OpenTable' : [],
- 'LeaseKey' : '',
- 'LeaseState' : 0,
- 'LeaseEpoch' : 0,
- }
- OPEN = {
- 'FileID' : '',
- 'TreeConnect' : 0,
- 'Connection' : 0, # Not Used
- 'Oplocklevel' : 0,
- 'Durable' : False,
- 'FileName' : '',
- 'ResilientHandle' : False,
- 'LastDisconnectTime' : 0,
- 'ResilientTimeout' : 0,
- 'OperationBuckets' : [],
- # If the client implements the SMB 3.0 dialect,
- # the client MUST implement the following
- 'CreateGuid' : '',
- 'IsPersistent' : False,
- 'DesiredAccess' : '',
- 'ShareMode' : 0,
- 'CreateOption' : '',
- 'FileAttributes' : '',
- 'CreateDisposition' : '',
- }
- REQUEST = {
- 'CancelID' : '',
- 'Message' : '',
- 'Timestamp' : 0,
- }
- CHANNEL = {
- 'SigningKey' : '',
- 'Connection' : 0,
- }
- class SessionError(Exception):
- def __init__( self, error = 0, packet=0):
- Exception.__init__(self)
- self.error = error
- self.packet = packet
-
- def get_error_code( self ):
- return self.error
- def get_error_packet( self ):
- return self.packet
- def __str__( self ):
- return 'SMB SessionError: %s(%s)' % (ERROR_MESSAGES[self.error])
- class SMB3:
- def __init__(self, remote_name, remote_host, my_name = None, host_type = nmb.TYPE_SERVER, sess_port = 445, timeout=60, UDP = 0, preferredDialect = None, session = None):
- # [MS-SMB2] Section 3
- self.RequireMessageSigning = False #
- self.ConnectionTable = {}
- self.GlobalFileTable = {}
- self.ClientGuid = ''.join([random.choice(string.letters) for i in range(16)])
- # Only for SMB 3.0
- self.EncryptionAlgorithmList = ['AES-CCM']
- self.MaxDialect = []
- self.RequireSecureNegotiate = False
- # Per Transport Connection Data
- self._Connection = {
- # Indexed by SessionID
- #'SessionTable' : {},
- # Indexed by MessageID
- 'OutstandingRequests' : {},
- 'OutstandingResponses' : {}, #
- 'SequenceWindow' : 0, #
- 'GSSNegotiateToken' : '', #
- 'MaxTransactSize' : 0, #
- 'MaxReadSize' : 0, #
- 'MaxWriteSize' : 0, #
- 'ServerGuid' : '', #
- 'RequireSigning' : False, #
- 'ServerName' : '', #
- # If the client implements the SMB 2.1 or SMB 3.0 dialects, it MUST
- # also implement the following
- 'Dialect' : '', #
- 'SupportsFileLeasing' : False, #
- 'SupportsMultiCredit' : False, #
- # If the client implements the SMB 3.0 dialect,
- # it MUST also implement the following
- 'SupportsDirectoryLeasing' : False, #
- 'SupportsMultiChannel' : False, #
- 'SupportsPersistentHandles': False, #
- 'SupportsEncryption' : False, #
- 'ClientCapabilities' : 0,
- 'ServerCapabilities' : 0, #
- 'ClientSecurityMode' : 0, #
- 'ServerSecurityMode' : 0, #
- # Outside the protocol
- 'ServerIP' : '', #
- }
-
- self._Session = {
- 'SessionID' : 0, #
- 'TreeConnectTable' : {}, #
- 'SessionKey' : '', #
- 'SigningRequired' : False, #
- 'Connection' : 0, #
- 'UserCredentials' : '', #
- 'OpenTable' : {}, #
- # If the client implements the SMB 3.0 dialect,
- # it MUST also implement the following
- 'ChannelList' : [],
- 'ChannelSequence' : 0,
- #'EncryptData' : False,
- 'EncryptData' : True,
- 'EncryptionKey' : '',
- 'DecryptionKey' : '',
- 'SigningKey' : '',
- 'ApplicationKey' : '',
- # Outside the protocol
- 'SessionFlags' : 0, #
- 'ServerName' : '', #
- 'ServerDomain' : '', #
- 'ServerDNSDomainName' : '', #
- 'ServerOS' : '', #
- 'SigningActivated' : False, #
- }
- self.SMB_PACKET = SMB2Packet
-
- self._timeout = timeout
- self._Connection['ServerIP'] = remote_host
- self._NetBIOSSession = None
- self.__userName = ''
- self.__password = ''
- self.__domain = ''
- self.__lmhash = ''
- self.__nthash = ''
- self.__kdc = ''
- self.__aesKey = ''
- self.__TGT = None
- self.__TGS = None
- if sess_port == 445 and remote_name == '*SMBSERVER':
- self._Connection['ServerName'] = remote_host
- else:
- self._Connection['ServerName'] = remote_name
- if session is None:
- if not my_name:
- my_name = socket.gethostname()
- i = string.find(my_name, '.')
- if i > -1:
- my_name = my_name[:i]
- if UDP:
- self._NetBIOSSession = nmb.NetBIOSUDPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout)
- else:
- self._NetBIOSSession = nmb.NetBIOSTCPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout)
- self.negotiateSession(preferredDialect)
- else:
- self._NetBIOSSession = session
- # We should increase the SequenceWindow since a packet was already received.
- self._Connection['SequenceWindow'] += 1
- # Let's negotiate again using the same connection
- self.negotiateSession(preferredDialect)
- def printStatus(self):
- print "CONNECTION"
- for i in self._Connection.items():
- print "%-40s : %s" % i
- print
- print "SESSION"
- for i in self._Session.items():
- print "%-40s : %s" % i
- def getServerName(self):
- return self._Session['ServerName']
- def getServerIP(self):
- return self._Connection['ServerIP']
- def getServerDomain(self):
- return self._Session['ServerDomain']
- def getServerDNSDomainName(self):
- return self._Session['ServerDNSDomainName']
- def getServerOS(self):
- return self._Session['ServerOS']
- def getServerOSMajor(self):
- return self._Session['ServerOSMajor']
- def getServerOSMinor(self):
- return self._Session['ServerOSMinor']
- def getServerOSBuild(self):
- return self._Session['ServerOSBuild']
- def isGuestSession(self):
- return self._Session['SessionFlags'] & SMB2_SESSION_FLAG_IS_GUEST
- def setTimeout(self, timeout):
- self._timeout = timeout
- @contextmanager
- def useTimeout(self, timeout):
- prev_timeout = self.getTimeout(timeout)
- try:
- yield
- finally:
- self.setTimeout(prev_timeout)
- def getDialect(self):
- return self._Connection['Dialect']
- def signSMB(self, packet):
- packet['Signature'] = '\x00'*16
- if self._Connection['Dialect'] == SMB2_DIALECT_21 or self._Connection['Dialect'] == SMB2_DIALECT_002:
- if len(self._Session['SessionKey']) > 0:
- signature = hmac.new(self._Session['SessionKey'], str(packet), hashlib.sha256).digest()
- packet['Signature'] = signature[:16]
- else:
- if len(self._Session['SessionKey']) > 0:
- p = str(packet)
- signature = crypto.AES_CMAC(self._Session['SigningKey'], p, len(p))
- packet['Signature'] = signature
-
- def sendSMB(self, packet):
- # The idea here is to receive multiple/single commands and create a compound request, and send it
- # Should return the MessageID for later retrieval. Implement compounded related requests.
- # If Connection.Dialect is equal to "3.000" and if Connection.SupportsMultiChannel or
- # Connection.SupportsPersistentHandles is TRUE, the client MUST set ChannelSequence in the
- # SMB2 header to Session.ChannelSequence
- # Check this is not a CANCEL request. If so, don't consume sequece numbers
- if packet['Command'] is not SMB2_CANCEL:
- packet['MessageID'] = self._Connection['SequenceWindow']
- self._Connection['SequenceWindow'] += 1
- packet['SessionID'] = self._Session['SessionID']
- # Default the credit charge to 1 unless set by the caller
- if packet.fields.has_key('CreditCharge') is False:
- packet['CreditCharge'] = 1
- # Standard credit request after negotiating protocol
- if self._Connection['SequenceWindow'] > 3:
- packet['CreditRequestResponse'] = 127
- messageId = packet['MessageID']
- if self._Session['SigningActivated'] is True and self._Connection['SequenceWindow'] > 2:
- if packet['TreeID'] > 0 and self._Session['TreeConnectTable'].has_key(packet['TreeID']) is True:
- if self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is False:
- packet['Flags'] = SMB2_FLAGS_SIGNED
- self.signSMB(packet)
- elif packet['TreeID'] == 0:
- packet['Flags'] = SMB2_FLAGS_SIGNED
- self.signSMB(packet)
- if (self._Session['SessionFlags'] & SMB2_SESSION_FLAG_ENCRYPT_DATA) or ( packet['TreeID'] != 0 and self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is True):
- plainText = str(packet)
- transformHeader = SMB2_TRANSFORM_HEADER()
- transformHeader['Nonce'] = ''.join([random.choice(string.letters) for i in range(11)])
- transformHeader['OriginalMessageSize'] = len(plainText)
- transformHeader['EncryptionAlgorithm'] = SMB2_ENCRYPTION_AES128_CCM
- transformHeader['SessionID'] = self._Session['SessionID']
- from Crypto.Cipher import AES
- try:
- AES.MODE_CCM
- except:
- LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
- raise
- cipher = AES.new(self._Session['EncryptionKey'], AES.MODE_CCM, transformHeader['Nonce'])
- cipher.update(str(transformHeader)[20:])
- cipherText = cipher.encrypt(plainText)
- transformHeader['Signature'] = cipher.digest()
- packet = str(transformHeader) + cipherText
- self._NetBIOSSession.send_packet(str(packet))
- return messageId
- def recvSMB(self, packetID = None):
- # First, verify we don't have the packet already
- if self._Connection['OutstandingResponses'].has_key(packetID):
- return self._Connection['OutstandingResponses'].pop(packetID)
- data = self._NetBIOSSession.recv_packet(self._timeout)
- if data.get_trailer().startswith('\xfdSMB'):
- # Packet is encrypted
- transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())
- from Crypto.Cipher import AES
- try:
- AES.MODE_CCM
- except:
- LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
- raise
- cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11])
- cipher.update(str(transformHeader)[20:])
- plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):])
- #cipher.verify(transformHeader['Signature'])
- packet = SMB2Packet(plainText)
- else:
- # In all SMB dialects for a response this field is interpreted as the Status field.
- # This field can be set to any value. For a list of valid status codes,
- # see [MS-ERREF] section 2.3.
- packet = SMB2Packet(data.get_trailer())
- # Loop while we receive pending requests
- if packet['Status'] == STATUS_PENDING:
- status = STATUS_PENDING
- while status == STATUS_PENDING:
- data = self._NetBIOSSession.recv_packet(self._timeout)
- if data.get_trailer().startswith('\xfeSMB'):
- packet = SMB2Packet(data.get_trailer())
- else:
- # Packet is encrypted
- transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())
- from Crypto.Cipher import AES
- try:
- AES.MODE_CCM
- except:
- LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
- raise
- cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11])
- cipher.update(str(transformHeader)[20:])
- plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):])
- #cipher.verify(transformHeader['Signature'])
- packet = SMB2Packet(plainText)
- status = packet['Status']
- if packet['MessageID'] == packetID or packetID is None:
- # if self._Session['SigningRequired'] is True:
- # self.signSMB(packet)
- # Let's update the sequenceWindow based on the CreditsCharged
- self._Connection['SequenceWindow'] += (packet['CreditCharge'] - 1)
- return packet
- else:
- self._Connection['OutstandingResponses'][packet['MessageID']] = packet
- return self.recvSMB(packetID)
- def negotiateSession(self, preferredDialect = None):
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_NEGOTIATE
- negSession = SMB2Negotiate()
- negSession['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
- if self.RequireMessageSigning is True:
- negSession['SecurityMode'] |= SMB2_NEGOTIATE_SIGNING_REQUIRED
- negSession['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION
- negSession['ClientGuid'] = self.ClientGuid
- if preferredDialect is not None:
- negSession['Dialects'] = [preferredDialect]
- else:
- negSession['Dialects'] = [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]
- negSession['DialectCount'] = len(negSession['Dialects'])
- packet['Data'] = negSession
- # Storing this data for later use
- self._Connection['ClientSecurityMode'] = negSession['SecurityMode']
- self._Connection['Capabilities'] = negSession['Capabilities']
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- # ToDo this:
- # If the DialectRevision in the SMB2 NEGOTIATE Response is 0x02FF, the client MUST issue a new
- # SMB2 NEGOTIATE request as described in section 3.2.4.2.2.2 with the only exception
- # that the client MUST allocate sequence number 1 from Connection.SequenceWindow, and MUST set
- # MessageId field of the SMB2 header to 1. Otherwise, the client MUST proceed as follows.
- negResp = SMB2Negotiate_Response(ans['Data'])
- self._Connection['MaxTransactSize'] = min(0x100000,negResp['MaxTransactSize'])
- self._Connection['MaxReadSize'] = min(0x100000,negResp['MaxReadSize'])
- self._Connection['MaxWriteSize'] = min(0x100000,negResp['MaxWriteSize'])
- self._Connection['ServerGuid'] = negResp['ServerGuid']
- self._Connection['GSSNegotiateToken'] = negResp['Buffer']
- self._Connection['Dialect'] = negResp['DialectRevision']
- if (negResp['SecurityMode'] & SMB2_NEGOTIATE_SIGNING_REQUIRED) == SMB2_NEGOTIATE_SIGNING_REQUIRED:
- self._Connection['RequireSigning'] = True
- if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LEASING) == SMB2_GLOBAL_CAP_LEASING:
- self._Connection['SupportsFileLeasing'] = True
- if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LARGE_MTU) == SMB2_GLOBAL_CAP_LARGE_MTU:
- self._Connection['SupportsMultiCredit'] = True
- if self._Connection['Dialect'] == SMB2_DIALECT_30:
- # Switching to the right packet format
- self.SMB_PACKET = SMB3Packet
- if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) == SMB2_GLOBAL_CAP_DIRECTORY_LEASING:
- self._Connection['SupportsDirectoryLeasing'] = True
- if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_MULTI_CHANNEL) == SMB2_GLOBAL_CAP_MULTI_CHANNEL:
- self._Connection['SupportsMultiChannel'] = True
- if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == SMB2_GLOBAL_CAP_PERSISTENT_HANDLES:
- self._Connection['SupportsPersistentHandles'] = True
- if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_ENCRYPTION) == SMB2_GLOBAL_CAP_ENCRYPTION:
- self._Connection['SupportsEncryption'] = True
- self._Connection['ServerCapabilities'] = negResp['Capabilities']
- self._Connection['ServerSecurityMode'] = negResp['SecurityMode']
- def getCredentials(self):
- return (
- self.__userName,
- self.__password,
- self.__domain,
- self.__lmhash,
- self.__nthash,
- self.__aesKey,
- self.__TGT,
- self.__TGS)
- def kerberosLogin(self, user, password, domain = '', lmhash = '', nthash = '', aesKey='', kdcHost = '', TGT=None, TGS=None):
- # If TGT or TGS are specified, they are in the form of:
- # TGS['KDC_REP'] = the response from the server
- # TGS['cipher'] = the cipher used
- # TGS['sessionKey'] = the sessionKey
- # If we have hashes, normalize them
- if lmhash != '' or nthash != '':
- if len(lmhash) % 2: lmhash = '0%s' % lmhash
- if len(nthash) % 2: nthash = '0%s' % nthash
- try: # just in case they were converted already
- lmhash = a2b_hex(lmhash)
- nthash = a2b_hex(nthash)
- except:
- pass
- self.__userName = user
- self.__password = password
- self.__domain = domain
- self.__lmhash = lmhash
- self.__nthash = nthash
- self.__kdc = kdcHost
- self.__aesKey = aesKey
- self.__TGT = TGT
- self.__TGS = TGS
-
- sessionSetup = SMB2SessionSetup()
- if self.RequireMessageSigning is True:
- sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED
- else:
- sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
- sessionSetup['Flags'] = 0
- #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS
- # Importing down here so pyasn1 is not required if kerberos is not used.
- from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set
- from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
- from impacket.krb5 import constants
- from impacket.krb5.types import Principal, KerberosTime, Ticket
- from pyasn1.codec.der import decoder, encoder
- import datetime
- # First of all, we need to get a TGT for the user
- userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
- if TGT is None:
- if TGS is None:
- tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost)
- else:
- tgt = TGT['KDC_REP']
- cipher = TGT['cipher']
- sessionKey = TGT['sessionKey']
- # Save the ticket
- # If you want, for debugging purposes
- # from impacket.krb5.ccache import CCache
- # ccache = CCache()
- # try:
- # if TGS is None:
- # ccache.fromTGT(tgt, oldSessionKey, sessionKey)
- # else:
- # ccache.fromTGS(TGS['KDC_REP'], TGS['oldSessionKey'], TGS['sessionKey'] )
- # ccache.saveFile('/tmp/ticket.bin')
- # except Exception, e:
- # print e
- # pass
- # Now that we have the TGT, we should ask for a TGS for cifs
- if TGS is None:
- serverName = Principal('cifs/%s' % (self._Connection['ServerName']), type=constants.PrincipalNameType.NT_SRV_INST.value)
- tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey)
- else:
- tgs = TGS['KDC_REP']
- cipher = TGS['cipher']
- sessionKey = TGS['sessionKey']
- # Let's build a NegTokenInit with a Kerberos REQ_AP
- blob = SPNEGO_NegTokenInit()
- # Kerberos
- blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]
- # Let's extract the ticket from the TGS
- tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0]
- ticket = Ticket()
- ticket.from_asn1(tgs['ticket'])
-
- # Now let's build the AP_REQ
- apReq = AP_REQ()
- apReq['pvno'] = 5
- apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)
- opts = list()
- apReq['ap-options'] = constants.encodeFlags(opts)
- seq_set(apReq,'ticket', ticket.to_asn1)
- authenticator = Authenticator()
- authenticator['authenticator-vno'] = 5
- authenticator['crealm'] = domain
- seq_set(authenticator, 'cname', userName.components_to_asn1)
- now = datetime.datetime.utcnow()
- authenticator['cusec'] = now.microsecond
- authenticator['ctime'] = KerberosTime.to_asn1(now)
- encodedAuthenticator = encoder.encode(authenticator)
- # Key Usage 11
- # AP-REQ Authenticator (includes application authenticator
- # subkey), encrypted with the application session key
- # (Section 5.5.1)
- encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None)
- apReq['authenticator'] = None
- apReq['authenticator']['etype'] = cipher.enctype
- apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator
- blob['MechToken'] = encoder.encode(apReq)
- sessionSetup['SecurityBufferLength'] = len(blob)
- sessionSetup['Buffer'] = blob.getData()
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_SESSION_SETUP
- packet['Data'] = sessionSetup
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- self._Session['SessionID'] = ans['SessionID']
- self._Session['SigningRequired'] = self._Connection['RequireSigning']
- self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash)
- self._Session['Connection'] = self._NetBIOSSession.get_socket()
- self._Session['SessionKey'] = sessionKey.contents[:16]
- if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30:
- self._Session['SigningKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCMAC\x00", "SmbSign\x00", 128)
- # Calculate the key derivations for dialect 3.0
- if self._Session['SigningRequired'] is True:
- self._Session['SigningActivated'] = True
- if self._Connection['Dialect'] == SMB2_DIALECT_30:
- self._Session['ApplicationKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2APP\x00", "SmbRpc\x00", 128)
- self._Session['EncryptionKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCCM\x00", "ServerIn \x00", 128)
- self._Session['DecryptionKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCCM\x00", "ServerOut\x00", 128)
-
- return True
- else:
- # We clean the stuff we used in case we want to authenticate again
- # within the same connection
- self._Session['UserCredentials'] = ''
- self._Session['Connection'] = 0
- self._Session['SessionID'] = 0
- self._Session['SigningRequired'] = False
- self._Session['SigningKey'] = ''
- self._Session['SessionKey'] = ''
- self._Session['SigningActivated'] = False
- raise
- def login(self, user, password, domain = '', lmhash = '', nthash = ''):
- # If we have hashes, normalize them
- if lmhash != '' or nthash != '':
- if len(lmhash) % 2: lmhash = '0%s' % lmhash
- if len(nthash) % 2: nthash = '0%s' % nthash
- try: # just in case they were converted already
- lmhash = a2b_hex(lmhash)
- nthash = a2b_hex(nthash)
- except:
- pass
- self.__userName = user
- self.__password = password
- self.__domain = domain
- self.__lmhash = lmhash
- self.__nthash = nthash
- self.__aesKey = ''
- self.__TGT = None
- self.__TGS = None
-
- sessionSetup = SMB2SessionSetup()
- if self.RequireMessageSigning is True:
- sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED
- else:
- sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
- sessionSetup['Flags'] = 0
- #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS
- # Let's build a NegTokenInit with the NTLMSSP
- # TODO: In the future we should be able to choose different providers
- blob = SPNEGO_NegTokenInit()
- # NTLMSSP
- blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
- auth = ntlm.getNTLMSSPType1('','', self._Connection['RequireSigning'])
- blob['MechToken'] = str(auth)
- sessionSetup['SecurityBufferLength'] = len(blob)
- sessionSetup['Buffer'] = blob.getData()
- # ToDo:
- # If this authentication is for establishing an alternative channel for an existing Session, as specified
- # in section 3.2.4.1.7, the client MUST also set the following values:
- # The SessionId field in the SMB2 header MUST be set to the Session.SessionId for the new
- # channel being established.
- # The SMB2_SESSION_FLAG_BINDING bit MUST be set in the Flags field.
- # The PreviousSessionId field MUST be set to zero.
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_SESSION_SETUP
- packet['Data'] = sessionSetup
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED):
- self._Session['SessionID'] = ans['SessionID']
- self._Session['SigningRequired'] = self._Connection['RequireSigning']
- self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash)
- self._Session['Connection'] = self._NetBIOSSession.get_socket()
- sessionSetupResponse = SMB2SessionSetup_Response(ans['Data'])
- respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer'])
- # Let's parse some data and keep it to ourselves in case it is asked
- ntlmChallenge = ntlm.NTLMAuthChallenge(respToken['ResponseToken'])
- if ntlmChallenge['TargetInfoFields_len'] > 0:
- av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']])
- if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None:
- try:
- self._Session['ServerName'] = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le')
- except:
- # For some reason, we couldn't decode Unicode here.. silently discard the operation
- pass
- if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None:
- try:
- if self._Session['ServerName'] != av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le'):
- self._Session['ServerDomain'] = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le')
- except:
- # For some reason, we couldn't decode Unicode here.. silently discard the operation
- pass
- if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None:
- try:
- self._Session['ServerDNSDomainName'] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le')
- except:
- # For some reason, we couldn't decode Unicode here.. silently discard the operation
- pass
- # Parse Version to know the target Operating system name. Not provided elsewhere anymore
- if ntlmChallenge.fields.has_key('Version'):
- version = ntlmChallenge['Version']
- if len(version) >= 4:
- self._Session['ServerOS'] = "Windows %d.%d Build %d" % (ord(version[0]), ord(version[1]), struct.unpack('<H',version[2:4])[0])
- self._Session["ServerOSMajor"] = ord(version[0])
- self._Session["ServerOSMinor"] = ord(version[1])
- self._Session["ServerOSBuild"] = struct.unpack('<H',version[2:4])[0]
- type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, respToken['ResponseToken'], user, password, domain, lmhash, nthash)
-
- if exportedSessionKey is not None:
- self._Session['SessionKey'] = exportedSessionKey
- if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30:
- self._Session['SigningKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCMAC\x00", "SmbSign\x00", 128)
- respToken2 = SPNEGO_NegTokenResp()
- respToken2['ResponseToken'] = str(type3)
- # Reusing the previous structure
- sessionSetup['SecurityBufferLength'] = len(respToken2)
- sessionSetup['Buffer'] = respToken2.getData()
- packetID = self.sendSMB(packet)
- packet = self.recvSMB(packetID)
- try:
- if packet.isValidAnswer(STATUS_SUCCESS):
- sessionSetupResponse = SMB2SessionSetup_Response(packet['Data'])
- self._Session['SessionFlags'] = sessionSetupResponse['SessionFlags']
- # Calculate the key derivations for dialect 3.0
- if self._Session['SigningRequired'] is True:
- self._Session['SigningActivated'] = True
- if self._Connection['Dialect'] == SMB2_DIALECT_30:
- self._Session['ApplicationKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2APP\x00", "SmbRpc\x00", 128)
- self._Session['EncryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCCM\x00", "ServerIn \x00", 128)
- self._Session['DecryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCCM\x00", "ServerOut\x00", 128)
-
- return True
- except:
- # We clean the stuff we used in case we want to authenticate again
- # within the same connection
- self._Session['UserCredentials'] = ''
- self._Session['Connection'] = 0
- self._Session['SessionID'] = 0
- self._Session['SigningRequired'] = False
- self._Session['SigningKey'] = ''
- self._Session['SessionKey'] = ''
- self._Session['SigningActivated'] = False
- raise
- def connectTree(self, share):
- # Just in case this came with the full path (maybe an SMB1 client), let's just leave
- # the sharename, we'll take care of the rest
- #print self._Session['TreeConnectTable']
- share = share.split('\\')[-1]
- if self._Session['TreeConnectTable'].has_key(share):
- # Already connected, no need to reconnect
- treeEntry = self._Session['TreeConnectTable'][share]
- treeEntry['NumberOfUses'] += 1
- self._Session['TreeConnectTable'][treeEntry['TreeConnectId']]['NumberOfUses'] += 1
- return treeEntry['TreeConnectId']
- #path = share
- try:
- _, _, _, _, sockaddr = socket.getaddrinfo(self._Connection['ServerIP'], 80, 0, 0, socket.IPPROTO_TCP)[0]
- remoteHost = sockaddr[0]
- except:
- remoteHost = self._Connection['ServerIP']
- path = '\\\\' + remoteHost + '\\' +share
- treeConnect = SMB2TreeConnect()
- treeConnect['Buffer'] = path.encode('utf-16le')
- treeConnect['PathLength'] = len(path)*2
-
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_TREE_CONNECT
- packet['Data'] = treeConnect
- packetID = self.sendSMB(packet)
- packet = self.recvSMB(packetID)
- if packet.isValidAnswer(STATUS_SUCCESS):
- treeConnectResponse = SMB2TreeConnect_Response(packet['Data'])
- treeEntry = copy.deepcopy(TREE_CONNECT)
- treeEntry['ShareName'] = share
- treeEntry['TreeConnectId'] = packet['TreeID']
- treeEntry['Session'] = packet['SessionID']
- treeEntry['NumberOfUses'] += 1
- if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_DFS) == SMB2_SHARE_CAP_DFS:
- treeEntry['IsDfsShare'] = True
- if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) == SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY:
- treeEntry['IsCAShare'] = True
- if self._Connection['Dialect'] == SMB2_DIALECT_30:
- if (self._Connection['SupportsEncryption'] is True) and ((treeConnectResponse['ShareFlags'] & SMB2_SHAREFLAG_ENCRYPT_DATA) == SMB2_SHAREFLAG_ENCRYPT_DATA):
- treeEntry['EncryptData'] = True
- # ToDo: This and what follows
- # If Session.EncryptData is FALSE, the client MUST then generate an encryption key, a
- # decryption key as specified in section 3.1.4.2, by providing the following inputs and store
- # them in Session.EncryptionKey and Session.DecryptionKey:
- if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_SCALEOUT) == SMB2_SHARE_CAP_SCALEOUT:
- treeEntry['IsScaleoutShare'] = True
- self._Session['TreeConnectTable'][packet['TreeID']] = treeEntry
- self._Session['TreeConnectTable'][share] = treeEntry
- return packet['TreeID']
- def disconnectTree(self, treeId):
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- if self._Session['TreeConnectTable'].has_key(treeId):
- # More than 1 use? descrease it and return, if not, send the packet
- if self._Session['TreeConnectTable'][treeId]['NumberOfUses'] > 1:
- treeEntry = self._Session['TreeConnectTable'][treeId]
- treeEntry['NumberOfUses'] -= 1
- self._Session['TreeConnectTable'][treeEntry['ShareName']]['NumberOfUses'] -= 1
- return True
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_TREE_DISCONNECT
- packet['TreeID'] = treeId
- treeDisconnect = SMB2TreeDisconnect()
- packet['Data'] = treeDisconnect
- packetID = self.sendSMB(packet)
- packet = self.recvSMB(packetID)
- if packet.isValidAnswer(STATUS_SUCCESS):
- shareName = self._Session['TreeConnectTable'][treeId]['ShareName']
- del(self._Session['TreeConnectTable'][shareName])
- del(self._Session['TreeConnectTable'][treeId])
- return True
- def create(self, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, fileAttributes, impersonationLevel = SMB2_IL_IMPERSONATION, securityFlags = 0, oplockLevel = SMB2_OPLOCK_LEVEL_NONE, createContexts = None):
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- fileName = string.replace(fileName, '/', '\\')
- if len(fileName) > 0:
- fileName = ntpath.normpath(fileName)
- if fileName[0] == '\\':
- fileName = fileName[1:]
- if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True:
- pathName = fileName
- else:
- pathName = '\\\\' + self._Connection['ServerName'] + '\\' + fileName
- fileEntry = copy.deepcopy(FILE)
- fileEntry['LeaseKey'] = uuid.generate()
- fileEntry['LeaseState'] = SMB2_LEASE_NONE
- self.GlobalFileTable[pathName] = fileEntry
- if self._Connection['Dialect'] == SMB2_DIALECT_30 and self._Connection['SupportsDirectoryLeasing'] is True:
- # Is this file NOT on the root directory?
- if len(fileName.split('\\')) > 2:
- parentDir = ntpath.dirname(pathName)
- if self.GlobalFileTable.has_key(parentDir):
- LOG.critical("Don't know what to do now! :-o")
- raise
- else:
- parentEntry = copy.deepcopy(FILE)
- parentEntry['LeaseKey'] = uuid.generate()
- parentEntry['LeaseState'] = SMB2_LEASE_NONE
- self.GlobalFileTable[parentDir] = parentEntry
-
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_CREATE
- packet['TreeID'] = treeId
- if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True:
- packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS
- smb2Create = SMB2Create()
- smb2Create['SecurityFlags'] = 0
- smb2Create['RequestedOplockLevel'] = oplockLevel
- smb2Create['ImpersonationLevel'] = impersonationLevel
- smb2Create['DesiredAccess'] = desiredAccess
- smb2Create['FileAttributes'] = fileAttributes
- smb2Create['ShareAccess'] = shareMode
- smb2Create['CreateDisposition'] = creationDisposition
- smb2Create['CreateOptions'] = creationOptions
-
- smb2Create['NameLength'] = len(fileName)*2
- if fileName != '':
- smb2Create['Buffer'] = fileName.encode('utf-16le')
- else:
- smb2Create['Buffer'] = '\x00'
- if createContexts is not None:
- smb2Create['Buffer'] += createContexts
- smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + smb2Create['NameLength']
- smb2Create['CreateContextsLength'] = len(createContexts)
- else:
- smb2Create['CreateContextsOffset'] = 0
- smb2Create['CreateContextsLength'] = 0
- packet['Data'] = smb2Create
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- createResponse = SMB2Create_Response(ans['Data'])
- openFile = copy.deepcopy(OPEN)
- openFile['FileID'] = createResponse['FileID']
- openFile['TreeConnect'] = treeId
- openFile['Oplocklevel'] = oplockLevel
- openFile['Durable'] = False
- openFile['ResilientHandle'] = False
- openFile['LastDisconnectTime'] = 0
- openFile['FileName'] = pathName
- # ToDo: Complete the OperationBuckets
- if self._Connection['Dialect'] == SMB2_DIALECT_30:
- openFile['DesiredAccess'] = oplockLevel
- openFile['ShareMode'] = oplockLevel
- openFile['CreateOptions'] = oplockLevel
- openFile['FileAttributes'] = oplockLevel
- openFile['CreateDisposition'] = oplockLevel
- # ToDo: Process the contexts
- self._Session['OpenTable'][str(createResponse['FileID'])] = openFile
- # The client MUST generate a handle for the Open, and it MUST
- # return success and the generated handle to the calling application.
- # In our case, str(FileID)
- return str(createResponse['FileID'])
- def close(self, treeId, fileId):
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- if self._Session['OpenTable'].has_key(fileId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_CLOSE
- packet['TreeID'] = treeId
- smbClose = SMB2Close()
- smbClose['Flags'] = 0
- smbClose['FileID'] = fileId
-
- packet['Data'] = smbClose
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- del(self.GlobalFileTable[self._Session['OpenTable'][fileId]['FileName']])
- del(self._Session['OpenTable'][fileId])
-
- # ToDo Remove stuff from GlobalFileTable
- return True
- def read(self, treeId, fileId, offset = 0, bytesToRead = 0, waitAnswer = True):
- # IMPORTANT NOTE: As you can see, this was coded as a recursive function
- # Hence, you can exhaust the memory pretty easy ( large bytesToRead )
- # This function should NOT be used for reading files directly, but another higher
- # level function should be used that will break the read into smaller pieces
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- if self._Session['OpenTable'].has_key(fileId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_READ
- packet['TreeID'] = treeId
- if self._Connection['MaxReadSize'] < bytesToRead:
- maxBytesToRead = self._Connection['MaxReadSize']
- else:
- maxBytesToRead = bytesToRead
- if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
- packet['CreditCharge'] = ( 1 + (maxBytesToRead - 1) / 65536)
- else:
- maxBytesToRead = min(65536,bytesToRead)
- smbRead = SMB2Read()
- smbRead['Padding'] = 0x50
- smbRead['FileID'] = fileId
- smbRead['Length'] = maxBytesToRead
- smbRead['Offset'] = offset
- packet['Data'] = smbRead
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- readResponse = SMB2Read_Response(ans['Data'])
- retData = readResponse['Buffer']
- if readResponse['DataRemaining'] > 0:
- retData += self.read(treeId, fileId, offset+len(retData), readResponse['DataRemaining'], waitAnswer)
- return retData
-
- def write(self, treeId, fileId, data, offset = 0, bytesToWrite = 0, waitAnswer = True):
- # IMPORTANT NOTE: As you can see, this was coded as a recursive function
- # Hence, you can exhaust the memory pretty easy ( large bytesToWrite )
- # This function should NOT be used for writing directly to files, but another higher
- # level function should be used that will break the writes into smaller pieces
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- if self._Session['OpenTable'].has_key(fileId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_WRITE
- packet['TreeID'] = treeId
- if self._Connection['MaxWriteSize'] < bytesToWrite:
- maxBytesToWrite = self._Connection['MaxWriteSize']
- else:
- maxBytesToWrite = bytesToWrite
- if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
- packet['CreditCharge'] = ( 1 + (maxBytesToWrite - 1) / 65536)
- else:
- maxBytesToWrite = min(65536,bytesToWrite)
- smbWrite = SMB2Write()
- smbWrite['FileID'] = fileId
- smbWrite['Length'] = maxBytesToWrite
- smbWrite['Offset'] = offset
- smbWrite['WriteChannelInfoOffset'] = 0
- smbWrite['Buffer'] = data[:maxBytesToWrite]
- packet['Data'] = smbWrite
- packetID = self.sendSMB(packet)
- if waitAnswer is True:
- ans = self.recvSMB(packetID)
- else:
- return maxBytesToWrite
- if ans.isValidAnswer(STATUS_SUCCESS):
- writeResponse = SMB2Write_Response(ans['Data'])
- bytesWritten = writeResponse['Count']
- if bytesWritten < bytesToWrite:
- bytesWritten += self.write(treeId, fileId, data[bytesWritten:], offset+bytesWritten, bytesToWrite-bytesWritten, waitAnswer)
- return bytesWritten
- def queryDirectory(self, treeId, fileId, searchString = '*', resumeIndex = 0, informationClass = FILENAMES_INFORMATION, maxBufferSize = None, enumRestart = False, singleEntry = False):
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- if self._Session['OpenTable'].has_key(fileId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_QUERY_DIRECTORY
- packet['TreeID'] = treeId
- queryDirectory = SMB2QueryDirectory()
- queryDirectory['FileInformationClass'] = informationClass
- if resumeIndex != 0 :
- queryDirectory['Flags'] = SMB2_INDEX_SPECIFIED
- queryDirectory['FileIndex'] = resumeIndex
- queryDirectory['FileID'] = fileId
- if maxBufferSize is None:
- maxBufferSize = self._Connection['MaxReadSize']
- queryDirectory['OutputBufferLength'] = maxBufferSize
- queryDirectory['FileNameLength'] = len(searchString)*2
- queryDirectory['Buffer'] = searchString.encode('utf-16le')
- packet['Data'] = queryDirectory
- if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
- packet['CreditCharge'] = ( 1 + (maxBufferSize - 1) / 65536)
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- queryDirectoryResponse = SMB2QueryDirectory_Response(ans['Data'])
- return queryDirectoryResponse['Buffer']
- def echo(self):
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_ECHO
- smbEcho = SMB2Echo()
- packet['Data'] = smbEcho
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- return True
- def cancel(self, packetID):
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_CANCEL
- packet['MessageID'] = packetID
- smbCancel = SMB2Cancel()
- packet['Data'] = smbCancel
- self.sendSMB(packet)
- def ioctl(self, treeId, fileId = None, ctlCode = -1, flags = 0, inputBlob = '', maxInputResponse = None, maxOutputResponse = None, waitAnswer = 1):
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- if fileId is None:
- fileId = '\xff'*16
- else:
- if self._Session['OpenTable'].has_key(fileId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_IOCTL
- packet['TreeID'] = treeId
-
- smbIoctl = SMB2Ioctl()
- smbIoctl['FileID'] = fileId
- smbIoctl['CtlCode'] = ctlCode
- smbIoctl['MaxInputResponse'] = maxInputResponse
- smbIoctl['MaxOutputResponse'] = maxOutputResponse
- smbIoctl['InputCount'] = len(inputBlob)
- if len(inputBlob) == 0:
- smbIoctl['InputOffset'] = 0
- smbIoctl['Buffer'] = '\x00'
- else:
- smbIoctl['Buffer'] = inputBlob
- smbIoctl['OutputOffset'] = 0
- smbIoctl['MaxOutputResponse'] = maxOutputResponse
- smbIoctl['Flags'] = flags
- packet['Data'] = smbIoctl
-
- packetID = self.sendSMB(packet)
- if waitAnswer == 0:
- return True
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- smbIoctlResponse = SMB2Ioctl_Response(ans['Data'])
- return smbIoctlResponse['Buffer']
- def flush(self,treeId, fileId):
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- if self._Session['OpenTable'].has_key(fileId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_FLUSH
- packet['TreeID'] = treeId
- smbFlush = SMB2Flush()
- smbFlush['FileID'] = fileId
- packet['Data'] = smbFlush
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- return True
- def lock(self, treeId, fileId, locks, lockSequence = 0):
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- if self._Session['OpenTable'].has_key(fileId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_LOCK
- packet['TreeID'] = treeId
- smbLock = SMB2Lock()
- smbLock['FileID'] = fileId
- smbLock['LockCount'] = len(locks)
- smbLock['LockSequence'] = lockSequence
- smbLock['Locks'] = ''.join(str(x) for x in locks)
- packet['Data'] = smbLock
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- smbFlushResponse = SMB2Lock_Response(ans['Data'])
- return True
- # ToDo:
- # If Open.ResilientHandle is TRUE or Connection.SupportsMultiChannel is TRUE, the client MUST
- # do the following:
- # The client MUST scan through Open.OperationBuckets and find an element with its Free field
- # set to TRUE. If no such element could be found, an implementation-specific error MUST be
- # returned to the application.
- # Let the zero-based array index of the element chosen above be referred to as BucketIndex, and
- # let BucketNumber = BucketIndex +1.
- # Set Open.OperationBuckets[BucketIndex].Free = FALSE
- # Let the SequenceNumber of the element chosen above be referred to as BucketSequence.
- # The LockSequence field of the SMB2 lock request MUST be set to (BucketNumber<< 4) +
- # BucketSequence.
- # Increment the SequenceNumber of the element chosen above using MOD 16 arithmetic.
- def logoff(self):
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_LOGOFF
- smbLogoff = SMB2Logoff()
- packet['Data'] = smbLogoff
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- # We clean the stuff we used in case we want to authenticate again
- # within the same connection
- self._Session['UserCredentials'] = ''
- self._Session['Connection'] = 0
- self._Session['SessionID'] = 0
- self._Session['SigningRequired'] = False
- self._Session['SigningKey'] = ''
- self._Session['SessionKey'] = ''
- self._Session['SigningActivated'] = False
- return True
- def queryInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0, flags = 0 ):
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- if self._Session['OpenTable'].has_key(fileId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_QUERY_INFO
- packet['TreeID'] = treeId
- queryInfo = SMB2QueryInfo()
- queryInfo['FileID'] = fileId
- queryInfo['InfoType'] = SMB2_0_INFO_FILE
- queryInfo['FileInfoClass'] = fileInfoClass
- queryInfo['OutputBufferLength'] = 65535
- queryInfo['AdditionalInformation'] = additionalInformation
- if len(inputBlob) == 0:
- queryInfo['InputBufferOffset'] = 0
- queryInfo['Buffer'] = '\x00'
- else:
- queryInfo['InputBufferLength'] = len(inputBlob)
- queryInfo['Buffer'] = inputBlob
- queryInfo['Flags'] = flags
- packet['Data'] = queryInfo
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- queryResponse = SMB2QueryInfo_Response(ans['Data'])
- return queryResponse['Buffer']
- def setInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0 ):
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- if self._Session['OpenTable'].has_key(fileId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- packet = self.SMB_PACKET()
- packet['Command'] = SMB2_SET_INFO
- packet['TreeID'] = treeId
- setInfo = SMB2SetInfo()
- setInfo['InfoType'] = SMB2_0_INFO_FILE
- setInfo['FileInfoClass'] = fileInfoClass
- setInfo['BufferLength'] = len(inputBlob)
- setInfo['AdditionalInformation'] = additionalInformation
- setInfo['FileID'] = fileId
- setInfo['Buffer'] = inputBlob
- packet['Data'] = setInfo
- packetID = self.sendSMB(packet)
- ans = self.recvSMB(packetID)
- if ans.isValidAnswer(STATUS_SUCCESS):
- return True
- def getSessionKey(self):
- if self.getDialect() == SMB2_DIALECT_30:
- return self._Session['ApplicationKey']
- else:
- return self._Session['SessionKey']
- def setSessionKey(self, key):
- if self.getDialect() == SMB2_DIALECT_30:
- self._Session['ApplicationKey'] = key
- else:
- self._Session['SessionKey'] = key
- ######################################################################
- # Higher level functions
- def rename(self, shareName, oldPath, newPath):
- oldPath = string.replace(oldPath,'/', '\\')
- oldPath = ntpath.normpath(oldPath)
- if len(oldPath) > 0 and oldPath[0] == '\\':
- oldPath = oldPath[1:]
- newPath = string.replace(newPath,'/', '\\')
- newPath = ntpath.normpath(newPath)
- if len(newPath) > 0 and newPath[0] == '\\':
- newPath = newPath[1:]
- treeId = self.connectTree(shareName)
- fileId = None
- try:
- fileId = self.create(treeId, oldPath, MAXIMUM_ALLOWED ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, 0x200020, FILE_OPEN, 0)
- renameReq = FILE_RENAME_INFORMATION_TYPE_2()
- renameReq['ReplaceIfExists'] = 1
- renameReq['RootDirectory'] = '\x00'*8
- renameReq['FileNameLength'] = len(newPath)*2
- renameReq['FileName'] = newPath.encode('utf-16le')
- self.setInfo(treeId, fileId, renameReq, infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_RENAME_INFO)
- finally:
- if fileId is not None:
- self.close(treeId, fileId)
- self.disconnectTree(treeId)
- return True
- def writeFile(self, treeId, fileId, data, offset = 0):
- finished = False
- writeOffset = offset
- while not finished:
- if len(data) == 0:
- break
- writeData = data[:self._Connection['MaxWriteSize']]
- data = data[self._Connection['MaxWriteSize']:]
- written = self.write(treeId, fileId, writeData, writeOffset, len(writeData))
- writeOffset += written
- return writeOffset - offset
- def listPath(self, shareName, path, password = None):
- # ToDo: Handle situations where share is password protected
- path = string.replace(path,'/', '\\')
- path = ntpath.normpath(path)
- if len(path) > 0 and path[0] == '\\':
- path = path[1:]
- treeId = self.connectTree(shareName)
- fileId = None
- try:
- # ToDo, we're assuming it's a directory, we should check what the file type is
- fileId = self.create(treeId, ntpath.dirname(path), FILE_READ_ATTRIBUTES | FILE_READ_DATA ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN, 0)
- res = ''
- files = []
- from impacket import smb
- while True:
- try:
- res = self.queryDirectory( treeId, fileId, ntpath.basename(path), maxBufferSize = 65535, informationClass = FILE_FULL_DIRECTORY_INFORMATION )
- nextOffset = 1
- while nextOffset != 0:
- fileInfo = smb.SMBFindFileFullDirectoryInfo(smb.SMB.FLAGS2_UNICODE)
- fileInfo.fromString(res)
- files.append(smb.SharedFile(fileInfo['CreationTime'],fileInfo['LastAccessTime'],fileInfo['LastChangeTime'],fileInfo['EndOfFile'],fileInfo['AllocationSize'],fileInfo['ExtFileAttributes'],fileInfo['FileName'].decode('utf-16le'), fileInfo['FileName'].decode('utf-16le')))
- nextOffset = fileInfo['NextEntryOffset']
- res = res[nextOffset:]
- except SessionError, e:
- if (e.get_error_code()) != STATUS_NO_MORE_FILES:
- raise
- break
- finally:
- if fileId is not None:
- self.close(treeId, fileId)
- self.disconnectTree(treeId)
- return files
- def mkdir(self, shareName, pathName, password = None):
- # ToDo: Handle situations where share is password protected
- pathName = string.replace(pathName,'/', '\\')
- pathName = ntpath.normpath(pathName)
- if len(pathName) > 0 and pathName[0] == '\\':
- pathName = pathName[1:]
- treeId = self.connectTree(shareName)
- fileId = None
- try:
- fileId = self.create(treeId, pathName,GENERIC_ALL ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE, 0)
- finally:
- if fileId is not None:
- self.close(treeId, fileId)
- self.disconnectTree(treeId)
- return True
- def rmdir(self, shareName, pathName, password = None):
- # ToDo: Handle situations where share is password protected
- pathName = string.replace(pathName,'/', '\\')
- pathName = ntpath.normpath(pathName)
- if len(pathName) > 0 and pathName[0] == '\\':
- pathName = pathName[1:]
- treeId = self.connectTree(shareName)
- fileId = None
- try:
- fileId = self.create(treeId, pathName, DELETE, FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0)
- finally:
- if fileId is not None:
- self.close(treeId, fileId)
- self.disconnectTree(treeId)
- return True
- def remove(self, shareName, pathName, password = None):
- # ToDo: Handle situations where share is password protected
- pathName = string.replace(pathName,'/', '\\')
- pathName = ntpath.normpath(pathName)
- if len(pathName) > 0 and pathName[0] == '\\':
- pathName = pathName[1:]
- treeId = self.connectTree(shareName)
- fileId = None
- try:
- fileId = self.create(treeId, pathName,DELETE | FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0)
- finally:
- if fileId is not None:
- self.close(treeId, fileId)
- self.disconnectTree(treeId)
- return True
- def retrieveFile(self, shareName, path, callback, mode = FILE_OPEN, offset = 0, password = None, shareAccessMode = FILE_SHARE_READ):
- # ToDo: Handle situations where share is password protected
- path = string.replace(path,'/', '\\')
- path = ntpath.normpath(path)
- if len(path) > 0 and path[0] == '\\':
- path = path[1:]
- treeId = self.connectTree(shareName)
- fileId = None
- from impacket import smb
- try:
- fileId = self.create(treeId, path, FILE_READ_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0)
- res = self.queryInfo(treeId, fileId)
- fileInfo = smb.SMBQueryFileStandardInfo(res)
- fileSize = fileInfo['EndOfFile']
- if (fileSize-offset) < self._Connection['MaxReadSize']:
- # Skip reading 0 bytes files.
- if (fileSize-offset) > 0:
- data = self.read(treeId, fileId, offset, fileSize-offset)
- callback(data)
- else:
- written = 0
- toBeRead = fileSize-offset
- while written < toBeRead:
- data = self.read(treeId, fileId, offset, self._Connection['MaxReadSize'])
- written += len(data)
- offset += len(data)
- callback(data)
- finally:
- if fileId is not None:
- self.close(treeId, fileId)
- self.disconnectTree(treeId)
- def storeFile(self, shareName, path, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = FILE_SHARE_WRITE):
- # ToDo: Handle situations where share is password protected
- path = string.replace(path,'/', '\\')
- path = ntpath.normpath(path)
- if len(path) > 0 and path[0] == '\\':
- path = path[1:]
- treeId = self.connectTree(shareName)
- fileId = None
- try:
- fileId = self.create(treeId, path, FILE_WRITE_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0)
- finished = False
- writeOffset = offset
- while not finished:
- data = callback(self._Connection['MaxWriteSize'])
- if len(data) == 0:
- break
- written = self.write(treeId, fileId, data, writeOffset, len(data))
- writeOffset += written
- finally:
- if fileId is not None:
- self.close(treeId, fileId)
- self.disconnectTree(treeId)
- def waitNamedPipe(self, treeId, pipename, timeout = 5):
- pipename = ntpath.basename(pipename)
- if self._Session['TreeConnectTable'].has_key(treeId) is False:
- raise SessionError(STATUS_INVALID_PARAMETER)
- if len(pipename) > 0xffff:
- raise SessionError(STATUS_INVALID_PARAMETER)
- pipeWait = FSCTL_PIPE_WAIT_STRUCTURE()
- pipeWait['Timeout'] = timeout*100000
- pipeWait['NameLength'] = len(pipename)*2
- pipeWait['TimeoutSpecified'] = 1
- pipeWait['Name'] = pipename.encode('utf-16le')
- return self.ioctl(treeId, None, FSCTL_PIPE_WAIT,flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=pipeWait, maxInputResponse = 0, maxOutputResponse=0)
-
- def getIOCapabilities(self):
- res = dict()
- res['MaxReadSize'] = self._Connection['MaxReadSize']
- res['MaxWriteSize'] = self._Connection['MaxWriteSize']
- return res
-
- ######################################################################
- # Backward compatibility functions and alias for SMB1 and DCE Transports
- # NOTE: It is strongly recommended not to use these commands
- # when implementing new client calls.
- get_server_name = getServerName
- get_server_domain = getServerDomain
- get_server_dns_domain_name = getServerDNSDomainName
- get_remote_name = getServerName
- get_remote_host = getServerIP
- get_server_os = getServerOS
- get_server_os_major = getServerOSMajor
- get_server_os_minor = getServerOSMinor
- get_server_os_build = getServerOSBuild
- tree_connect_andx = connectTree
- tree_connect = connectTree
- connect_tree = connectTree
- disconnect_tree = disconnectTree
- set_timeout = setTimeout
- use_timeout = useTimeout
- stor_file = storeFile
- retr_file = retrieveFile
- list_path = listPath
- def __del__(self):
- if self._NetBIOSSession:
- self._NetBIOSSession.close()
- def doesSupportNTLMv2(self):
- # Always true :P
- return True
-
- def is_login_required(self):
- # Always true :P
- return True
- def is_signing_required(self):
- return self._Session["SigningRequired"]
- def nt_create_andx(self, treeId, fileName, smb_packet=None, cmd = None):
- if len(fileName) > 0 and fileName[0] == '\\':
- fileName = fileName[1:]
-
- if cmd is not None:
- from impacket import smb
- ntCreate = smb.SMBCommand(data = str(cmd))
- params = smb.SMBNtCreateAndX_Parameters(ntCreate['Parameters'])
- return self.create(treeId, fileName, params['AccessMask'], params['ShareAccess'],
- params['CreateOptions'], params['Disposition'], params['FileAttributes'],
- params['Impersonation'], params['SecurityFlags'])
-
- else:
- return self.create(treeId, fileName,
- FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA |
- FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | READ_CONTROL,
- FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, 0 )
-
- def get_socket(self):
- return self._NetBIOSSession.get_socket()
- def write_andx(self,tid,fid,data, offset = 0, wait_answer=1, write_pipe_mode = False, smb_packet=None):
- # ToDo: Handle the custom smb_packet situation
- return self.write(tid, fid, data, offset, len(data))
- def TransactNamedPipe(self, tid, fid, data, noAnswer = 0, waitAnswer = 1, offset = 0):
- return self.ioctl(tid, fid, FSCTL_PIPE_TRANSCEIVE, SMB2_0_IOCTL_IS_FSCTL, data, maxOutputResponse = 65535, waitAnswer = noAnswer | waitAnswer)
- def TransactNamedPipeRecv(self):
- ans = self.recvSMB()
- if ans.isValidAnswer(STATUS_SUCCESS):
- smbIoctlResponse = SMB2Ioctl_Response(ans['Data'])
- return smbIoctlResponse['Buffer']
- def read_andx(self, tid, fid, offset=0, max_size = None, wait_answer=1, smb_packet=None):
- # ToDo: Handle the custom smb_packet situation
- if max_size is None:
- max_size = self._Connection['MaxReadSize']
- return self.read(tid, fid, offset, max_size, wait_answer)
- def list_shared(self):
- # In the context of SMB2/3, forget about the old LANMAN, throw NOT IMPLEMENTED
- raise SessionError(STATUS_NOT_IMPLEMENTED)
- def open_andx(self, tid, fileName, open_mode, desired_access):
- # ToDo Return all the attributes of the file
- if len(fileName) > 0 and fileName[0] == '\\':
- fileName = fileName[1:]
- fileId = self.create(tid,fileName,desired_access, open_mode, FILE_NON_DIRECTORY_FILE, open_mode, 0)
- return fileId, 0, 0, 0, 0, 0, 0, 0, 0
|