Nmap Development mailing list archives

[NSE] SMB authentication patch


From: Ron <ron () skullsecurity net>
Date: Thu, 09 Oct 2008 19:11:00 -0500

Hi everybody,

I think I've got the SMB authentication all working nicely. I haven't
tested it as thoroughly as I'd like to, so this is more of a RFC release
than a stable release. But please, have a look and let me know what you
think!

This is tested against Sven's current OpenSSL bindings:
svn://svn.insecure.org/nmap-exp/sven/nse_openssl

But is also backwards compatible with the Nmap trunk (it'll disable
authentication and stick to null sessions if OpenSSL isn't available,
either because the 'openssl' nselib is missing or because
nmap.have_ssl() (or whatever it is) returns false).

There are a number of script args that can be used, the important ones
being smbuser, smbdomain, and smbpassword. smbhash is also available,
which lets you use the lanman or ntlm hashes directly (dumped by, say,
fgdump or pwdump). (I decided not to auto-detect hashes passed in the
password field, since there's a small chance of ambiguity). I also have
a boolean parameter called smbguest -- if it's set to 'true' or '1', it
will attempt to use the guest account in addition to the given account
and anonymous. Basically, no matter what you pass, it'll do this:
1) Attempt to log in with the given credentials; if that fails
2) (if smbguest is set) Attempt to log in with 'Guest' and a blank
password; if that fails
3) Attempt to log in with the null account (blank username/password).
This should always succeed, but will have severely restricted access.

I've tested this against Windows 2000, Windows XP, and Windows 2003,
plus a domain account on a Windows 2003 box, and they all worked perfectly.

I'm not 100% positive that I've tested every path, nor am I sure that
I've updated all the comments to reflect what I've done, so if you're
poking through the code, please have a look for that.

Looking forward to hearing comments!
Ron

PS: I've attached this as a 4000-line diff against the current Nmap
trunk. I can re-post as a .tgz if that's easier.
Index: scripts/smb-enumdomains.nse
===================================================================
--- scripts/smb-enumdomains.nse (revision 10583)
+++ scripts/smb-enumdomains.nse (working copy)
@@ -13,23 +13,50 @@
 -- sudo nmap -sU -sS --script smb-enumdomains.nse -p U:137,T:139 <host>\n
 --
 --@output
--- Host script results:
+-- Host script results:\n
 -- |  MSRPC: List of domains:\n
--- |  Domain: TEST1\n
--- |   |_ SID: S-1-5-21-1060284298-842925246-839522115\n
--- |   |_ Users: Administrator, ASPNET, Guest, Ron, test\n
--- |   |_ Creation time: 2006-10-17 15:35:07\n
--- |   |_ Min password length: 0 characters\n
--- |   |_ Max password age: 10675199 days\n
--- |   |_ Min password age: 0 days\n
--- |   |_ Password history length: 0 passwords\n
--- |   |_ Lockout threshold: 0 login attempts\n
--- |   |_ Lockout duration: 60 minutes\n
--- |   |_ Lockout window: 60 minutes\n
--- |   |_ Password properties: \n
--- |     |_  Password complexity requirements do not exist\n
+-- |  Domain: TESTDOMAIN\n
+-- |   |_ SID: S-1-5-21-2956463495-2656032972-1271678565\n
+-- |   |_ Users: Administrator, Guest, SUPPORT_388945a0\n
+-- |   |_ Creation time: 2007-11-26 15:24:04\n
+-- |   |_ Passwords: min length: 11 characters; min age: 5 days; max age: 63 days\n
+-- |   |_ Password lockout: 3 attempts in under 15 minutes will lock the account until manually reset\n
+-- |   |_ Password history : 5 passwords\n
+-- |   |_ Password properties:\n
+-- |     |_  Password complexity requirements exist\n
 -- |_    |_  Administrator account cannot be locked out\n
-
+-- 
+--@args  smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
+--                   are NOT understood. To set a domain, use the smbdomain argument. 
+--@args  smbdomain   The domain to log in with. If you aren't in a domained environment, then anything
+--                   will (should?) be accepted by the server. 
+--@args  smbpassword The password to connect with. Be cautious with this, since some servers will lock
+--                   accounts if the incorrect password is given (although it's rare for the 
+--                   'administrator' account to be lockoutable, in the off chance that it is, you could
+--                   get yourself in trouble). 
+--@args  smbhash     A password hash to use when logging in. This is given as a single hex string (32
+--                   characters) or a pair of hex strings (2 x 32 characters, optionally separated by a 
+--                   single character). These hashes are the Lanman or NTLM hash of the user's password,
+--                   and are stored by systems, on the harddrive or memory. They can be retrived from memory
+--                   using the fgdump or pwdump tools. 
+--@args  smbguest    If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one 
+--                   fails. This should be harmless, but I thought I would disable it by default anyway
+--                   because I'm not entirely sure of any possible consequences. 
+--@args  smbtype     The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
+--                   decent compromise between security and compatibility. If you are paranoid, you might 
+--                   want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be 
+--                   avoiding this protocol altogether :P). If you're using an extremely old system, you 
+--                   might need to set this to 'v1' or 'lm', which are less secure but more compatible. \n
+--\n
+--                   If you want finer grained control, these are the possible options:\n
+--                   <ul>
+--                       <li>v1 -- Sends LMv1 and NTLMv1</li>
+--                       <li>LMv1 -- Sends LMv1 only</li>
+--                       <li>NTLMv1 -- Sends NTLMv1 only (default)</li>
+--                       <li>v2 -- Sends LMv2 and NTLMv2</li>
+--                       <li>LMv2 -- Sends LMv2 only</li>
+--                   </ul>
+--
 -----------------------------------------------------------------------
 
 id = "MSRPC: List of domains"
@@ -57,26 +84,25 @@
 
 action = function(host)
        local response = " \n"
-       local status, socket
-       local uid, tid, fid
+       local status, smbstate
 
        -- Create the SMB session
-       status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SAMR_PATH)
+       status, smbstate  = msrpc.start_smb(host, msrpc.SAMR_PATH)
        if(status == false) then
-               return "ERROR: " .. socket
+               return "ERROR: " .. smbstate
        end
 
        -- Bind to SAMR service
-       status, bind_result = msrpc.bind(socket, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil, uid, tid, fid)
+       status, bind_result = msrpc.bind(smbstate, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil)
        if(status == false) then
-               msrpc.stop_smb(socket, uid, tid)
+               msrpc.stop_smb(smbstate)
                return "ERROR: " .. bind_result
        end
 
        -- Call connect4()
-       status, connect4_result = msrpc.samr_connect4(socket, host.ip, uid, tid, fid)
+       status, connect4_result = msrpc.samr_connect4(smbstate, host.ip)
        if(status == false) then
-               msrpc.stop_smb(socket, uid, tid)
+               msrpc.stop_smb(smbstate)
                return "ERROR: " .. connect4_result
        end
 
@@ -84,9 +110,9 @@
        connect_handle = connect4_result['connect_handle']
 
        -- Call EnumDomains()
-       status, enumdomains_result = msrpc.samr_enumdomains(socket, connect_handle, uid, tid, fid)
+       status, enumdomains_result = msrpc.samr_enumdomains(smbstate, connect_handle)
        if(status == false) then
-               msrpc.stop_smb(socket, uid, tid)
+               msrpc.stop_smb(smbstate)
                return "ERROR: " .. enumdomains_result
        end
 
@@ -104,9 +130,9 @@
                        local domain_handle
 
                        -- Call LookupDomain()
-                       status, lookupdomain_result = msrpc.samr_lookupdomain(socket, connect_handle, domain, uid, tid, 
fid)
+                       status, lookupdomain_result = msrpc.samr_lookupdomain(smbstate, connect_handle, domain)
                        if(status == false) then
-                               msrpc.stop_smb(socket, uid, tid)
+                               msrpc.stop_smb(smbstate)
                                return "ERROR: " .. lookupdomain_result
                        end
 
@@ -114,9 +140,9 @@
                        sid = lookupdomain_result['sid']
        
                        -- Call OpenDomain()
-                       status, opendomain_result = msrpc.samr_opendomain(socket, connect_handle, sid, uid, tid, fid)
+                       status, opendomain_result = msrpc.samr_opendomain(smbstate, connect_handle, sid)
                        if(status == false) then
-                               msrpc.stop_smb(socket, uid, tid)
+                               msrpc.stop_smb(smbstate)
                                return "ERROR: " .. opendomain_result
                        end
 
@@ -125,44 +151,80 @@
        
                        -- Call QueryDomainInfo2() to get domain properties. We call these for three types == 1, 8, and 
12, since those return
                        -- the most useful information. 
-                       status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(socket, domain_handle, 1, uid, 
tid, fid)
+                       status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(smbstate, domain_handle, 1)
                        if(status == false) then
-                               msrpc.stop_smb(socket, uid, tid)
+                               msrpc.stop_smb(smbstate)
                                return "ERROR: " .. querydomaininfo2_result
                        end
-                       status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(socket, domain_handle, 8, uid, 
tid, fid, querydomaininfo2_result)
+                       status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(smbstate, domain_handle, 8, 
querydomaininfo2_result)
                        if(status == false) then
-                               msrpc.stop_smb(socket, uid, tid)
+                               msrpc.stop_smb(smbstate)
                                return "ERROR: " .. querydomaininfo2_result
                        end
-                       status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(socket, domain_handle, 12, uid, 
tid, fid, querydomaininfo2_result)
+                       status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(smbstate, domain_handle, 12, 
querydomaininfo2_result)
                        if(status == false) then
-                               msrpc.stop_smb(socket, uid, tid)
+                               msrpc.stop_smb(smbstate)
                                return "ERROR: " .. querydomaininfo2_result
                        end
 
                        -- Call EnumDomainUsers() to get users
-                       status, enumdomainusers_result = msrpc.samr_enumdomainusers(socket, domain_handle, uid, tid, 
fid)
+                       status, enumdomainusers_result = msrpc.samr_enumdomainusers(smbstate, domain_handle)
                        if(status == false) then
-                               msrpc.stop_smb(socket, uid, tid)
+                               msrpc.stop_smb(smbstate)
                                return "ERROR: " .. enumdomainusers_result
                        end
 
                        -- Close the domain handle
-                       msrpc.samr_close(socket, domain_handle, uid, tid, fid)
+                       msrpc.samr_close(smbstate, domain_handle)
 
                        -- Finally, fill in the response!
                        response = response .. string.format("Domain: %s\n", domain)
                        response = response .. string.format(" |_ SID: %s\n",                               
msrpc.sid_to_string(lookupdomain_result['sid']))
                        response = response .. string.format(" |_ Users: %s\n",                             
stdnse.strjoin(", ", enumdomainusers_result['names']))
                        response = response .. string.format(" |_ Creation time: %s\n",                     
querydomaininfo2_result['create_date'])
-                       response = response .. string.format(" |_ Min password length: %d characters\n",    
querydomaininfo2_result['min_password_length'])
-                       response = response .. string.format(" |_ Max password age: %d days\n",             
querydomaininfo2_result['max_password_age'])
-                       response = response .. string.format(" |_ Min password age: %d days\n",             
querydomaininfo2_result['min_password_age'])
-                       response = response .. string.format(" |_ Password history length: %d passwords\n", 
querydomaininfo2_result['password_history_length'])
-                       response = response .. string.format(" |_ Lockout threshold: %d login attempts\n",  
querydomaininfo2_result['lockout_threshold'])
-                       response = response .. string.format(" |_ Lockout duration: %d minutes\n",          
querydomaininfo2_result['lockout_duration'])
-                       response = response .. string.format(" |_ Lockout window: %d minutes\n",            
querydomaininfo2_result['lockout_window'])
+
+                       -- Password characteristics
+                       local min_password_length = querydomaininfo2_result['min_password_length']
+                       local max_password_age = querydomaininfo2_result['max_password_age']
+                       local min_password_age = querydomaininfo2_result['min_password_age']
+
+                       if(min_password_length > 0) then
+                               min_password_length = string.format("%d characters", min_password_length)
+                       else
+                               min_password_length = "n/a"
+                       end
+
+                       if(max_password_age > 0 and max_password_age < 5000) then
+                               max_password_age = string.format("%d days", max_password_age)
+                       else
+                               max_password_age = "n/a"
+                       end
+
+                       if(min_password_age > 0) then
+                               min_password_age = string.format("%d days", min_password_age)
+                       else
+                               min_password_age = "n/a"
+                       end
+
+                       response = response .. string.format(" |_ Passwords: min length: %s; min age: %s; max age: 
%s\n", min_password_length, min_password_age, max_password_age)
+
+                       local lockout_duration = querydomaininfo2_result['lockout_duration']
+                       if(lockout_duration < 0) then
+                               lockout_duration = string.format("for %d minutes", 
querydomaininfo2_result['lockout_duration'])
+                       else
+                               lockout_duration = "until manually reset"
+                       end
+
+                       if(querydomaininfo2_result['lockout_threshold'] > 0) then
+                               response = response .. string.format(" |_ Password lockout: %d attempts in under %d 
minutes will lock the account %s\n",  querydomaininfo2_result['lockout_threshold'], 
querydomaininfo2_result['lockout_window'], lockout_duration)
+                       else
+                               response = response .. string.format(" |_ Account lockout disabled\n")
+                       end
+
+                       if(querydomaininfo2_result['password_history_length']) > 0 then
+                               response = response .. string.format(" |_ Password history : %d passwords\n", 
querydomaininfo2_result['password_history_length'])
+                       end
+
                        if(#querydomaininfo2_result['password_properties_list'] > 0) then
                                response = response .. " |_ Password properties: \n   |_  " .. stdnse.strjoin("\n   |_  
", querydomaininfo2_result['password_properties_list']) .. "\n"
                        end
@@ -170,10 +232,10 @@
        end
 
        -- Close the connect handle
-       msrpc.samr_close(socket, connect_handle, uid, tid, fid)
+       msrpc.samr_close(smbstate, connect_handle)
 
        -- Close the SMB session
-       msrpc.stop_smb(socket, uid, tid)
+       msrpc.stop_smb(smbstate)
 
        return response
 
Index: scripts/smb-os-discovery.nse
===================================================================
--- scripts/smb-os-discovery.nse        (revision 10583)
+++ scripts/smb-os-discovery.nse        (working copy)
@@ -1,5 +1,8 @@
 --- Attempts to determine the operating system over SMB protocol (ports 445 and 139). 
---  See nselib/smb.lua for more information on this protocol. 
+--  See nselib/smb.lua for more information on this protocol. \n
+--\n
+-- This will accept arguments for username/password/etc., but it's not genreally (ever?)
+-- required.
 --
 --@usage
 -- nmap --script smb-os-discovery.nse -p445 127.0.0.1\n
@@ -11,6 +14,37 @@
 -- |  Name: WORKGROUP\TEST1\n
 -- |_ System time: 2008-09-09 20:55:55 UTC-5\n
 -- 
+--@args  smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
+--                   are NOT understood. To set a domain, use the smbdomain argument. 
+--@args  smbdomain   The domain to log in with. If you aren't in a domained environment, then anything
+--                   will (should?) be accepted by the server. 
+--@args  smbpassword The password to connect with. Be cautious with this, since some servers will lock
+--                   accounts if the incorrect password is given (although it's rare for the 
+--                   'administrator' account to be lockoutable, in the off chance that it is, you could
+--                   get yourself in trouble). 
+--@args  smbhash     A password hash to use when logging in. This is given as a single hex string (32
+--                   characters) or a pair of hex strings (2 x 32 characters, optionally separated by a 
+--                   single character). These hashes are the Lanman or NTLM hash of the user's password,
+--                   and are stored by systems, on the harddrive or memory. They can be retrived from memory
+--                   using the fgdump or pwdump tools. 
+--@args  smbguest    If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one 
+--                   fails. This should be harmless, but I thought I would disable it by default anyway
+--                   because I'm not entirely sure of any possible consequences. 
+--@args  smbtype     The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
+--                   decent compromise between security and compatibility. If you are paranoid, you might 
+--                   want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be 
+--                   avoiding this protocol altogether :P). If you're using an extremely old system, you 
+--                   might need to set this to 'v1' or 'lm', which are less secure but more compatible. \n
+--\n
+--                   If you want finer grained control, these are the possible options:\n
+--                   <ul>
+--                       <li>v1 -- Sends LMv1 and NTLMv1</li>
+--                       <li>LMv1 -- Sends LMv1 only</li>
+--                       <li>NTLMv1 -- Sends NTLMv1 only (default)</li>
+--                       <li>v2 -- Sends LMv2 and NTLMv2</li>
+--                       <li>LMv2 -- Sends LMv2 only</li>
+--                   </ul>
+--
 -----------------------------------------------------------------------
 
 id = "OS from SMB"
@@ -52,31 +86,35 @@
 
 action = function(host)
 
+       local state
+       local status, err
+
        -- Start up SMB
-       status, socket = smb.start(host)
+       status, state = smb.start(host)
        if(status == false) then
-               return "Error: " .. socket
+               return "Error: " .. state
        end
 
        -- Negotiate protocol
-       status, negotiate_result = smb.negotiate_protocol(socket)
+       status, err = smb.negotiate_protocol(state)
+
        if(status == false) then
                stdnse.print_debug(2, "Negotiate session failed")
-               smb.stop(socket)
-               return "Error: " .. negotiate_result
+               smb.stop(state)
+               return "Error: " .. err
        end
 
        -- Start a session
-       status, session_result = smb.start_session(socket, "", negotiate_result['session_key'], 
negotiate_result['capabilities'])
+       status, err = smb.start_session(state, "")
        if(status == false) then
-               smb.stop(socket)
-               return "Error: " .. session_result
+               smb.stop(state)
+               return "Error: " .. err
        end
 
        -- Kill SMB
-       smb.stop(socket, session_result['uid'])
+       smb.stop(state)
 
-       return string.format("%s\nLAN Manager: %s\nName: %s\\%s\nSystem time: %s %s\n", 
get_windows_version(session_result['os']), session_result['lanmanager'], negotiate_result['domain'], 
negotiate_result['server'], negotiate_result['date'], negotiate_result['timezone_str'])
+       return string.format("%s\nLAN Manager: %s\nName: %s\\%s\nSystem time: %s %s\n", 
get_windows_version(state['os']), state['lanmanager'], state['domain'], state['server'], state['date'], 
state['timezone_str'])
 end
 
 
Index: scripts/smb-enumusers.nse
===================================================================
--- scripts/smb-enumusers.nse   (revision 10583)
+++ scripts/smb-enumusers.nse   (working copy)
@@ -63,7 +63,69 @@
 -- sudo nmap -sU -sS --script smb-enumusers.nse -p U:137,T:139 <host>\n
 --
 --@output
--- TODO
+-- Host script results:\n
+-- |  MSRPC: List of user accounts:\n
+-- |_ TESTBOX\Administrator, EXTERNAL\DnsAdmins, TESTBOX\Guest, EXTERNAL\HelpServicesGroup, EXTERNAL\PARTNERS$, 
TESTBOX\SUPPORT_388945a0\n
+-- \n
+-- Host script results:\n
+-- |  MSRPC: List of user accounts:\n
+-- |  Administrator\n
+-- |    |_ Domain: TESTBOX\n
+-- |    |_ RID: 500\n
+-- |    |_ Full name: Built-in account for administering the computer/domain\n
+-- |    |_ Flags: Normal account, Password doesn't expire\n
+-- |  DnsAdmins\n
+-- |    |_ Domain: EXTERNAL\n
+-- |    |_ RID: 1107\n
+-- |  Guest\n
+-- |    |_ Domain: TESTBOX\n
+-- |    |_ RID: 501\n
+-- |    |_ Full name: Built-in account for guest access to the computer/domain\n
+-- |    |_ Flags: Normal account, Disabled, Password not required, Password doesn't expire\n
+-- |  HelpServicesGroup\n
+-- |    |_ Domain: EXTERNAL\n
+-- |    |_ RID: 1001\n
+-- |  PARTNERS$\n
+-- |    |_ Domain: EXTERNAL\n
+-- |    |_ RID: 1104\n
+-- |  SUPPORT_388945a0\n
+-- |    |_ Domain: TESTBOX\n
+-- |    |_ RID: 1001\n
+-- |    |_ Full name: This is a vendor's account for the Help and Support Service\n
+-- |    |_ Description: CN=Microsoft Corporation,L=Redmond,S=Washington,C=US\n
+-- |_   |_ Flags: Normal account, Disabled, Password doesn't expire, Account auto locked (ACB_AUTOLOCK)\n
+--
+--@args  smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
+--                   are NOT understood. To set a domain, use the smbdomain argument. 
+--@args  smbdomain   The domain to log in with. If you aren't in a domained environment, then anything
+--                   will (should?) be accepted by the server. 
+--@args  smbpassword The password to connect with. Be cautious with this, since some servers will lock
+--                   accounts if the incorrect password is given (although it's rare for the 
+--                   'administrator' account to be lockoutable, in the off chance that it is, you could
+--                   get yourself in trouble). 
+--@args  smbhash     A password hash to use when logging in. This is given as a single hex string (32
+--                   characters) or a pair of hex strings (2 x 32 characters, optionally separated by a 
+--                   single character). These hashes are the Lanman or NTLM hash of the user's password,
+--                   and are stored by systems, on the harddrive or memory. They can be retrived from memory
+--                   using the fgdump or pwdump tools. 
+--@args  smbguest    If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one 
+--                   fails. This should be harmless, but I thought I would disable it by default anyway
+--                   because I'm not entirely sure of any possible consequences. 
+--@args  smbtype     The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
+--                   decent compromise between security and compatibility. If you are paranoid, you might 
+--                   want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be 
+--                   avoiding this protocol altogether :P). If you're using an extremely old system, you 
+--                   might need to set this to 'v1' or 'lm', which are less secure but more compatible. \n
+--\n
+--                   If you want finer grained control, these are the possible options:\n
+--                   <ul>
+--                       <li>v1 -- Sends LMv1 and NTLMv1</li>
+--                       <li>LMv1 -- Sends LMv1 only</li>
+--                       <li>NTLMv1 -- Sends NTLMv1 only (default)</li>
+--                       <li>v2 -- Sends LMv2 and NTLMv2</li>
+--                       <li>LMv2 -- Sends LMv2 only</li>
+--                   </ul>
+--
 -----------------------------------------------------------------------
 
 id = "MSRPC: List of user accounts"
@@ -96,29 +158,30 @@
 --        array of tables. Each table contains a 'name', 'domain', 'fullname', 'rid', and 'description'. 
 local function enum_samr(host)
 
+       local smbstate
        local bind_result, connect4_result, enumdomains_result
        local connect_handle
-       local status, socket
-       local uid, tid, fid
+       local status, smbstate
        local response = {}
 
        -- Create the SMB session
-       status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SAMR_PATH)
+       status, smbstate = msrpc.start_smb(host, msrpc.SAMR_PATH)
+
        if(status == false) then
-               return false, socket
+               return false, smbstate
        end
 
        -- Bind to SAMR service
-       status, bind_result = msrpc.bind(socket, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil, uid, tid, fid)
+       status, bind_result = msrpc.bind(smbstate, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil)
        if(status == false) then
-               msrpc.stop_smb(socket, uid, tid)
+               msrpc.stop_smb(smbstate)
                return false, bind_result
        end
 
        -- Call connect4()
-       status, connect4_result = msrpc.samr_connect4(socket, host.ip, uid, tid, fid)
+       status, connect4_result = msrpc.samr_connect4(smbstate, host.ip)
        if(status == false) then
-               msrpc.stop_smb(socket, uid, tid)
+               msrpc.stop_smb(smbstate)
                return false, connect4_result
        end
 
@@ -126,18 +189,19 @@
        connect_handle = connect4_result['connect_handle']
 
        -- Call EnumDomains()
-       status, enumdomains_result = msrpc.samr_enumdomains(socket, connect_handle, uid, tid, fid)
+       status, enumdomains_result = msrpc.samr_enumdomains(smbstate, connect_handle)
        if(status == false) then
-               msrpc.stop_smb(socket, uid, tid)
+               msrpc.stop_smb(smbstate)
                return false, enumdomains_result
        end
 
        -- If no domains were returned, go back with an error
        if(#enumdomains_result['domains'] == 0) then
-               msrpc.stop_smb(socket, uid, tid)
+               msrpc.stop_smb(smbstate)
                return false, "Couldn't find any domains"
        end
 
+       -- Now, loop through the domains and find the users
        for i = 1, #enumdomains_result['domains'], 1 do
 
                local domain = enumdomains_result['domains'][i]
@@ -148,9 +212,9 @@
                        local opendomain_result, querydisplayinfo_result
 
                        -- Call LookupDomain()
-                       status, lookupdomain_result = msrpc.samr_lookupdomain(socket, connect_handle, domain, uid, tid, 
fid)
+                       status, lookupdomain_result = msrpc.samr_lookupdomain(smbstate, connect_handle, domain)
                        if(status == false) then
-                               msrpc.stop_smb(socket, uid, tid)
+                               msrpc.stop_smb(smbstate)
                                return false, lookupdomain_result
                        end
 
@@ -158,9 +222,9 @@
                        sid = lookupdomain_result['sid']
        
                        -- Call OpenDomain()
-                       status, opendomain_result = msrpc.samr_opendomain(socket, connect_handle, sid, uid, tid, fid)
+                       status, opendomain_result = msrpc.samr_opendomain(smbstate, connect_handle, sid)
                        if(status == false) then
-                               msrpc.stop_smb(socket, uid, tid)
+                               msrpc.stop_smb(smbstate)
                                return false, opendomain_result
                        end
 
@@ -168,14 +232,14 @@
                        domain_handle = opendomain_result['domain_handle']
        
                        -- Call QueryDisplayInfo()
-                       status, querydisplayinfo_result = msrpc.samr_querydisplayinfo(socket, domain_handle, uid, tid, 
fid)
+                       status, querydisplayinfo_result = msrpc.samr_querydisplayinfo(smbstate, domain_handle)
                        if(status == false) then
-                               msrpc.stop_smb(socket, uid, tid)
+                               msrpc.stop_smb(smbstate)
                                return false, querydisplayinfo_result
                        end
 
                        -- Close the domain handle
-                       msrpc.samr_close(socket, domain_handle,  uid, tid, fid)
+                       msrpc.samr_close(smbstate, domain_handle)
 
                        -- Finally, fill in the response!
                        for i = 1, #querydisplayinfo_result['details'], 1 do
@@ -186,10 +250,10 @@
        end -- Domain loop
 
        -- Close the connect handle
-       msrpc.samr_close(socket, connect_handle, uid, tid, fid)
+       msrpc.samr_close(smbstate, connect_handle)
 
        -- Stop the SAMR SMB
-       msrpc.stop_smb(socket, uid, tid)
+       msrpc.stop_smb(smbstate)
 
        return true, response
 end
@@ -201,32 +265,32 @@
 --        array of tables. Each table contains a 'name', 'domain', and 'rid'. 
 local function enum_lsa(host)
 
-       local status, socket
-       local uid, tid, fid
+       local smbstate
+       local status
        local response = {}
 
     -- Create the SMB session
-    status, socket, uid, tid, fid, negotiate_result = msrpc.start_smb(host, msrpc.LSA_PATH)
+    status, smbstate = msrpc.start_smb(host, msrpc.LSA_PATH)
     if(status == false) then
-        return false, socket
+        return false, smbstate
     end
 
     -- Bind to LSA service
-    status, bind_result = msrpc.bind(socket, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil, uid, tid, fid)
+    status, bind_result = msrpc.bind(smbstate, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil)
     if(status == false) then
-        msrpc.stop_smb(socket, uid, tid)
+        msrpc.stop_smb(smbstate)
         return false, bind_result
     end
 
     -- Open the LSA policy
-    status, openpolicy2_result = msrpc.lsa_openpolicy2(socket, host.ip, uid, tid, fid)
+    status, openpolicy2_result = msrpc.lsa_openpolicy2(smbstate, host.ip)
     if(status == false) then
-        msrpc.stop_smb(socket, uid, tid)
+        msrpc.stop_smb(smbstate)
         return false, openpolicy2_result
     end
 
     -- Start with some common names, as well as the name returned by the negotiate call
-    names = {"administrator", "guest", "test", negotiate_result['domain'], negotiate_result['server'] }
+    names = {"administrator", "guest", "test", smbstate['domain'], smbstate['server'] }
 
     -- Get the server's name from nbstat
     local result, server_name = netbios.get_server_name(host.ip)
@@ -241,9 +305,9 @@
     end
 
     -- Look up the names, if any are valid than the server's SID will be returned
-    status, lookupnames2_result = msrpc.lsa_lookupnames2(socket, openpolicy2_result['policy_handle'], names, uid, tid, 
fid)
+    status, lookupnames2_result = msrpc.lsa_lookupnames2(smbstate, openpolicy2_result['policy_handle'], names)
     if(status == false) then
-        msrpc.stop_smb(socket, uid, tid)
+        msrpc.stop_smb(smbstate)
         return false, lookupnames2_result
     end
 
@@ -259,9 +323,9 @@
                        rids[#rids + 1] = j 
                end
 
-        status, lookupsids2_result = msrpc.lsa_lookupsids2(socket, openpolicy2_result['policy_handle'], sid, rids, 
uid, tid, fid)
+        status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sid, rids)
         if(status == false) then
-            msrpc.stop_smb(socket, uid, tid)
+            msrpc.stop_smb(smbstate)
             return false, lookupsids2_result
         end
 
@@ -284,9 +348,9 @@
                        end
 
                        -- Try converting this group of RIDs into names
-            status, lookupsids2_result = msrpc.lsa_lookupsids2(socket, openpolicy2_result['policy_handle'], sid, rids, 
uid, tid, fid)
+            status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sid, 
rids)
             if(status == false) then
-                msrpc.stop_smb(socket, uid, tid)
+                msrpc.stop_smb(smbstate)
                 return false, lookupsids2_result
             end
 
@@ -308,9 +372,9 @@
     end
 
     -- Close the handle
-    msrpc.lsa_close(socket, openpolicy2_result['policy_handle'], uid, tid, fid)
+    msrpc.lsa_close(smbstate, openpolicy2_result['policy_handle'])
 
-    msrpc.stop_smb(socket, uid, tid)
+    msrpc.stop_smb(smbstate)
 
        return true, response
 end
@@ -325,6 +389,19 @@
        local name_strings = {}
        local response = " \n"
 
+       -- Try enumerating through LSA first. Since LSA provides less information, we want the
+       -- SAMR result to overwrite it. 
+       status, lsa_result  = enum_lsa(host)
+       if(status == false) then
+               response = response .. "Enum via LSA error: " .. lsa_result .. "\n"
+       else
+               -- Copy the returned array into the names[] table, using the name as the key
+               stdnse.print_debug("EnumUsers: Received %d names from LSA", #lsa_result)
+               for i = 1, #lsa_result, 1 do
+                       names[string.upper(lsa_result[i]['name'])] = lsa_result[i]
+               end
+       end
+
        -- Try enumerating through SAMR
        status, samr_result = enum_samr(host)
        if(status == false) then
@@ -337,23 +414,9 @@
                end
        end
 
-       -- Try enumerating through LSA
-       status, lsa_result  = enum_lsa(host)
-       if(status == false) then
-               response = response .. "Enum via LSA error: " .. lsa_result .. "\n"
-       else
-               -- Copy the returned array into the names[] table, using the name as the key
-               stdnse.print_debug("EnumUsers: Received %d names from LSA", #samr_result)
-               for i = 1, #lsa_result, 1 do
-                       if(names[lsa_result[i]['name']] == nil) then
-                               names[string.upper(lsa_result[i]['name'])] = lsa_result[i]
-                       end
-               end
-       end
-
        -- Put the names into an array of strings, so we can sort them
        for name, details in pairs(names) do
-               name_strings[#name_strings + 1] = name
+               name_strings[#name_strings + 1] = names[name]['name']
        end
        -- Sort them
        table.sort(name_strings, function (a, b) return string.lower(a) < string.lower(b) end)
@@ -364,11 +427,18 @@
        else
                -- If we're not verbose, just print out the names. Otherwise, print out everything we can
                if(nmap.verbosity() < 1) then
-                       response = response .. stdnse.strjoin(", ", name_strings)
+                       local response_array = {}
+                       for i = 1, #name_strings, 1 do
+                               local name = string.upper(name_strings[i])
+                               response_array[#response_array + 1] = (names[name]['domain'] .. "\\" .. 
names[name]['name'])
+                       end
+                               
+                       response = response .. stdnse.strjoin(", ", response_array)
                else
                        for i = 1, #name_strings, 1 do
-                               local name = name_strings[i]
+                               local name = string.upper(name_strings[i])
                                response = response .. string.format("%s\n", names[name]['name'])
+
                                if(names[name]['domain'] ~= nil)      then response = response .. string.format("  |_ 
Domain: %s\n",      names[name]['domain'])      end
                                if(names[name]['rid'] ~= nil)         then response = response .. string.format("  |_ 
RID: %s\n",         names[name]['rid'])         end
                                if(names[name]['fullname'] ~= nil)    then response = response .. string.format("  |_ 
Full name: %s\n",   names[name]['fullname'])    end
Index: scripts/smb-security-mode.nse
===================================================================
--- scripts/smb-security-mode.nse       (revision 10583)
+++ scripts/smb-security-mode.nse       (working copy)
@@ -31,6 +31,9 @@
 -- \n
 --  See nselib/smb.lua for more information on the protocol itself. \n
 --\n
+-- This script will allow you to use smb arguments (username/password), but it probably
+-- won't ever require them. \n
+--\n
 --@usage
 -- nmap --script smb-security-mode.nse -p445 127.0.0.1\n
 -- sudo nmap -sU -sS --script smb-security-mode.nse -p U:137,T:139 127.0.0.1\n
@@ -40,6 +43,37 @@
 -- |  SMB Security: Challenge/response passwords supported\n
 -- |_ SMB Security: Message signing supported\n
 -- 
+--@args  smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
+--                   are NOT understood. To set a domain, use the smbdomain argument. 
+--@args  smbdomain   The domain to log in with. If you aren't in a domained environment, then anything
+--                   will (should?) be accepted by the server. 
+--@args  smbpassword The password to connect with. Be cautious with this, since some servers will lock
+--                   accounts if the incorrect password is given (although it's rare for the 
+--                   'administrator' account to be lockoutable, in the off chance that it is, you could
+--                   get yourself in trouble). 
+--@args  smbhash     A password hash to use when logging in. This is given as a single hex string (32
+--                   characters) or a pair of hex strings (2 x 32 characters, optionally separated by a 
+--                   single character). These hashes are the Lanman or NTLM hash of the user's password,
+--                   and are stored by systems, on the harddrive or memory. They can be retrived from memory
+--                   using the fgdump or pwdump tools. 
+--@args  smbguest    If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one 
+--                   fails. This should be harmless, but I thought I would disable it by default anyway
+--                   because I'm not entirely sure of any possible consequences. 
+--@args  smbtype     The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
+--                   decent compromise between security and compatibility. If you are paranoid, you might 
+--                   want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be 
+--                   avoiding this protocol altogether :P). If you're using an extremely old system, you 
+--                   might need to set this to 'v1' or 'lm', which are less secure but more compatible. \n
+--\n
+--                   If you want finer grained control, these are the possible options:\n
+--                   <ul>
+--                       <li>v1 -- Sends LMv1 and NTLMv1</li>
+--                       <li>LMv1 -- Sends LMv1 only</li>
+--                       <li>NTLMv1 -- Sends NTLMv1 only (default)</li>
+--                       <li>v2 -- Sends LMv2 and NTLMv2</li>
+--                       <li>LMv2 -- Sends LMv2 only</li>
+--                   </ul>
+--
 -----------------------------------------------------------------------
 
 id = "SMB Security"
@@ -66,20 +100,23 @@
 
 action = function(host)
 
-       local status, socket = smb.start(host)
+       local state
+       local status, err
 
+       status, state = smb.start(host)
        if(status == false) then
-               return "Error: " .. socket
+               return "Error: " .. state
        end
 
-       status, result = smb.negotiate_protocol(socket)
+       status, err = smb.negotiate_protocol(state)
 
        if(status == false) then
-               smb.stop(socket)
-               return "Error: " .. result
+               smb.stop(state)
+               return "Error: " .. err
        end
 
-       local security_mode = result['security_mode']
+       local security_mode = state['security_mode']
+
        local response = ""
        
        -- User-level authentication or share-level authentication
@@ -105,7 +142,7 @@
         response = response .. "SMB Security: Message signing not supported\n"
     end
 
-       smb.stop(socket)
+       smb.stop(state)
        return response
 end
 
Index: scripts/smb-enumshares.nse
===================================================================
--- scripts/smb-enumshares.nse  (revision 10583)
+++ scripts/smb-enumshares.nse  (working copy)
@@ -19,7 +19,41 @@
 --
 --@output
 -- Host script results:\n
--- TODO
+-- |  MSRPC: NetShareEnumAll():\n
+-- |  Anonymous shares: IPC$\n
+-- |_ Restricted shares: F$, ADMIN$, C$\n
+-- 
+--@args  smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
+--                   are NOT understood. To set a domain, use the smbdomain argument. 
+--@args  smbdomain   The domain to log in with. If you aren't in a domained environment, then anything
+--                   will (should?) be accepted by the server. 
+--@args  smbpassword The password to connect with. Be cautious with this, since some servers will lock
+--                   accounts if the incorrect password is given (although it's rare for the 
+--                   'administrator' account to be lockoutable, in the off chance that it is, you could
+--                   get yourself in trouble). 
+--@args  smbhash     A password hash to use when logging in. This is given as a single hex string (32
+--                   characters) or a pair of hex strings (2 x 32 characters, optionally separated by a 
+--                   single character). These hashes are the Lanman or NTLM hash of the user's password,
+--                   and are stored by systems, on the harddrive or memory. They can be retrived from memory
+--                   using the fgdump or pwdump tools. 
+--@args  smbguest    If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one 
+--                   fails. This should be harmless, but I thought I would disable it by default anyway
+--                   because I'm not entirely sure of any possible consequences. 
+--@args  smbtype     The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
+--                   decent compromise between security and compatibility. If you are paranoid, you might 
+--                   want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be 
+--                   avoiding this protocol altogether :P). If you're using an extremely old system, you 
+--                   might need to set this to 'v1' or 'lm', which are less secure but more compatible. \n
+--\n
+--                   If you want finer grained control, these are the possible options:\n
+--                   <ul>
+--                       <li>v1 -- Sends LMv1 and NTLMv1</li>
+--                       <li>LMv1 -- Sends LMv1 only</li>
+--                       <li>NTLMv1 -- Sends NTLMv1 only (default)</li>
+--                       <li>v2 -- Sends LMv2 and NTLMv2</li>
+--                       <li>LMv2 -- Sends LMv2 only</li>
+--                   </ul>
+--
 -----------------------------------------------------------------------
 
 id = "MSRPC: NetShareEnumAll()"
@@ -53,31 +87,31 @@
 --        a list of all shares on a system. 
 local function samr_enum_shares(host)
 
-       local status, socket, uid, tid, fid
+       local status, smbstate
        local bind_result, netshareenumall_result
 
        -- Create the SMB session
-       status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
+       status, smbstate = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
        if(status == false) then
-               return false, socket
+               return false, smbstate
        end
 
        -- Bind to SRVSVC service
-       status, bind_result = msrpc.bind(socket, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil, uid, tid, fid)
+       status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
        if(status == false) then
-               smb.stop(socket)
+               smb.stop(smbstate)
                return false, bind_result
        end
 
        -- Call netsharenumall
-       status, netshareenumall_result = msrpc.srvsvc_netshareenumall(socket, host.ip, uid, tid, fid)
+       status, netshareenumall_result = msrpc.srvsvc_netshareenumall(smbstate, host.ip)
        if(status == false) then
-               smb.stop(socket)
+               smb.stop(smbstate)
                return false, netshareenumall_result
        end
 
        -- Stop the SMB session
-       smb.stop(socket, uid, tid)
+       smb.stop(smbstate)
 
        return true, netshareenumall_result['shares']
 end
@@ -90,28 +124,29 @@
 --@return (allowed_shares, denied_shares) Lists of shares we can and can't access, 
 --        but all of which exist. 
 function check_shares(host, shares)
+       local smbstate
     local i
     local allowed_shares = {}
     local denied_shares = {}
 
        -- Begin the SMB session
-       status, socket = smb.start(host)
+       status, smbstate = smb.start(host)
        if(status == false) then
-               return false, socket
+               return false, smbstate
        end
 
        -- Negotiate the protocol
-       status, negotiate_result = smb.negotiate_protocol(socket)
+       status, err = smb.negotiate_protocol(smbstate)
        if(status == false) then
-               smb.stop(socket)
-               return false, negotiate_result
+               smb.stop(smbstate)
+               return false, err
        end
 
        -- Start up a null session
-       status, session_result = smb.start_session(socket, "", negotiate_result['session_key'], 
negotiate_result['capabilities'])
+       status, err = smb.start_session(smbstate, "", "", "", "", "LM")
        if(status == false) then
-               smb.stop(socket)
-               return false, session_result
+               smb.stop(smbstate)
+               return false, err
        end
 
        -- Connect to the shares
@@ -123,28 +158,28 @@
 
                -- Try connecting to the tree
                stdnse.print_debug(3, "EnumShares: Testing share %s", share)
-        status, tree_result = smb.tree_connect(socket, share, session_result['uid'])
+        status, err = smb.tree_connect(smbstate, share)
                -- If it fails, checkwhy
         if(status == false) then
                        -- If the result was ACCESS_DENIED, record it
-            if(tree_result == 0xc0000022 or tree_result == 'NT_STATUS_ACCESS_DENIED') then
+            if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then
                                stdnse.print_debug(3, "EnumShares: Access was denied")
                 denied_shares[#denied_shares + 1] = shares[i]
                        else
-                               stdnse.print_debug(3, "EnumShares: Share didn't pan out: %s", tree_result)
+                               stdnse.print_debug(3, "EnumShares: Share didn't pan out: %s", err)
             end
         else
                        -- Add it to allowed shares
                        stdnse.print_debug(3, "EnumShares: Access was granted")
             allowed_shares[#allowed_shares + 1] = shares[i]
-            smb.tree_disconnect(socket, session_result['uid'], tree_result['tid'])
+            smb.tree_disconnect(smbstate)
         end
     end
 
        -- Log off the user
-       smb.stop(socket, session_result['uid'])
+       smb.stop(smbstate)
 
-    return allowed_shares, denied_shares
+    return true, allowed_shares, denied_shares
 end
 
 
@@ -171,8 +206,12 @@
        end
 
        -- Break them into anonymous/authenticated shares
-       allowed, denied = check_shares(host, shares)
+       status, allowed, denied = check_shares(host, shares)
 
+       if(status == false) then
+               return "ERROR: " .. allowed
+       end
+
        return response .. string.format("Anonymous shares: %s\nRestricted shares: %s\n", stdnse.strjoin(", ", 
allowed), stdnse.strjoin(", ", denied))
 end
 
Index: nselib/smb.lua
===================================================================
--- nselib/smb.lua      (revision 10583)
+++ nselib/smb.lua      (working copy)
@@ -2,10 +2,10 @@
 --  sent to/from ports 139 or 445 of Windows systems, although it's also implemented by
 --  others (the most notable one being Samba). \n
 --\n
--- The intention of this library is toe ventually handle all aspects of the SMB protocol,
+-- The intention of this library is to eventually handle all aspects of the SMB protocol,
 -- A programmer using this library must already have some knowledge of the SMB protocol, 
 -- although a lot isn't necessary. You can pick up a lot by looking at the code that uses
--- this. The basic login is this:\n
+-- this. The basic login/logoff is this:\n
 --\n
 -- [connect]\n
 -- C->S SMB_COM_NEGOTIATE\n
@@ -21,16 +21,16 @@
 -- S->C SMB_COM_LOGOFF_ANDX\n
 --\n
 -- In terms of functions here, the protocol is:\n
--- status, socket            = smb.start(host)\n
--- status, negotiate_result  = smb.negotiate_protocol(socket)\n
--- status, session_result    = smb.start_session(socket, username, negotiate_result['session_key'], 
negotiate_result['capabilities'])\n
--- status, tree_result       = smb.tree_connect(socket, path, session_result['uid'])\n
--- status, disconnect_result = smb.tree_disconnect(socket, session_result['uid'], tree_result['tid'])\n
--- status, logoff_result     = smb.logoff(socket, session_result['uid'])\n
--- status, err               = smb.stop(socket)\n
+-- status, smbstate = smb.start(host)\n
+-- status, err      = smb.negotiate_protocol(smbstate)\n
+-- status, err      = smb.start_session(smbstate)\n
+-- status, err      = smb.tree_connect(smbstate, path)\n
+-- status, err      = smb.tree_disconnect(smbstate)\n
+-- status, err      = smb.logoff(smbstate)\n
+-- status, err      = smb.stop(smbstate)\n
 --\n
--- Optionally, the 'stop' function can also call tree_disconnect and logoff, by giving it extra parameters:\n
--- status, err               = smb.stop(socket, session_result['uid'], tree_result['tid'])\n
+-- The 'stop' function will automatically call tree_disconnect and logoff, 
+-- cleaning up the session.\n
 -- \n
 -- To initially begin the connection, there are two options:\n
 -- 1) Attempt to start a raw session over 445, if it's open. \n
@@ -47,6 +47,35 @@
 --\n
 -- If that's successful, SMB_COM_SESSION_SETUP_ANDX is sent. It is essentially the logon
 -- packet, where the username, domain, and password are sent to the server for verification. 
+-- The username and password are generally picked up from the program parameters, which are
+-- set when running a script, or from the registry [TODO: Where?], which are set by other 
+-- scripts. However, they can also be passed as parameters to the function, which will 
+-- override any other username/password set. \n
+--\n
+-- If a username is set without a password, then a NULL session is started. If a login fails,
+-- we attempt to log in as the 'GUEST' account with a blank password. If that fails, we try
+-- setting up a NULL session. Starting a NULL session will always work, but we may not get
+-- any further (tree_connect() might fail). \n
+--\n
+-- In terms of the login protocol, by default, we sent only NTLMv1 authentication, Lanman
+-- isn't set. The reason for this is, NTLMv2 isn't supported by every system (and I don't know
+-- how to do message signing on the v2 protocols), and doesn't have a significant security
+-- advantage over NTLMv1 (the major change in NTLMv2 is incorporating a client challenge). 
+-- Lanman is horribly insecure, though, so I don't send it at all. These options can, however,
+-- be overridden either through script parameters or registry settings [TODO]. \n
+--\n
+-- Lanman v1 is a fairly weak protocol, although it's still fairly difficult to reverse. NTLM v1 is a slightly mor 
secure
+-- protocol (although not much) -- it's also fairly difficult to reverse, though. Windows clients, by default send 
LMv1 and
+-- NTLMv1 together, but every modern Windows server will accept NTLM alone, so I opted to use that. LMv2 and NTLMv2 are
+-- slightly more secure, and they let the client specify random data (to help fight malicious servers with pre-
+-- generated tables). LMv2 and NTLMv2 are identical, except that NTLMv2 has a longer client challenge. LMv2 can be sent
+-- alone, but NTLMv2 can't.\n
+--\n
+-- Another interesting aspect of the password hashing is that the original password isn't even necessary, the
+-- password's hash can be used instead. This hash can be dumped from memory of a live system by tools such as
+-- pwdump and fgdump, or read straight from the SAM file. This means that if a password file is recovered, 
+-- it doesn't even need to be cracked before it can be used here.\n
+--\n
 -- The response to SMB_COM_SESSION_SETUP_ANDX is fairly simple, containing a boolean for 
 -- success, along with the operating system and the lan manager name. \n
 --\n
@@ -56,10 +85,51 @@
 --\n
 -- Each share will either return STATUS_BAD_NETWORK_NAME if the share doesn't
 -- exist, STATUS_ACCESS_DENIED if it exists but we don't have access, or 
--- STATUS_SUCCESS if exists and we do have access. \n
+-- STATUS_SUCCESS if exists and we do have access. STATUS_ACCESS_DENIED is also returned
+-- if the server requires message signing and we don't return a valid signature.\n
 --\n
--- Thanks go to Christopher R. Hertel and Implementing CIFS, which 
--- taught me everything I know about Microsoft's protocols. \n
+-- Thanks go to Christopher R. Hertel and his book <i>Implementing CIFS</i>, which 
+-- taught me everything I know about Microsoft's protocols. Additionally, I used Samba's
+-- list of error codes for my constants, although I don't believe they would be covered
+-- by GPL, since they're public now anyways, but I'm not a lawyer and, if somebody feels
+-- differently, let me know and we can sort this out. \n
+--\n
+-- The following arguments are understood by this script. I don't know if putting them in the nselib file
+-- is the right thing to do, but they're here for now anyways. \n
+--\n
+-- Here's an example of a script with parameters:\n
+--  nmap --script=<script> --script-args smbusername=administrator,smbpassword=ch4ngem3,smbtype=v2 <host>\n
+--\n
+--@args  smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
+--                   are NOT understood. To set a domain, use the smbdomain argument. 
+--@args  smbdomain   The domain to log in with. If you aren't in a domained environment, then anything
+--                   will (should?) be accepted by the server. 
+--@args  smbpassword The password to connect with. Be cautious with this, since some servers will lock
+--                   accounts if the incorrect password is given (although it's rare for the 
+--                   'administrator' account to be lockoutable, in the off chance that it is, you could
+--                   get yourself in trouble). 
+--@args  smbhash     A password hash to use when logging in. This is given as a single hex string (32
+--                   characters) or a pair of hex strings (2 x 32 characters, optionally separated by a 
+--                   single character). These hashes are the Lanman or NTLM hash of the user's password,
+--                   and are stored by systems, on the harddrive or memory. They can be retrived from memory
+--                   using the fgdump or pwdump tools. 
+--@args  smbguest    If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one 
+--                   fails. This should be harmless, but I thought I would disable it by default anyway
+--                   because I'm not entirely sure of any possible consequences. 
+--@args  smbtype     The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
+--                   decent compromise between security and compatibility. If you are paranoid, you might 
+--                   want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be 
+--                   avoiding this protocol altogether :P). If you're using an extremely old system, you 
+--                   might need to set this to 'v1' or 'lm', which are less secure but more compatible. \n
+--\n
+--                   If you want finer grained control, these are the possible options:\n
+--                   <ul>
+--                       <li>v1 -- Sends LMv1 and NTLMv1</li>
+--                       <li>LMv1 -- Sends LMv1 only</li>
+--                       <li>NTLMv1 -- Sends NTLMv1 only (default)</li>
+--                       <li>v2 -- Sends LMv2 and NTLMv2</li>
+--                       <li>LMv2 -- Sends LMv2 only</li>
+--                   </ul>
 --
 --@author Ron Bowes <ron () skullsecurity net>
 --@copyright See nmaps COPYING for licence
@@ -70,604 +140,31 @@
 require 'bin'
 require 'netbios'
 require 'stdnse'
+have_ssl = (nmap.have_ssl() and pcall(require, "openssl"))
 
-local mutex = nmap.mutex("SMB")
-
+-- These arrays are filled in with constants at the bottom of this file
 local command_codes = {}
 local command_names = {}
-command_codes['SMB_COM_CREATE_DIRECTORY']          = 0x00
-command_codes['SMB_COM_DELETE_DIRECTORY']          = 0x01
-command_codes['SMB_COM_OPEN']                      = 0x02
-command_codes['SMB_COM_CREATE']                    = 0x03
-command_codes['SMB_COM_CLOSE']                     = 0x04
-command_codes['SMB_COM_FLUSH']                     = 0x05
-command_codes['SMB_COM_DELETE']                    = 0x06
-command_codes['SMB_COM_RENAME']                    = 0x07
-command_codes['SMB_COM_QUERY_INFORMATION']         = 0x08
-command_codes['SMB_COM_SET_INFORMATION']           = 0x09
-command_codes['SMB_COM_READ']                      = 0x0A
-command_codes['SMB_COM_WRITE']                     = 0x0B
-command_codes['SMB_COM_LOCK_BYTE_RANGE']           = 0x0C
-command_codes['SMB_COM_UNLOCK_BYTE_RANGE']         = 0x0D
-command_codes['SMB_COM_CREATE_TEMPORARY']          = 0x0E
-command_codes['SMB_COM_CREATE_NEW']                = 0x0F
-command_codes['SMB_COM_CHECK_DIRECTORY']           = 0x10
-command_codes['SMB_COM_PROCESS_EXIT']              = 0x11
-command_codes['SMB_COM_SEEK']                      = 0x12
-command_codes['SMB_COM_LOCK_AND_READ']             = 0x13
-command_codes['SMB_COM_WRITE_AND_UNLOCK']          = 0x14
-command_codes['SMB_COM_READ_RAW']                  = 0x1A
-command_codes['SMB_COM_READ_MPX']                  = 0x1B
-command_codes['SMB_COM_READ_MPX_SECONDARY']        = 0x1C
-command_codes['SMB_COM_WRITE_RAW']                 = 0x1D
-command_codes['SMB_COM_WRITE_MPX']                 = 0x1E
-command_codes['SMB_COM_WRITE_MPX_SECONDARY']       = 0x1F
-command_codes['SMB_COM_WRITE_COMPLETE']            = 0x20
-command_codes['SMB_COM_QUERY_SERVER']              = 0x21
-command_codes['SMB_COM_SET_INFORMATION2']          = 0x22
-command_codes['SMB_COM_QUERY_INFORMATION2']        = 0x23
-command_codes['SMB_COM_LOCKING_ANDX']              = 0x24
-command_codes['SMB_COM_TRANSACTION']               = 0x25
-command_codes['SMB_COM_TRANSACTION_SECONDARY']     = 0x26
-command_codes['SMB_COM_IOCTL']                     = 0x27
-command_codes['SMB_COM_IOCTL_SECONDARY']           = 0x28
-command_codes['SMB_COM_COPY']                      = 0x29
-command_codes['SMB_COM_MOVE']                      = 0x2A
-command_codes['SMB_COM_ECHO']                      = 0x2B
-command_codes['SMB_COM_WRITE_AND_CLOSE']           = 0x2C
-command_codes['SMB_COM_OPEN_ANDX']                 = 0x2D
-command_codes['SMB_COM_READ_ANDX']                 = 0x2E
-command_codes['SMB_COM_WRITE_ANDX']                = 0x2F
-command_codes['SMB_COM_NEW_FILE_SIZE']             = 0x30
-command_codes['SMB_COM_CLOSE_AND_TREE_DISC']       = 0x31
-command_codes['SMB_COM_TRANSACTION2']              = 0x32
-command_codes['SMB_COM_TRANSACTION2_SECONDARY']    = 0x33
-command_codes['SMB_COM_FIND_CLOSE2']               = 0x34
-command_codes['SMB_COM_FIND_NOTIFY_CLOSE']         = 0x35
-command_codes['SMB_COM_TREE_CONNECT']              = 0x70
-command_codes['SMB_COM_TREE_DISCONNECT']           = 0x71
-command_codes['SMB_COM_NEGOTIATE']                 = 0x72
-command_codes['SMB_COM_SESSION_SETUP_ANDX']        = 0x73
-command_codes['SMB_COM_LOGOFF_ANDX']               = 0x74
-command_codes['SMB_COM_TREE_CONNECT_ANDX']         = 0x75
-command_codes['SMB_COM_QUERY_INFORMATION_DISK']    = 0x80
-command_codes['SMB_COM_SEARCH']                    = 0x81
-command_codes['SMB_COM_FIND']                      = 0x82
-command_codes['SMB_COM_FIND_UNIQUE']               = 0x83
-command_codes['SMB_COM_FIND_CLOSE']                = 0x84
-command_codes['SMB_COM_NT_TRANSACT']               = 0xA0
-command_codes['SMB_COM_NT_TRANSACT_SECONDARY']     = 0xA1
-command_codes['SMB_COM_NT_CREATE_ANDX']            = 0xA2
-command_codes['SMB_COM_NT_CANCEL']                 = 0xA4
-command_codes['SMB_COM_NT_RENAME']                 = 0xA5
-command_codes['SMB_COM_OPEN_PRINT_FILE']           = 0xC0
-command_codes['SMB_COM_WRITE_PRINT_FILE']          = 0xC1
-command_codes['SMB_COM_CLOSE_PRINT_FILE']          = 0xC2
-command_codes['SMB_COM_GET_PRINT_QUEUE']           = 0xC3
-command_codes['SMB_COM_READ_BULK']                 = 0xD8
-command_codes['SMB_COM_WRITE_BULK']                = 0xD9
-command_codes['SMB_COM_WRITE_BULK_DATA']           = 0xDA
-command_codes['SMB_NO_FURTHER_COMMANDS']           = 0xFF
+local status_codes = {}
+local status_names = {}
 
-for i, v in pairs(command_codes) do
-       command_names[v] = i
-end
+local mutex = nmap.mutex("SMB")
 
+---Convert a status number from the SMB header into a status name, returning an error message (not nil) if 
+-- it wasn't found. 
+--
+--@param status The numerical status. 
+--@return A string representing the error. Never nil. 
+local function get_status_name(status)
 
+       if(status_names[status] == nil) then
+               -- If the name wasn't found in the array, do a linear search on it (TODO: Why is this happening??)
+               for i, v in pairs(status_names) do
+                       if(v == status) then
+                               return i
+                       end
+               end
 
-local status_codes = {}
-local status_names = {}
-status_codes['NT_STATUS_OK'] = 0x0000
-status_codes['NT_STATUS_BUFFER_OVERFLOW'] = 0x80000005
-status_codes['NT_STATUS_UNSUCCESSFUL'] = 0xc0000001
-status_codes['NT_STATUS_NOT_IMPLEMENTED'] = 0xc0000002
-status_codes['NT_STATUS_INVALID_INFO_CLASS'] = 0xc0000003
-status_codes['NT_STATUS_INFO_LENGTH_MISMATCH'] = 0xc0000004
-status_codes['NT_STATUS_ACCESS_VIOLATION'] = 0xc0000005
-status_codes['NT_STATUS_IN_PAGE_ERROR'] = 0xc0000006
-status_codes['NT_STATUS_PAGEFILE_QUOTA'] = 0xc0000007
-status_codes['NT_STATUS_INVALID_HANDLE'] = 0xc0000008
-status_codes['NT_STATUS_BAD_INITIAL_STACK'] = 0xc0000009
-status_codes['NT_STATUS_BAD_INITIAL_PC'] = 0xc000000a
-status_codes['NT_STATUS_INVALID_CID'] = 0xc000000b
-status_codes['NT_STATUS_TIMER_NOT_CANCELED'] = 0xc000000c
-status_codes['NT_STATUS_INVALID_PARAMETER'] = 0xc000000d
-status_codes['NT_STATUS_NO_SUCH_DEVICE'] = 0xc000000e
-status_codes['NT_STATUS_NO_SUCH_FILE'] = 0xc000000f
-status_codes['NT_STATUS_INVALID_DEVICE_REQUEST'] = 0xc0000010
-status_codes['NT_STATUS_END_OF_FILE'] = 0xc0000011
-status_codes['NT_STATUS_WRONG_VOLUME'] = 0xc0000012
-status_codes['NT_STATUS_NO_MEDIA_IN_DEVICE'] = 0xc0000013
-status_codes['NT_STATUS_UNRECOGNIZED_MEDIA'] = 0xc0000014
-status_codes['NT_STATUS_NONEXISTENT_SECTOR'] = 0xc0000015
-status_codes['NT_STATUS_MORE_PROCESSING_REQUIRED'] = 0xc0000016
-status_codes['NT_STATUS_NO_MEMORY'] = 0xc0000017
-status_codes['NT_STATUS_CONFLICTING_ADDRESSES'] = 0xc0000018
-status_codes['NT_STATUS_NOT_MAPPED_VIEW'] = 0xc0000019
-status_codes['NT_STATUS_UNABLE_TO_FREE_VM'] = 0xc000001a
-status_codes['NT_STATUS_UNABLE_TO_DELETE_SECTION'] = 0xc000001b
-status_codes['NT_STATUS_INVALID_SYSTEM_SERVICE'] = 0xc000001c
-status_codes['NT_STATUS_ILLEGAL_INSTRUCTION'] = 0xc000001d
-status_codes['NT_STATUS_INVALID_LOCK_SEQUENCE'] = 0xc000001e
-status_codes['NT_STATUS_INVALID_VIEW_SIZE'] = 0xc000001f
-status_codes['NT_STATUS_INVALID_FILE_FOR_SECTION'] = 0xc0000020
-status_codes['NT_STATUS_ALREADY_COMMITTED'] = 0xc0000021
-status_codes['NT_STATUS_ACCESS_DENIED'] = 0xc0000022
-status_codes['NT_STATUS_BUFFER_TOO_SMALL'] = 0xc0000023
-status_codes['NT_STATUS_OBJECT_TYPE_MISMATCH'] = 0xc0000024
-status_codes['NT_STATUS_NONCONTINUABLE_EXCEPTION'] = 0xc0000025
-status_codes['NT_STATUS_INVALID_DISPOSITION'] = 0xc0000026
-status_codes['NT_STATUS_UNWIND'] = 0xc0000027
-status_codes['NT_STATUS_BAD_STACK'] = 0xc0000028
-status_codes['NT_STATUS_INVALID_UNWIND_TARGET'] = 0xc0000029
-status_codes['NT_STATUS_NOT_LOCKED'] = 0xc000002a
-status_codes['NT_STATUS_PARITY_ERROR'] = 0xc000002b
-status_codes['NT_STATUS_UNABLE_TO_DECOMMIT_VM'] = 0xc000002c
-status_codes['NT_STATUS_NOT_COMMITTED'] = 0xc000002d
-status_codes['NT_STATUS_INVALID_PORT_ATTRIBUTES'] = 0xc000002e
-status_codes['NT_STATUS_PORT_MESSAGE_TOO_LONG'] = 0xc000002f
-status_codes['NT_STATUS_INVALID_PARAMETER_MIX'] = 0xc0000030
-status_codes['NT_STATUS_INVALID_QUOTA_LOWER'] = 0xc0000031
-status_codes['NT_STATUS_DISK_CORRUPT_ERROR'] = 0xc0000032
-status_codes['NT_STATUS_OBJECT_NAME_INVALID'] = 0xc0000033
-status_codes['NT_STATUS_OBJECT_NAME_NOT_FOUND'] = 0xc0000034
-status_codes['NT_STATUS_OBJECT_NAME_COLLISION'] = 0xc0000035
-status_codes['NT_STATUS_HANDLE_NOT_WAITABLE'] = 0xc0000036
-status_codes['NT_STATUS_PORT_DISCONNECTED'] = 0xc0000037
-status_codes['NT_STATUS_DEVICE_ALREADY_ATTACHED'] = 0xc0000038
-status_codes['NT_STATUS_OBJECT_PATH_INVALID'] = 0xc0000039
-status_codes['NT_STATUS_OBJECT_PATH_NOT_FOUND'] = 0xc000003a
-status_codes['NT_STATUS_OBJECT_PATH_SYNTAX_BAD'] = 0xc000003b
-status_codes['NT_STATUS_DATA_OVERRUN'] = 0xc000003c
-status_codes['NT_STATUS_DATA_LATE_ERROR'] = 0xc000003d
-status_codes['NT_STATUS_DATA_ERROR'] = 0xc000003e
-status_codes['NT_STATUS_CRC_ERROR'] = 0xc000003f
-status_codes['NT_STATUS_SECTION_TOO_BIG'] = 0xc0000040
-status_codes['NT_STATUS_PORT_CONNECTION_REFUSED'] = 0xc0000041
-status_codes['NT_STATUS_INVALID_PORT_HANDLE'] = 0xc0000042
-status_codes['NT_STATUS_SHARING_VIOLATION'] = 0xc0000043
-status_codes['NT_STATUS_QUOTA_EXCEEDED'] = 0xc0000044
-status_codes['NT_STATUS_INVALID_PAGE_PROTECTION'] = 0xc0000045
-status_codes['NT_STATUS_MUTANT_NOT_OWNED'] = 0xc0000046
-status_codes['NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED'] = 0xc0000047
-status_codes['NT_STATUS_PORT_ALREADY_SET'] = 0xc0000048
-status_codes['NT_STATUS_SECTION_NOT_IMAGE'] = 0xc0000049
-status_codes['NT_STATUS_SUSPEND_COUNT_EXCEEDED'] = 0xc000004a
-status_codes['NT_STATUS_THREAD_IS_TERMINATING'] = 0xc000004b
-status_codes['NT_STATUS_BAD_WORKING_SET_LIMIT'] = 0xc000004c
-status_codes['NT_STATUS_INCOMPATIBLE_FILE_MAP'] = 0xc000004d
-status_codes['NT_STATUS_SECTION_PROTECTION'] = 0xc000004e
-status_codes['NT_STATUS_EAS_NOT_SUPPORTED'] = 0xc000004f
-status_codes['NT_STATUS_EA_TOO_LARGE'] = 0xc0000050
-status_codes['NT_STATUS_NONEXISTENT_EA_ENTRY'] = 0xc0000051
-status_codes['NT_STATUS_NO_EAS_ON_FILE'] = 0xc0000052
-status_codes['NT_STATUS_EA_CORRUPT_ERROR'] = 0xc0000053
-status_codes['NT_STATUS_FILE_LOCK_CONFLICT'] = 0xc0000054
-status_codes['NT_STATUS_LOCK_NOT_GRANTED'] = 0xc0000055
-status_codes['NT_STATUS_DELETE_PENDING'] = 0xc0000056
-status_codes['NT_STATUS_CTL_FILE_NOT_SUPPORTED'] = 0xc0000057
-status_codes['NT_STATUS_UNKNOWN_REVISION'] = 0xc0000058
-status_codes['NT_STATUS_REVISION_MISMATCH'] = 0xc0000059
-status_codes['NT_STATUS_INVALID_OWNER'] = 0xc000005a
-status_codes['NT_STATUS_INVALID_PRIMARY_GROUP'] = 0xc000005b
-status_codes['NT_STATUS_NO_IMPERSONATION_TOKEN'] = 0xc000005c
-status_codes['NT_STATUS_CANT_DISABLE_MANDATORY'] = 0xc000005d
-status_codes['NT_STATUS_NO_LOGON_SERVERS'] = 0xc000005e
-status_codes['NT_STATUS_NO_SUCH_LOGON_SESSION'] = 0xc000005f
-status_codes['NT_STATUS_NO_SUCH_PRIVILEGE'] = 0xc0000060
-status_codes['NT_STATUS_PRIVILEGE_NOT_HELD'] = 0xc0000061
-status_codes['NT_STATUS_INVALID_ACCOUNT_NAME'] = 0xc0000062
-status_codes['NT_STATUS_USER_EXISTS'] = 0xc0000063
-status_codes['NT_STATUS_NO_SUCH_USER'] = 0xc0000064
-status_codes['NT_STATUS_GROUP_EXISTS'] = 0xc0000065
-status_codes['NT_STATUS_NO_SUCH_GROUP'] = 0xc0000066
-status_codes['NT_STATUS_MEMBER_IN_GROUP'] = 0xc0000067
-status_codes['NT_STATUS_MEMBER_NOT_IN_GROUP'] = 0xc0000068
-status_codes['NT_STATUS_LAST_ADMIN'] = 0xc0000069
-status_codes['NT_STATUS_WRONG_PASSWORD'] = 0xc000006a
-status_codes['NT_STATUS_ILL_FORMED_PASSWORD'] = 0xc000006b
-status_codes['NT_STATUS_PASSWORD_RESTRICTION'] = 0xc000006c
-status_codes['NT_STATUS_LOGON_FAILURE'] = 0xc000006d
-status_codes['NT_STATUS_ACCOUNT_RESTRICTION'] = 0xc000006e
-status_codes['NT_STATUS_INVALID_LOGON_HOURS'] = 0xc000006f
-status_codes['NT_STATUS_INVALID_WORKSTATION'] = 0xc0000070
-status_codes['NT_STATUS_PASSWORD_EXPIRED'] = 0xc0000071
-status_codes['NT_STATUS_ACCOUNT_DISABLED'] = 0xc0000072
-status_codes['NT_STATUS_NONE_MAPPED'] = 0xc0000073
-status_codes['NT_STATUS_TOO_MANY_LUIDS_REQUESTED'] = 0xc0000074
-status_codes['NT_STATUS_LUIDS_EXHAUSTED'] = 0xc0000075
-status_codes['NT_STATUS_INVALID_SUB_AUTHORITY'] = 0xc0000076
-status_codes['NT_STATUS_INVALID_ACL'] = 0xc0000077
-status_codes['NT_STATUS_INVALID_SID'] = 0xc0000078
-status_codes['NT_STATUS_INVALID_SECURITY_DESCR'] = 0xc0000079
-status_codes['NT_STATUS_PROCEDURE_NOT_FOUND'] = 0xc000007a
-status_codes['NT_STATUS_INVALID_IMAGE_FORMAT'] = 0xc000007b
-status_codes['NT_STATUS_NO_TOKEN'] = 0xc000007c
-status_codes['NT_STATUS_BAD_INHERITANCE_ACL'] = 0xc000007d
-status_codes['NT_STATUS_RANGE_NOT_LOCKED'] = 0xc000007e
-status_codes['NT_STATUS_DISK_FULL'] = 0xc000007f
-status_codes['NT_STATUS_SERVER_DISABLED'] = 0xc0000080
-status_codes['NT_STATUS_SERVER_NOT_DISABLED'] = 0xc0000081
-status_codes['NT_STATUS_TOO_MANY_GUIDS_REQUESTED'] = 0xc0000082
-status_codes['NT_STATUS_GUIDS_EXHAUSTED'] = 0xc0000083
-status_codes['NT_STATUS_INVALID_ID_AUTHORITY'] = 0xc0000084
-status_codes['NT_STATUS_AGENTS_EXHAUSTED'] = 0xc0000085
-status_codes['NT_STATUS_INVALID_VOLUME_LABEL'] = 0xc0000086
-status_codes['NT_STATUS_SECTION_NOT_EXTENDED'] = 0xc0000087
-status_codes['NT_STATUS_NOT_MAPPED_DATA'] = 0xc0000088
-status_codes['NT_STATUS_RESOURCE_DATA_NOT_FOUND'] = 0xc0000089
-status_codes['NT_STATUS_RESOURCE_TYPE_NOT_FOUND'] = 0xc000008a
-status_codes['NT_STATUS_RESOURCE_NAME_NOT_FOUND'] = 0xc000008b
-status_codes['NT_STATUS_ARRAY_BOUNDS_EXCEEDED'] = 0xc000008c
-status_codes['NT_STATUS_FLOAT_DENORMAL_OPERAND'] = 0xc000008d
-status_codes['NT_STATUS_FLOAT_DIVIDE_BY_ZERO'] = 0xc000008e
-status_codes['NT_STATUS_FLOAT_INEXACT_RESULT'] = 0xc000008f
-status_codes['NT_STATUS_FLOAT_INVALID_OPERATION'] = 0xc0000090
-status_codes['NT_STATUS_FLOAT_OVERFLOW'] = 0xc0000091
-status_codes['NT_STATUS_FLOAT_STACK_CHECK'] = 0xc0000092
-status_codes['NT_STATUS_FLOAT_UNDERFLOW'] = 0xc0000093
-status_codes['NT_STATUS_INTEGER_DIVIDE_BY_ZERO'] = 0xc0000094
-status_codes['NT_STATUS_INTEGER_OVERFLOW'] = 0xc0000095
-status_codes['NT_STATUS_PRIVILEGED_INSTRUCTION'] = 0xc0000096
-status_codes['NT_STATUS_TOO_MANY_PAGING_FILES'] = 0xc0000097
-status_codes['NT_STATUS_FILE_INVALID'] = 0xc0000098
-status_codes['NT_STATUS_ALLOTTED_SPACE_EXCEEDED'] = 0xc0000099
-status_codes['NT_STATUS_INSUFFICIENT_RESOURCES'] = 0xc000009a
-status_codes['NT_STATUS_DFS_EXIT_PATH_FOUND'] = 0xc000009b
-status_codes['NT_STATUS_DEVICE_DATA_ERROR'] = 0xc000009c
-status_codes['NT_STATUS_DEVICE_NOT_CONNECTED'] = 0xc000009d
-status_codes['NT_STATUS_DEVICE_POWER_FAILURE'] = 0xc000009e
-status_codes['NT_STATUS_FREE_VM_NOT_AT_BASE'] = 0xc000009f
-status_codes['NT_STATUS_MEMORY_NOT_ALLOCATED'] = 0xc00000a0
-status_codes['NT_STATUS_WORKING_SET_QUOTA'] = 0xc00000a1
-status_codes['NT_STATUS_MEDIA_WRITE_PROTECTED'] = 0xc00000a2
-status_codes['NT_STATUS_DEVICE_NOT_READY'] = 0xc00000a3
-status_codes['NT_STATUS_INVALID_GROUP_ATTRIBUTES'] = 0xc00000a4
-status_codes['NT_STATUS_BAD_IMPERSONATION_LEVEL'] = 0xc00000a5
-status_codes['NT_STATUS_CANT_OPEN_ANONYMOUS'] = 0xc00000a6
-status_codes['NT_STATUS_BAD_VALIDATION_CLASS'] = 0xc00000a7
-status_codes['NT_STATUS_BAD_TOKEN_TYPE'] = 0xc00000a8
-status_codes['NT_STATUS_BAD_MASTER_BOOT_RECORD'] = 0xc00000a9
-status_codes['NT_STATUS_INSTRUCTION_MISALIGNMENT'] = 0xc00000aa
-status_codes['NT_STATUS_INSTANCE_NOT_AVAILABLE'] = 0xc00000ab
-status_codes['NT_STATUS_PIPE_NOT_AVAILABLE'] = 0xc00000ac
-status_codes['NT_STATUS_INVALID_PIPE_STATE'] = 0xc00000ad
-status_codes['NT_STATUS_PIPE_BUSY'] = 0xc00000ae
-status_codes['NT_STATUS_ILLEGAL_FUNCTION'] = 0xc00000af
-status_codes['NT_STATUS_PIPE_DISCONNECTED'] = 0xc00000b0
-status_codes['NT_STATUS_PIPE_CLOSING'] = 0xc00000b1
-status_codes['NT_STATUS_PIPE_CONNECTED'] = 0xc00000b2
-status_codes['NT_STATUS_PIPE_LISTENING'] = 0xc00000b3
-status_codes['NT_STATUS_INVALID_READ_MODE'] = 0xc00000b4
-status_codes['NT_STATUS_IO_TIMEOUT'] = 0xc00000b5
-status_codes['NT_STATUS_FILE_FORCED_CLOSED'] = 0xc00000b6
-status_codes['NT_STATUS_PROFILING_NOT_STARTED'] = 0xc00000b7
-status_codes['NT_STATUS_PROFILING_NOT_STOPPED'] = 0xc00000b8
-status_codes['NT_STATUS_COULD_NOT_INTERPRET'] = 0xc00000b9
-status_codes['NT_STATUS_FILE_IS_A_DIRECTORY'] = 0xc00000ba
-status_codes['NT_STATUS_NOT_SUPPORTED'] = 0xc00000bb
-status_codes['NT_STATUS_REMOTE_NOT_LISTENING'] = 0xc00000bc
-status_codes['NT_STATUS_DUPLICATE_NAME'] = 0xc00000bd
-status_codes['NT_STATUS_BAD_NETWORK_PATH'] = 0xc00000be
-status_codes['NT_STATUS_NETWORK_BUSY'] = 0xc00000bf
-status_codes['NT_STATUS_DEVICE_DOES_NOT_EXIST'] = 0xc00000c0
-status_codes['NT_STATUS_TOO_MANY_COMMANDS'] = 0xc00000c1
-status_codes['NT_STATUS_ADAPTER_HARDWARE_ERROR'] = 0xc00000c2
-status_codes['NT_STATUS_INVALID_NETWORK_RESPONSE'] = 0xc00000c3
-status_codes['NT_STATUS_UNEXPECTED_NETWORK_ERROR'] = 0xc00000c4
-status_codes['NT_STATUS_BAD_REMOTE_ADAPTER'] = 0xc00000c5
-status_codes['NT_STATUS_PRINT_QUEUE_FULL'] = 0xc00000c6
-status_codes['NT_STATUS_NO_SPOOL_SPACE'] = 0xc00000c7
-status_codes['NT_STATUS_PRINT_CANCELLED'] = 0xc00000c8
-status_codes['NT_STATUS_NETWORK_NAME_DELETED'] = 0xc00000c9
-status_codes['NT_STATUS_NETWORK_ACCESS_DENIED'] = 0xc00000ca
-status_codes['NT_STATUS_BAD_DEVICE_TYPE'] = 0xc00000cb
-status_codes['NT_STATUS_BAD_NETWORK_NAME'] = 0xc00000cc
-status_codes['NT_STATUS_TOO_MANY_NAMES'] = 0xc00000cd
-status_codes['NT_STATUS_TOO_MANY_SESSIONS'] = 0xc00000ce
-status_codes['NT_STATUS_SHARING_PAUSED'] = 0xc00000cf
-status_codes['NT_STATUS_REQUEST_NOT_ACCEPTED'] = 0xc00000d0
-status_codes['NT_STATUS_REDIRECTOR_PAUSED'] = 0xc00000d1
-status_codes['NT_STATUS_NET_WRITE_FAULT'] = 0xc00000d2
-status_codes['NT_STATUS_PROFILING_AT_LIMIT'] = 0xc00000d3
-status_codes['NT_STATUS_NOT_SAME_DEVICE'] = 0xc00000d4
-status_codes['NT_STATUS_FILE_RENAMED'] = 0xc00000d5
-status_codes['NT_STATUS_VIRTUAL_CIRCUIT_CLOSED'] = 0xc00000d6
-status_codes['NT_STATUS_NO_SECURITY_ON_OBJECT'] = 0xc00000d7
-status_codes['NT_STATUS_CANT_WAIT'] = 0xc00000d8
-status_codes['NT_STATUS_PIPE_EMPTY'] = 0xc00000d9
-status_codes['NT_STATUS_CANT_ACCESS_DOMAIN_INFO'] = 0xc00000da
-status_codes['NT_STATUS_CANT_TERMINATE_SELF'] = 0xc00000db
-status_codes['NT_STATUS_INVALID_SERVER_STATE'] = 0xc00000dc
-status_codes['NT_STATUS_INVALID_DOMAIN_STATE'] = 0xc00000dd
-status_codes['NT_STATUS_INVALID_DOMAIN_ROLE'] = 0xc00000de
-status_codes['NT_STATUS_NO_SUCH_DOMAIN'] = 0xc00000df
-status_codes['NT_STATUS_DOMAIN_EXISTS'] = 0xc00000e0
-status_codes['NT_STATUS_DOMAIN_LIMIT_EXCEEDED'] = 0xc00000e1
-status_codes['NT_STATUS_OPLOCK_NOT_GRANTED'] = 0xc00000e2
-status_codes['NT_STATUS_INVALID_OPLOCK_PROTOCOL'] = 0xc00000e3
-status_codes['NT_STATUS_INTERNAL_DB_CORRUPTION'] = 0xc00000e4
-status_codes['NT_STATUS_INTERNAL_ERROR'] = 0xc00000e5
-status_codes['NT_STATUS_GENERIC_NOT_MAPPED'] = 0xc00000e6
-status_codes['NT_STATUS_BAD_DESCRIPTOR_FORMAT'] = 0xc00000e7
-status_codes['NT_STATUS_INVALID_USER_BUFFER'] = 0xc00000e8
-status_codes['NT_STATUS_UNEXPECTED_IO_ERROR'] = 0xc00000e9
-status_codes['NT_STATUS_UNEXPECTED_MM_CREATE_ERR'] = 0xc00000ea
-status_codes['NT_STATUS_UNEXPECTED_MM_MAP_ERROR'] = 0xc00000eb
-status_codes['NT_STATUS_UNEXPECTED_MM_EXTEND_ERR'] = 0xc00000ec
-status_codes['NT_STATUS_NOT_LOGON_PROCESS'] = 0xc00000ed
-status_codes['NT_STATUS_LOGON_SESSION_EXISTS'] = 0xc00000ee
-status_codes['NT_STATUS_INVALID_PARAMETER_1'] = 0xc00000ef
-status_codes['NT_STATUS_INVALID_PARAMETER_2'] = 0xc00000f0
-status_codes['NT_STATUS_INVALID_PARAMETER_3'] = 0xc00000f1
-status_codes['NT_STATUS_INVALID_PARAMETER_4'] = 0xc00000f2
-status_codes['NT_STATUS_INVALID_PARAMETER_5'] = 0xc00000f3
-status_codes['NT_STATUS_INVALID_PARAMETER_6'] = 0xc00000f4
-status_codes['NT_STATUS_INVALID_PARAMETER_7'] = 0xc00000f5
-status_codes['NT_STATUS_INVALID_PARAMETER_8'] = 0xc00000f6
-status_codes['NT_STATUS_INVALID_PARAMETER_9'] = 0xc00000f7
-status_codes['NT_STATUS_INVALID_PARAMETER_10'] = 0xc00000f8
-status_codes['NT_STATUS_INVALID_PARAMETER_11'] = 0xc00000f9
-status_codes['NT_STATUS_INVALID_PARAMETER_12'] = 0xc00000fa
-status_codes['NT_STATUS_REDIRECTOR_NOT_STARTED'] = 0xc00000fb
-status_codes['NT_STATUS_REDIRECTOR_STARTED'] = 0xc00000fc
-status_codes['NT_STATUS_STACK_OVERFLOW'] = 0xc00000fd
-status_codes['NT_STATUS_NO_SUCH_PACKAGE'] = 0xc00000fe
-status_codes['NT_STATUS_BAD_FUNCTION_TABLE'] = 0xc00000ff
-status_codes['NT_STATUS_DIRECTORY_NOT_EMPTY'] = 0xc0000101
-status_codes['NT_STATUS_FILE_CORRUPT_ERROR'] = 0xc0000102
-status_codes['NT_STATUS_NOT_A_DIRECTORY'] = 0xc0000103
-status_codes['NT_STATUS_BAD_LOGON_SESSION_STATE'] = 0xc0000104
-status_codes['NT_STATUS_LOGON_SESSION_COLLISION'] = 0xc0000105
-status_codes['NT_STATUS_NAME_TOO_LONG'] = 0xc0000106
-status_codes['NT_STATUS_FILES_OPEN'] = 0xc0000107
-status_codes['NT_STATUS_CONNECTION_IN_USE'] = 0xc0000108
-status_codes['NT_STATUS_MESSAGE_NOT_FOUND'] = 0xc0000109
-status_codes['NT_STATUS_PROCESS_IS_TERMINATING'] = 0xc000010a
-status_codes['NT_STATUS_INVALID_LOGON_TYPE'] = 0xc000010b
-status_codes['NT_STATUS_NO_GUID_TRANSLATION'] = 0xc000010c
-status_codes['NT_STATUS_CANNOT_IMPERSONATE'] = 0xc000010d
-status_codes['NT_STATUS_IMAGE_ALREADY_LOADED'] = 0xc000010e
-status_codes['NT_STATUS_ABIOS_NOT_PRESENT'] = 0xc000010f
-status_codes['NT_STATUS_ABIOS_LID_NOT_EXIST'] = 0xc0000110
-status_codes['NT_STATUS_ABIOS_LID_ALREADY_OWNED'] = 0xc0000111
-status_codes['NT_STATUS_ABIOS_NOT_LID_OWNER'] = 0xc0000112
-status_codes['NT_STATUS_ABIOS_INVALID_COMMAND'] = 0xc0000113
-status_codes['NT_STATUS_ABIOS_INVALID_LID'] = 0xc0000114
-status_codes['NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE'] = 0xc0000115
-status_codes['NT_STATUS_ABIOS_INVALID_SELECTOR'] = 0xc0000116
-status_codes['NT_STATUS_NO_LDT'] = 0xc0000117
-status_codes['NT_STATUS_INVALID_LDT_SIZE'] = 0xc0000118
-status_codes['NT_STATUS_INVALID_LDT_OFFSET'] = 0xc0000119
-status_codes['NT_STATUS_INVALID_LDT_DESCRIPTOR'] = 0xc000011a
-status_codes['NT_STATUS_INVALID_IMAGE_NE_FORMAT'] = 0xc000011b
-status_codes['NT_STATUS_RXACT_INVALID_STATE'] = 0xc000011c
-status_codes['NT_STATUS_RXACT_COMMIT_FAILURE'] = 0xc000011d
-status_codes['NT_STATUS_MAPPED_FILE_SIZE_ZERO'] = 0xc000011e
-status_codes['NT_STATUS_TOO_MANY_OPENED_FILES'] = 0xc000011f
-status_codes['NT_STATUS_CANCELLED'] = 0xc0000120
-status_codes['NT_STATUS_CANNOT_DELETE'] = 0xc0000121
-status_codes['NT_STATUS_INVALID_COMPUTER_NAME'] = 0xc0000122
-status_codes['NT_STATUS_FILE_DELETED'] = 0xc0000123
-status_codes['NT_STATUS_SPECIAL_ACCOUNT'] = 0xc0000124
-status_codes['NT_STATUS_SPECIAL_GROUP'] = 0xc0000125
-status_codes['NT_STATUS_SPECIAL_USER'] = 0xc0000126
-status_codes['NT_STATUS_MEMBERS_PRIMARY_GROUP'] = 0xc0000127
-status_codes['NT_STATUS_FILE_CLOSED'] = 0xc0000128
-status_codes['NT_STATUS_TOO_MANY_THREADS'] = 0xc0000129
-status_codes['NT_STATUS_THREAD_NOT_IN_PROCESS'] = 0xc000012a
-status_codes['NT_STATUS_TOKEN_ALREADY_IN_USE'] = 0xc000012b
-status_codes['NT_STATUS_PAGEFILE_QUOTA_EXCEEDED'] = 0xc000012c
-status_codes['NT_STATUS_COMMITMENT_LIMIT'] = 0xc000012d
-status_codes['NT_STATUS_INVALID_IMAGE_LE_FORMAT'] = 0xc000012e
-status_codes['NT_STATUS_INVALID_IMAGE_NOT_MZ'] = 0xc000012f
-status_codes['NT_STATUS_INVALID_IMAGE_PROTECT'] = 0xc0000130
-status_codes['NT_STATUS_INVALID_IMAGE_WIN_16'] = 0xc0000131
-status_codes['NT_STATUS_LOGON_SERVER_CONFLICT'] = 0xc0000132
-status_codes['NT_STATUS_TIME_DIFFERENCE_AT_DC'] = 0xc0000133
-status_codes['NT_STATUS_SYNCHRONIZATION_REQUIRED'] = 0xc0000134
-status_codes['NT_STATUS_DLL_NOT_FOUND'] = 0xc0000135
-status_codes['NT_STATUS_OPEN_FAILED'] = 0xc0000136
-status_codes['NT_STATUS_IO_PRIVILEGE_FAILED'] = 0xc0000137
-status_codes['NT_STATUS_ORDINAL_NOT_FOUND'] = 0xc0000138
-status_codes['NT_STATUS_ENTRYPOINT_NOT_FOUND'] = 0xc0000139
-status_codes['NT_STATUS_CONTROL_C_EXIT'] = 0xc000013a
-status_codes['NT_STATUS_LOCAL_DISCONNECT'] = 0xc000013b
-status_codes['NT_STATUS_REMOTE_DISCONNECT'] = 0xc000013c
-status_codes['NT_STATUS_REMOTE_RESOURCES'] = 0xc000013d
-status_codes['NT_STATUS_LINK_FAILED'] = 0xc000013e
-status_codes['NT_STATUS_LINK_TIMEOUT'] = 0xc000013f
-status_codes['NT_STATUS_INVALID_CONNECTION'] = 0xc0000140
-status_codes['NT_STATUS_INVALID_ADDRESS'] = 0xc0000141
-status_codes['NT_STATUS_DLL_INIT_FAILED'] = 0xc0000142
-status_codes['NT_STATUS_MISSING_SYSTEMFILE'] = 0xc0000143
-status_codes['NT_STATUS_UNHANDLED_EXCEPTION'] = 0xc0000144
-status_codes['NT_STATUS_APP_INIT_FAILURE'] = 0xc0000145
-status_codes['NT_STATUS_PAGEFILE_CREATE_FAILED'] = 0xc0000146
-status_codes['NT_STATUS_NO_PAGEFILE'] = 0xc0000147
-status_codes['NT_STATUS_INVALID_LEVEL'] = 0xc0000148
-status_codes['NT_STATUS_WRONG_PASSWORD_CORE'] = 0xc0000149
-status_codes['NT_STATUS_ILLEGAL_FLOAT_CONTEXT'] = 0xc000014a
-status_codes['NT_STATUS_PIPE_BROKEN'] = 0xc000014b
-status_codes['NT_STATUS_REGISTRY_CORRUPT'] = 0xc000014c
-status_codes['NT_STATUS_REGISTRY_IO_FAILED'] = 0xc000014d
-status_codes['NT_STATUS_NO_EVENT_PAIR'] = 0xc000014e
-status_codes['NT_STATUS_UNRECOGNIZED_VOLUME'] = 0xc000014f
-status_codes['NT_STATUS_SERIAL_NO_DEVICE_INITED'] = 0xc0000150
-status_codes['NT_STATUS_NO_SUCH_ALIAS'] = 0xc0000151
-status_codes['NT_STATUS_MEMBER_NOT_IN_ALIAS'] = 0xc0000152
-status_codes['NT_STATUS_MEMBER_IN_ALIAS'] = 0xc0000153
-status_codes['NT_STATUS_ALIAS_EXISTS'] = 0xc0000154
-status_codes['NT_STATUS_LOGON_NOT_GRANTED'] = 0xc0000155
-status_codes['NT_STATUS_TOO_MANY_SECRETS'] = 0xc0000156
-status_codes['NT_STATUS_SECRET_TOO_LONG'] = 0xc0000157
-status_codes['NT_STATUS_INTERNAL_DB_ERROR'] = 0xc0000158
-status_codes['NT_STATUS_FULLSCREEN_MODE'] = 0xc0000159
-status_codes['NT_STATUS_TOO_MANY_CONTEXT_IDS'] = 0xc000015a
-status_codes['NT_STATUS_LOGON_TYPE_NOT_GRANTED'] = 0xc000015b
-status_codes['NT_STATUS_NOT_REGISTRY_FILE'] = 0xc000015c
-status_codes['NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED'] = 0xc000015d
-status_codes['NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR'] = 0xc000015e
-status_codes['NT_STATUS_FT_MISSING_MEMBER'] = 0xc000015f
-status_codes['NT_STATUS_ILL_FORMED_SERVICE_ENTRY'] = 0xc0000160
-status_codes['NT_STATUS_ILLEGAL_CHARACTER'] = 0xc0000161
-status_codes['NT_STATUS_UNMAPPABLE_CHARACTER'] = 0xc0000162
-status_codes['NT_STATUS_UNDEFINED_CHARACTER'] = 0xc0000163
-status_codes['NT_STATUS_FLOPPY_VOLUME'] = 0xc0000164
-status_codes['NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND'] = 0xc0000165
-status_codes['NT_STATUS_FLOPPY_WRONG_CYLINDER'] = 0xc0000166
-status_codes['NT_STATUS_FLOPPY_UNKNOWN_ERROR'] = 0xc0000167
-status_codes['NT_STATUS_FLOPPY_BAD_REGISTERS'] = 0xc0000168
-status_codes['NT_STATUS_DISK_RECALIBRATE_FAILED'] = 0xc0000169
-status_codes['NT_STATUS_DISK_OPERATION_FAILED'] = 0xc000016a
-status_codes['NT_STATUS_DISK_RESET_FAILED'] = 0xc000016b
-status_codes['NT_STATUS_SHARED_IRQ_BUSY'] = 0xc000016c
-status_codes['NT_STATUS_FT_ORPHANING'] = 0xc000016d
-status_codes['NT_STATUS_PARTITION_FAILURE'] = 0xc0000172
-status_codes['NT_STATUS_INVALID_BLOCK_LENGTH'] = 0xc0000173
-status_codes['NT_STATUS_DEVICE_NOT_PARTITIONED'] = 0xc0000174
-status_codes['NT_STATUS_UNABLE_TO_LOCK_MEDIA'] = 0xc0000175
-status_codes['NT_STATUS_UNABLE_TO_UNLOAD_MEDIA'] = 0xc0000176
-status_codes['NT_STATUS_EOM_OVERFLOW'] = 0xc0000177
-status_codes['NT_STATUS_NO_MEDIA'] = 0xc0000178
-status_codes['NT_STATUS_NO_SUCH_MEMBER'] = 0xc000017a
-status_codes['NT_STATUS_INVALID_MEMBER'] = 0xc000017b
-status_codes['NT_STATUS_KEY_DELETED'] = 0xc000017c
-status_codes['NT_STATUS_NO_LOG_SPACE'] = 0xc000017d
-status_codes['NT_STATUS_TOO_MANY_SIDS'] = 0xc000017e
-status_codes['NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED'] = 0xc000017f
-status_codes['NT_STATUS_KEY_HAS_CHILDREN'] = 0xc0000180
-status_codes['NT_STATUS_CHILD_MUST_BE_VOLATILE'] = 0xc0000181
-status_codes['NT_STATUS_DEVICE_CONFIGURATION_ERROR'] = 0xc0000182
-status_codes['NT_STATUS_DRIVER_INTERNAL_ERROR'] = 0xc0000183
-status_codes['NT_STATUS_INVALID_DEVICE_STATE'] = 0xc0000184
-status_codes['NT_STATUS_IO_DEVICE_ERROR'] = 0xc0000185
-status_codes['NT_STATUS_DEVICE_PROTOCOL_ERROR'] = 0xc0000186
-status_codes['NT_STATUS_BACKUP_CONTROLLER'] = 0xc0000187
-status_codes['NT_STATUS_LOG_FILE_FULL'] = 0xc0000188
-status_codes['NT_STATUS_TOO_LATE'] = 0xc0000189
-status_codes['NT_STATUS_NO_TRUST_LSA_SECRET'] = 0xc000018a
-status_codes['NT_STATUS_NO_TRUST_SAM_ACCOUNT'] = 0xc000018b
-status_codes['NT_STATUS_TRUSTED_DOMAIN_FAILURE'] = 0xc000018c
-status_codes['NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE'] = 0xc000018d
-status_codes['NT_STATUS_EVENTLOG_FILE_CORRUPT'] = 0xc000018e
-status_codes['NT_STATUS_EVENTLOG_CANT_START'] = 0xc000018f
-status_codes['NT_STATUS_TRUST_FAILURE'] = 0xc0000190
-status_codes['NT_STATUS_MUTANT_LIMIT_EXCEEDED'] = 0xc0000191
-status_codes['NT_STATUS_NETLOGON_NOT_STARTED'] = 0xc0000192
-status_codes['NT_STATUS_ACCOUNT_EXPIRED'] = 0xc0000193
-status_codes['NT_STATUS_POSSIBLE_DEADLOCK'] = 0xc0000194
-status_codes['NT_STATUS_NETWORK_CREDENTIAL_CONFLICT'] = 0xc0000195
-status_codes['NT_STATUS_REMOTE_SESSION_LIMIT'] = 0xc0000196
-status_codes['NT_STATUS_EVENTLOG_FILE_CHANGED'] = 0xc0000197
-status_codes['NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT'] = 0xc0000198
-status_codes['NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT'] = 0xc0000199
-status_codes['NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT'] = 0xc000019a
-status_codes['NT_STATUS_DOMAIN_TRUST_INCONSISTENT'] = 0xc000019b
-status_codes['NT_STATUS_FS_DRIVER_REQUIRED'] = 0xc000019c
-status_codes['NT_STATUS_NO_USER_SESSION_KEY'] = 0xc0000202
-status_codes['NT_STATUS_USER_SESSION_DELETED'] = 0xc0000203
-status_codes['NT_STATUS_RESOURCE_LANG_NOT_FOUND'] = 0xc0000204
-status_codes['NT_STATUS_INSUFF_SERVER_RESOURCES'] = 0xc0000205
-status_codes['NT_STATUS_INVALID_BUFFER_SIZE'] = 0xc0000206
-status_codes['NT_STATUS_INVALID_ADDRESS_COMPONENT'] = 0xc0000207
-status_codes['NT_STATUS_INVALID_ADDRESS_WILDCARD'] = 0xc0000208
-status_codes['NT_STATUS_TOO_MANY_ADDRESSES'] = 0xc0000209
-status_codes['NT_STATUS_ADDRESS_ALREADY_EXISTS'] = 0xc000020a
-status_codes['NT_STATUS_ADDRESS_CLOSED'] = 0xc000020b
-status_codes['NT_STATUS_CONNECTION_DISCONNECTED'] = 0xc000020c
-status_codes['NT_STATUS_CONNECTION_RESET'] = 0xc000020d
-status_codes['NT_STATUS_TOO_MANY_NODES'] = 0xc000020e
-status_codes['NT_STATUS_TRANSACTION_ABORTED'] = 0xc000020f
-status_codes['NT_STATUS_TRANSACTION_TIMED_OUT'] = 0xc0000210
-status_codes['NT_STATUS_TRANSACTION_NO_RELEASE'] = 0xc0000211
-status_codes['NT_STATUS_TRANSACTION_NO_MATCH'] = 0xc0000212
-status_codes['NT_STATUS_TRANSACTION_RESPONDED'] = 0xc0000213
-status_codes['NT_STATUS_TRANSACTION_INVALID_ID'] = 0xc0000214
-status_codes['NT_STATUS_TRANSACTION_INVALID_TYPE'] = 0xc0000215
-status_codes['NT_STATUS_NOT_SERVER_SESSION'] = 0xc0000216
-status_codes['NT_STATUS_NOT_CLIENT_SESSION'] = 0xc0000217
-status_codes['NT_STATUS_CANNOT_LOAD_REGISTRY_FILE'] = 0xc0000218
-status_codes['NT_STATUS_DEBUG_ATTACH_FAILED'] = 0xc0000219
-status_codes['NT_STATUS_SYSTEM_PROCESS_TERMINATED'] = 0xc000021a
-status_codes['NT_STATUS_DATA_NOT_ACCEPTED'] = 0xc000021b
-status_codes['NT_STATUS_NO_BROWSER_SERVERS_FOUND'] = 0xc000021c
-status_codes['NT_STATUS_VDM_HARD_ERROR'] = 0xc000021d
-status_codes['NT_STATUS_DRIVER_CANCEL_TIMEOUT'] = 0xc000021e
-status_codes['NT_STATUS_REPLY_MESSAGE_MISMATCH'] = 0xc000021f
-status_codes['NT_STATUS_MAPPED_ALIGNMENT'] = 0xc0000220
-status_codes['NT_STATUS_IMAGE_CHECKSUM_MISMATCH'] = 0xc0000221
-status_codes['NT_STATUS_LOST_WRITEBEHIND_DATA'] = 0xc0000222
-status_codes['NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID'] = 0xc0000223
-status_codes['NT_STATUS_PASSWORD_MUST_CHANGE'] = 0xc0000224
-status_codes['NT_STATUS_NOT_FOUND'] = 0xc0000225
-status_codes['NT_STATUS_NOT_TINY_STREAM'] = 0xc0000226
-status_codes['NT_STATUS_RECOVERY_FAILURE'] = 0xc0000227
-status_codes['NT_STATUS_STACK_OVERFLOW_READ'] = 0xc0000228
-status_codes['NT_STATUS_FAIL_CHECK'] = 0xc0000229
-status_codes['NT_STATUS_DUPLICATE_OBJECTID'] = 0xc000022a
-status_codes['NT_STATUS_OBJECTID_EXISTS'] = 0xc000022b
-status_codes['NT_STATUS_CONVERT_TO_LARGE'] = 0xc000022c
-status_codes['NT_STATUS_RETRY'] = 0xc000022d
-status_codes['NT_STATUS_FOUND_OUT_OF_SCOPE'] = 0xc000022e
-status_codes['NT_STATUS_ALLOCATE_BUCKET'] = 0xc000022f
-status_codes['NT_STATUS_PROPSET_NOT_FOUND'] = 0xc0000230
-status_codes['NT_STATUS_MARSHALL_OVERFLOW'] = 0xc0000231
-status_codes['NT_STATUS_INVALID_VARIANT'] = 0xc0000232
-status_codes['NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND'] = 0xc0000233
-status_codes['NT_STATUS_ACCOUNT_LOCKED_OUT'] = 0xc0000234
-status_codes['NT_STATUS_HANDLE_NOT_CLOSABLE'] = 0xc0000235
-status_codes['NT_STATUS_CONNECTION_REFUSED'] = 0xc0000236
-status_codes['NT_STATUS_GRACEFUL_DISCONNECT'] = 0xc0000237
-status_codes['NT_STATUS_ADDRESS_ALREADY_ASSOCIATED'] = 0xc0000238
-status_codes['NT_STATUS_ADDRESS_NOT_ASSOCIATED'] = 0xc0000239
-status_codes['NT_STATUS_CONNECTION_INVALID'] = 0xc000023a
-status_codes['NT_STATUS_CONNECTION_ACTIVE'] = 0xc000023b
-status_codes['NT_STATUS_NETWORK_UNREACHABLE'] = 0xc000023c
-status_codes['NT_STATUS_HOST_UNREACHABLE'] = 0xc000023d
-status_codes['NT_STATUS_PROTOCOL_UNREACHABLE'] = 0xc000023e
-status_codes['NT_STATUS_PORT_UNREACHABLE'] = 0xc000023f
-status_codes['NT_STATUS_REQUEST_ABORTED'] = 0xc0000240
-status_codes['NT_STATUS_CONNECTION_ABORTED'] = 0xc0000241
-status_codes['NT_STATUS_BAD_COMPRESSION_BUFFER'] = 0xc0000242
-status_codes['NT_STATUS_USER_MAPPED_FILE'] = 0xc0000243
-status_codes['NT_STATUS_AUDIT_FAILED'] = 0xc0000244
-status_codes['NT_STATUS_TIMER_RESOLUTION_NOT_SET'] = 0xc0000245
-status_codes['NT_STATUS_CONNECTION_COUNT_LIMIT'] = 0xc0000246
-status_codes['NT_STATUS_LOGIN_TIME_RESTRICTION'] = 0xc0000247
-status_codes['NT_STATUS_LOGIN_WKSTA_RESTRICTION'] = 0xc0000248
-status_codes['NT_STATUS_IMAGE_MP_UP_MISMATCH'] = 0xc0000249
-status_codes['NT_STATUS_INSUFFICIENT_LOGON_INFO'] = 0xc0000250
-status_codes['NT_STATUS_BAD_DLL_ENTRYPOINT'] = 0xc0000251
-status_codes['NT_STATUS_BAD_SERVICE_ENTRYPOINT'] = 0xc0000252
-status_codes['NT_STATUS_LPC_REPLY_LOST'] = 0xc0000253
-status_codes['NT_STATUS_IP_ADDRESS_CONFLICT1'] = 0xc0000254
-status_codes['NT_STATUS_IP_ADDRESS_CONFLICT2'] = 0xc0000255
-status_codes['NT_STATUS_REGISTRY_QUOTA_LIMIT'] = 0xc0000256
-status_codes['NT_STATUS_PATH_NOT_COVERED'] = 0xc0000257
-status_codes['NT_STATUS_NO_CALLBACK_ACTIVE'] = 0xc0000258
-status_codes['NT_STATUS_LICENSE_QUOTA_EXCEEDED'] = 0xc0000259
-status_codes['NT_STATUS_PWD_TOO_SHORT'] = 0xc000025a
-status_codes['NT_STATUS_PWD_TOO_RECENT'] = 0xc000025b
-status_codes['NT_STATUS_PWD_HISTORY_CONFLICT'] = 0xc000025c
-status_codes['NT_STATUS_PLUGPLAY_NO_DEVICE'] = 0xc000025e
-status_codes['NT_STATUS_UNSUPPORTED_COMPRESSION'] = 0xc000025f
-status_codes['NT_STATUS_INVALID_HW_PROFILE'] = 0xc0000260
-status_codes['NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH'] = 0xc0000261
-status_codes['NT_STATUS_DRIVER_ORDINAL_NOT_FOUND'] = 0xc0000262
-status_codes['NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND'] = 0xc0000263
-status_codes['NT_STATUS_RESOURCE_NOT_OWNED'] = 0xc0000264
-status_codes['NT_STATUS_TOO_MANY_LINKS'] = 0xc0000265
-status_codes['NT_STATUS_QUOTA_LIST_INCONSISTENT'] = 0xc0000266
-status_codes['NT_STATUS_FILE_IS_OFFLINE'] = 0xc0000267
-status_codes['NT_STATUS_DS_NO_MORE_RIDS'] = 0xc00002a8
-status_codes['NT_STATUS_NOT_A_REPARSE_POINT'] = 0xc0000275
-status_codes['NT_STATUS_NO_SUCH_JOB'] = 0xc000EDE
-for i, v in pairs(status_codes) do
-       status_names[v] = i
-end
-local function get_status_name(status)
-       if(status_names[status] == nil) then
                return string.format("NT_STATUS_UNKNOWN (0x%08x)", status)
        else
                return status_names[status]
@@ -675,7 +172,6 @@
 end
 
 
-
 --- Determines whether or not SMB checks are possible on this host, and, if they are, 
 --  which port is best to use. This is how it decides:\n
 --\n
@@ -710,12 +206,17 @@
 --  SMB). 
 --
 -- @param host The host object
--- @return (status, socket) if the status is true, result is the newly crated socket. 
+-- @return (status, smb) if the status is true, result is the newly crated smb object; 
 --         otherwise, socket is the error message. 
 function start(host)
        local port = get_port(host)
        local status, result
+       local state = {}
 
+       state['uid'] = 0
+       state['tid'] = 0
+       state['ip']  = host.ip
+
        if(port == nil) then
                return false, "Couldn't find a valid port to check"
        end
@@ -725,23 +226,25 @@
        stdnse.print_debug(3, "SMB: Mutex lock obtained")
 
        if(port == 445) then
-               status, result = start_raw(host, port)
+               status, state['socket'] = start_raw(host, port)
+               state['port'] = 445
                if(status == false) then
                        stdnse.print_debug(3, "SMB: Attempting to release SMB mutex (1)")
                        mutex "done"
                        stdnse.print_debug(3, "SMB: SMB mutex released (1)")
                end
-       
-               return status, result
+
+               return status, state
        elseif(port == 139) then
-               status, result = start_netbios(host, port)
+               status, state['socket'] = start_netbios(host, port)
+               state['port'] = 139
                if(status == false) then
                        stdnse.print_debug(3, "SMB: Attempting to release SMB mutex (2)")
                        mutex "done"
                        stdnse.print_debug(3, "SMB: SMB mutex released (2)")
                end
 
-               return status, result
+               return status, state
        end
 
        stdnse.print_debug(3, "SMB: Attempting to release SMB mutex (3)")
@@ -754,23 +257,20 @@
 --- Kills the SMB connection, closes the socket, and releases the mutex. Because of the mutex 
 --  being released, a script HAS to call stop() before it exits, no matter why it's exiting! 
 --
---  In addition to killing the connection, this function can log off the user and disconnect
---  a tree. To do so, the appropriate parameters are passed. For a logoff, the uid is required. 
---  For a tree disconnect, both tid and uid are required. 
+--  In addition to killing the connection, this function will log off the user and disconnect
+--  the connected tree, if possible.
 --
---@param socket The socket associated with the connection. 
---@param uid    [optional] If given, will do a logoff before disconnecting. 
---@param tid    [optional] If given, will do a tree disconnect before disconnecting. 
+--@param smb    The SMB object associated with the connection
 --@return (status, result) If status is false, result is an error message. Otherwise, result
 --        is undefined. 
-function stop(socket, uid, tid) 
+function stop(smb) 
 
-       if(tid ~= nil and uid ~= nil) then
-               tree_disconnect(socket, uid, tid)
+       if(smb['tid'] ~= 0) then
+               tree_disconnect(smb)
        end
 
-       if(uid ~= nil) then
-               logoff(socket, uid)
+       if(smb['uid'] ~= 0) then
+               logoff(smb)
        end
 
        stdnse.print_debug(3, "SMB: Attempting to release SMB mutex (4)")
@@ -778,8 +278,8 @@
        stdnse.print_debug(3, "SMB: SMB mutex released (4)")
 
        stdnse.print_debug(2, "Closing SMB socket")
-       if(socket ~= nil) then
-               local status, err = socket:close()
+       if(smb['socket'] ~= nil) then
+               local status, err = smb['socket']:close()
 
                if(status == false) then
                        return false, "Failed to close socket: " .. err
@@ -791,7 +291,6 @@
 
 --- Begins a raw SMB session, likely over port 445. Since nothing extra is required, this
 --  function simply makes a connection and returns the socket. 
---  it off to smb_start(). 
 -- 
 --@param host The host object to check. 
 --@param port The port to use (most likely 445).
@@ -811,7 +310,9 @@
 end
 
 --- This function will take a string like "a.b.c.d" and return "a", "a.b", "a.b.c", and "a.b.c.d". 
---  This is used for discovering NetBIOS names. 
+--  This is used for discovering NetBIOS names. If a NetBIOS name is unknown, the substrings of the 
+--  DNS name can be used in this way. 
+--
 --@param name The name to take apart
 --@param list [optional] If list is set, names will be added to it then returned
 --@return An array of the sub names
@@ -950,8 +451,179 @@
        return false, "Couldn't find a NetBIOS name that works for the server. Sorry!"
 end
 
+---Generate the Lanman v1 hash (LMv1). The generated hash is incredibly easy to reverse, because the input
+-- is padded or truncated to 14 characters, then split into two 7-character strings. Each of these strings
+-- are used as a key to encrypt the string, "KGS!@#$%" in DES. Because the keys are no longer than 
+-- 7-characters long, it's pretty trivial to bruteforce them. 
+--
+--@param Password the password to hash
+--@return (status, hash) If status is true, the hash is returned; otherwise, an error message is returned.
+local function lm_create_hash(password)
+       if(have_ssl ~= true) then
+               return false, "OpenSSL not present"
+       end
 
+       local str1, str2
+       local key1, key2
+       local result
 
+       -- Convert the password to uppercase
+       password = string.upper(password)
+
+       -- If password is under 14 characters, pad it to 14
+       if(#password < 14) then
+               password = password .. string.rep(string.char(0), 14 - #password)
+       end
+
+       -- Take the first and second half of the password (note that if it's longer than 14 characters, it's truncated)
+       str1 = string.sub(password, 1, 7)
+       str2 = string.sub(password, 8, 14)
+
+       -- Generate the keys
+       key1 = openssl.DES_string_to_key(str1)
+       key2 = openssl.DES_string_to_key(str2)
+
+       -- Encrypt the string "KGS!@#$%" with each half, and concatenate it
+       result = openssl.encrypt("DES", key1, nil, "KGS!@#$%") .. openssl.encrypt("DES", key2, nil, "KGS!@#$%")
+
+       return true, result
+end
+
+---Generate the NTLMv1 hash. This hash is quite a bit better than LMv1, and is far easier to generate. Basically,
+-- it's the MD4() of the Unicode password. 
+--
+--@param Password the password to hash
+--@return (status, hash) If status is true, the hash is returned; otherwise, an error message is returned.
+local function ntlm_create_hash(password)
+       if(have_ssl ~= true) then
+               return false, "OpenSSL not present"
+       end
+
+       local i
+       local unicode = ""
+
+       for i = 1, #password, 1 do
+               unicode = unicode .. bin.pack("<S", string.byte(password, i))
+       end
+
+       return true, openssl.md4(unicode)
+end
+
+---Create the Lanman response to send back to the server. To do this, the Lanman password is padded to 21 
+-- characters and split into three 7-character strings. Each of those strings is used as a key to encrypt
+-- the server challenge. The three encrypted strings are concatenated and returned. 
+--
+--@param lanman    The LMv1 hash
+--@param challenge The server's challenge. 
+--@return (status, response) If status is true, the response is returned; otherwise, an error message is returned.
+local function lm_create_response(lanman, challenge)
+       if(have_ssl ~= true) then
+               return false, "OpenSSL not present"
+       end
+
+       local str1, str2, str3
+       local key1, key2, key3
+       local result
+
+       -- Pad the hash to 21 characters
+       lanman = lanman .. string.rep(string.char(0), 21 - #lanman)
+
+       -- Take the first and second half of the password (note that if it's longer than 14 characters, it's truncated)
+       str1 = string.sub(lanman, 1,  7)
+       str2 = string.sub(lanman, 8,  14)
+       str3 = string.sub(lanman, 15, 21)
+
+       -- Generate the keys
+       key1 = openssl.DES_string_to_key(str1)
+       key2 = openssl.DES_string_to_key(str2)
+       key3 = openssl.DES_string_to_key(str3)
+
+       -- Encrypt the challenge with each key
+       result = openssl.encrypt("DES", key1, nil, challenge) .. openssl.encrypt("DES", key2, nil, challenge) .. 
openssl.encrypt("DES", key3, nil, challenge) 
+
+       return true, result
+end
+
+---Create the NTLM response to send back to the server. This is actually done the exact same way as the Lanman hash,
+-- so I call the Lanman function. 
+--
+--@param ntlm      The NTLMv1 hash
+--@param challenge The server's challenge. 
+--@return (status, response) If status is true, the response is returned; otherwise, an error message is returned.
+local function ntlm_create_response(ntlm, challenge)
+       return lm_create_response(ntlm, challenge)
+end
+
+---Create the NTLMv2 hash, which is based on the NTLMv1 hash (for easy upgrading), the username, and the domain. 
+-- Essentially, the NTLM hash is used as a HMAC-MD5 key, which is used to hash the unicode domain concatenated 
+-- with the unicode username. 
+--
+--@param ntlm     The NTLMv1 hash. 
+--@param username The username we're using. 
+--@param domain   The domain. 
+--@return (status, response) If status is true, the response is returned; otherwise, an error message is returned.
+local function ntlmv2_create_hash(ntlm, username, domain)
+       if(have_ssl ~= true) then
+               return false, "OpenSSL not present"
+       end
+
+       local unicode = ""
+
+       username = string.upper(username)
+       domain   = string.upper(domain)
+
+       for i = 1, #username, 1 do
+               unicode = unicode .. bin.pack("<S", string.byte(username, i))
+       end
+
+       for i = 1, #domain, 1 do
+               unicode = unicode .. bin.pack("<S", string.byte(domain, i))
+       end
+
+       return true, openssl.hmac("MD5", ntlm, unicode)
+end
+
+---Create the LMv2 response, which can be sent back to the server. This is identical to the NTLMv2 function, 
+-- except that it uses an 8-byte client challenge. 
+--
+-- The reason for LMv2 is a long and twisted story. Well, not really. The reason is basically that the v1 hashes
+-- are always 24-bytes, and some servers expect 24 bytes, but the NTLMv2 hash is more than 24 bytes. So, the only
+-- way to keep pass-through compatibility was to have a v2-hash that was guaranteed to be 24 bytes. So LMv1 was
+-- born -- it has a 16-byte hash followed by the 8-byte client challenge, for a total of 24 bytes. And now you've
+-- learned something
+--
+--@param ntlm      The NVLMv1 hash.
+--@param username  The username we're using. 
+--@param domain    The domain. 
+--@param challenge The server challenge. 
+--@return (status, response) If status is true, the response is returned; otherwise, an error message is returned.
+local function lmv2_create_response(ntlm, username, domain, challenge)
+       return ntlmv2_create_response(ntlm, username, domain, challenge, 8)
+end
+
+---Create the NTLMv2 response, which can be sent back to the server. This is done by using the HMAC-MD5 function 
+-- with the NTLMv2 hash as a key, and the server challenge concatenated with the client challenge for the data. 
+-- The resulting hash is concatenated with the client challenge and returned.
+--
+-- The "proper" implementation for this uses a certain structure for the client challenge, involving the time
+-- and computer name and stuff (if you don't do this, Wireshark tells you it's a malformed packet). In my tests, 
+-- however, I couldn't get Vista to recognize a client challenge longer than 24 bytes, and this structure was
+-- guaranteed to be much longer than 24 bytes. So, I just use a random string generated by OpenSSL. I've tested
+-- it on every Windows system from Windows 2000 to Windows Vista, and it has always worked. 
+local function ntlmv2_create_response(ntlm, username, domain, challenge, client_challenge_length)
+       if(have_ssl ~= true) then
+               return false, "OpenSSL not present"
+       end
+
+       local client_challenge = openssl.rand_bytes(client_challenge_length)
+       local ntlmv2_hash
+
+       status, ntlmv2_hash = ntlmv2_create_hash(ntlm, username, domain)
+
+       return true, openssl.hmac("MD5", ntlmv2_hash, challenge .. client_challenge) .. client_challenge
+end
+
+
 --- Creates a string containing a SMB packet header. The header looks like this:\n
 -- --------------------------------------------------------------------------------------------------\n
 -- | 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9  8  7  6  5  4  3  2  1  0 |\n
@@ -983,10 +655,10 @@
 --@param uid     The UserID, which is returned by SMB_COM_SESSION_SETUP_ANDX (0 otherwise)
 --@param tid     The TreeID, which is returned by SMB_COM_TREE_CONNECT_ANDX (0 otherwise)
 --@return A binary string containing the packed packet header. 
-local function smb_encode_header(command, uid, tid)
+local function smb_encode_header(smb, command)
 
        -- Used for the header
-       local smb = string.char(0xFF) .. "SMB"
+       local sig = string.char(0xFF) .. "SMB"
 
        -- Pretty much every flags is deprecated. We set these two because they're required to be on. 
        local flags  = bit.bor(0x10, 0x08) -- SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES
@@ -995,10 +667,10 @@
        local flags2 = bit.bor(0x4000, 0x0040, 0x0001) -- SMB_FLAGS2_32BIT_STATUS | SMB_FLAGS2_IS_LONG_NAME | 
SMB_FLAGS2_KNOWS_LONG_NAMES
 
        local header = bin.pack("<CCCCCICSSLSSSSS",
-                               smb:byte(1),  -- Header
-                               smb:byte(2),  -- Header
-                               smb:byte(3),  -- Header
-                               smb:byte(4),  -- Header
+                               sig:byte(1),  -- Header
+                               sig:byte(2),  -- Header
+                               sig:byte(3),  -- Header
+                               sig:byte(4),  -- Header
                                command,      -- Command
                                0,            -- status
                                flags,        -- flags
@@ -1006,9 +678,9 @@
                                0,            -- extra (pid_high)
                                0,            -- extra (signature)
                                0,            -- extra (unused)
-                               tid,          -- tid
+                               smb['tid'],   -- tid
                                0,            -- pid
-                               uid,          -- uid
+                               smb['uid'],   -- uid
                                0             -- mid
                        )
 
@@ -1043,20 +715,20 @@
 --  in 4 bytes of big endian, and sends it out. The length field is actually 17 or 24 bits 
 --  wide, depending on whether or not we're using raw, but that shouldn't matter. 
 --
---@param socket The socket to send the packet on.
---@param header The header, encoded with smb_get_header().
---@param parameters The parameters
---@param data The data
+--@param smb        The SMB object associated with the connection
+--@param header     The header, encoded with smb_get_header().
+--@param parameters The parameters.
+--@param data       The data.
 --@return (result, err) If result is false, err is the error message. Otherwise, err is
 --        undefined
-function smb_send(socket, header, parameters, data)
+function smb_send(smb, header, parameters, data)
     local encoded_parameters = smb_encode_parameters(parameters)
     local encoded_data       = smb_encode_data(data)
     local len = string.len(header) + string.len(encoded_parameters) + string.len(encoded_data)
     local out = bin.pack(">I<AAA", len, header, encoded_parameters, encoded_data)
 
        stdnse.print_debug(2, "Sending SMB packet (len: %d)", string.len(out))
-    return socket:send(out)
+    return smb['socket']:send(out)
 end
 
 --- Reads the next packet from the socket, and parses it into the header, parameters, 
@@ -1065,20 +737,20 @@
 --        Some buffering should happen here. Currently, we're waiting on 32 bytes, which
 --        is the length of the header, but there's no guarantee that we get the entire
 --        body. 
---@param socket The socket to read the packet from
+--@param smb    The SMB object associated with the connection
 --@return (status, header, parameters, data) If status is true, the header, 
 --        parameters, and data are all the raw arrays (with the lengths already
 --        removed). If status is false, header contains an error message and parameters/
 --        data are undefined. 
-function smb_read(socket)
+function smb_read(smb)
        local status, result
        local pos, length, header, parameter_length, parameters, data_length, data
 
        -- Receive the response
        -- [TODO] set the timeout length per jah's strategy:
        --   http://seclists.org/nmap-dev/2008/q3/0702.html
-       socket:set_timeout(1000)
-       status, result = socket:receive_bytes(32);
+       smb['socket']:set_timeout(1000)
+       status, result = smb['socket']:receive_bytes(32);
 
        -- Make sure the connection is still alive
        if(status ~= true) then
@@ -1113,7 +785,7 @@
 -- * The "encryption key" (aka, the server challenge)\n
 -- * The capabilities\n
 -- * The server and domain names\n
---@param socket The socket, in the proper state (ie, newly connected). 
+--@param smb    The SMB object associated with the connection
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a 
 --        table with the following elements:\n
 --      'security_mode'    Whether or not to use cleartext passwords, message signatures, etc.\n
@@ -1130,16 +802,15 @@
 --      'server_challenge' A random string used for challenge/response\n
 --      'domain'           The server's primary domain\n
 --      'server'           The server's name\n
-function negotiate_protocol(socket)
+function negotiate_protocol(smb)
        local header, parameters, data
        local pos
        local header1, header2, header3, ehader4, command, status, flags, flags2, pid_high, signature, unused, pid, mid
        local dialect, security_mode, max_mpx, max_vc, max_buffer, max_raw_buffer, session_key, capabilities, time, 
timezone, key_length
        local server_challenge, date, timezone_str
        local domain, server
-       local response = {}
 
-       header     = smb_encode_header(command_codes['SMB_COM_NEGOTIATE'], 0, 0)
+       header     = smb_encode_header(smb, command_codes['SMB_COM_NEGOTIATE'])
 
        -- Parameters are blank
        parameters = ""
@@ -1149,13 +820,13 @@
 
        -- Send the negotiate request
        stdnse.print_debug(2, "Sending SMB_COM_NEGOTIATE")
-       result, err = smb_send(socket, header, parameters, data)
+       result, err = smb_send(smb, header, parameters, data)
        if(status == false) then
                return err
        end
 
        -- Read the result
-       status, header, parameters, data = smb_read(socket)
+       status, header, parameters, data = smb_read(smb)
        if(status ~= true) then
                return false, header
        end
@@ -1199,25 +870,261 @@
                pos, ch, dummy = bin.unpack("<CC", data, pos)
        end
 
-       -- Fill out response variables
-       response['security_mode']    = security_mode
-       response['max_mpx']          = max_mpx
-       response['max_vc']           = max_vc
-       response['max_buffer']       = max_buffer
-       response['max_raw_buffer']   = max_raw_buffer
-       response['session_key']      = session_key
-       response['capabilities']     = capabilities
-       response['time']             = time
-       response['date']             = date
-       response['timezone']         = timezone
-       response['timezone_str']     = timezone_str
-       response['server_challenge'] = server_challenge
-       response['domain']           = domain
-       response['server']           = server
+       -- Fill out smb variables
+       smb['security_mode']    = security_mode
+       smb['max_mpx']          = max_mpx
+       smb['max_vc']           = max_vc
+       smb['max_buffer']       = max_buffer
+       smb['max_raw_buffer']   = max_raw_buffer
+       smb['session_key']      = session_key
+       smb['capabilities']     = capabilities
+       smb['time']             = time
+       smb['date']             = date
+       smb['timezone']         = timezone
+       smb['timezone_str']     = timezone_str
+       smb['server_challenge'] = server_challenge
+       smb['domain']           = domain
+       smb['server']           = server
 
-       return true, response
+       return true
 end
 
+---Determines which hash type is going to be used, based on the function parameters, the registry, and 
+-- the nmap arguments (in that order).
+--
+--@param hash_type [optional] The function parameter version, which will override all others if set. 
+--@return The highest priority hash type that's set.
+local function get_hash_type(hash_type)
+       
+       if(hash_type ~= nil) then
+               stdnse.print_debug(2, "SMB: Using logon type passed as a parameter: %s", hash_type)
+       else
+               if(nmap.registry.args.smbtype ~= nil) then
+                       hash_type = nmap.registry.args.smbtype
+                       stdnse.print_debug(2, "SMB: Using logon type passed as an nmap parameter: %s", hash_type)
+               else
+                       hash_type = "ntlm"
+                       stdnse.print_debug(2, "SMB: Using default logon type: %s", hash_type)
+               end
+       end
+
+       return string.lower(hash_type)
+end
+
+
+---Determines which username is going to be used, based on the function parameters, the registry, and 
+-- the nmap arguments (in that order).
+--
+--@param username [optional] The function parameter version, which will override all others if set. 
+--@return The highest priority username that's set.
+-- TODO: Get username from the registry
+local function get_username(username)
+
+       if(username ~= nil) then
+               stdnse.print_debug(2, "SMB: Using username passed as a parameter: %s", username)
+       else
+               if(nmap.registry.args.smbusername ~= nil) then
+                       username = nmap.registry.args.smbusername
+                       stdnse.print_debug(2, "SMB: Using username passed as an nmap parameter: %s", username)
+               else
+                       username = nil
+                       stdnse.print_debug(2, "SMB: Couldn't find a username to use, not logging in")
+               end
+       end
+
+       return username
+end
+
+---Determines which domain is going to be used, based on the function parameters, the registry, and 
+-- the nmap arguments (in that order).
+--
+--@param domain [optional] The function parameter version, which will override all others if set. 
+--@return The highest priority domain that's set.
+local function get_domain(domain)
+
+       if(domain ~= nil) then
+               stdnse.print_debug(2, "SMB: Using domain passed as a parameter: %s", domain)
+       else
+               if(nmap.registry.args.smbdomain ~= nil) then
+                       domain = nmap.registry.args.smbdomain
+                       stdnse.print_debug(2, "SMB: Using domain passed as an nmap parameter: %s", domain)
+               else
+                       domain = ""
+                       stdnse.print_debug(2, "SMB: couldn't find domain to use, using blank")
+               end
+       end
+
+       return domain
+end
+
+---Generate the Lanman and NTLM password hashes. The password itself is taken from the function parameters,
+-- the registry, and the nmap arguments (in that order). If no password is set, then the password hash
+-- is used (which is read from all the usual places). If neither is set, then a blank password is used. \n
+--\n
+-- The output passwords are hashed based on the hash type. \n
+--\n
+--@param username The username, which is used for v2 passwords. 
+--@param domain The username, which is used for v2 passwords. 
+--@param password [optional] The overriding password. 
+--@param password_hash [optional] The overriding password hash. Shouldn't be set if password is set. 
+--@param challenge The server challenge.
+--@param hash_type The way in which to hash the password. 
+--@return The highest priority domain that's set.
+local function get_password_response(username, domain, password, password_hash, challenge, hash_type)
+
+       -- Check if there's a password set
+       if(password ~= nil) then
+               stdnse.print_debug(2, "SMB: Using password passed as a parameter")
+       else
+               if(nmap.registry.args.smbpassword ~= nil) then
+                       password = nmap.registry.args.smbpassword
+                       stdnse.print_debug(2, "SMB: Using password passed as an nmap parameter: %s", password)
+               else
+                       password = nil
+                       stdnse.print_debug(2, "SMB: couldn't find password to use, checking for a useable hash")
+               end
+       end
+
+       -- If we got a password, hash it, otherwise, try getting a hash from the standard places
+       if(password ~= nil) then
+               -- If we have a blank password, just return the empty stringAAA
+               if(password == "") then
+                       return "", ""
+               end
+
+               status, lm_hash   = lm_create_hash(password)
+               status, ntlm_hash = ntlm_create_hash(password)
+       else
+               local hashes = nil
+
+               if(nmap.registry.args.smbhash ~= nil) then
+                       hashes = nmap.registry.args.smbhash
+               end
+
+               if(hashes ~= nil) then
+                       if(string.find(hashes, "^" .. string.rep("%x%x", 16) .. "$")) then
+                               stdnse.print_debug(2, "SMB: Found a 16-byte hex string", good_type)
+                               lm_hash   = bin.pack("H", hashes:sub(1, 32))
+                               ntlm_hash = bin.pack("H", hashes:sub(1, 32))
+                       elseif(string.find(hashes, "^" .. string.rep("%x%x", 32) .. "$")) then
+                               stdnse.print_debug(2, "SMB: Found a 32-byte hex string", good_type)
+                               lm_hash   = bin.pack("H", hashes:sub(1, 32))
+                               ntlm_hash = bin.pack("H", hashes:sub(33, 64))
+                       elseif(string.find(hashes, "^" .. string.rep("%x%x", 16) .. "." .. string.rep("%x%x", 16) .. 
"$")) then
+                               stdnse.print_debug(2, "SMB: Found two 16-byte hex strings", good_type)
+                               lm_hash   = bin.pack("H", hashes:sub(1, 32))
+                               ntlm_hash = bin.pack("H", hashes:sub(34, 65))
+                       else
+                               stdnse.print_debug(1, "SMB: Hashes input in an invalid format")
+                               lm_hash = nil
+                               ntlm_hash = nil
+                       end
+               end
+       end
+
+       -- At this point, we should have a good lm_hash and ntlm_hash
+       if(lm_hash == nil or ntlm_hash == nil) then
+               stdnse.print_debug(1, "SMB: Couldn't determine which password to use, falling back to default accounts")
+               return nil, nil
+       end
+
+       -- Output what we've got so far
+       stdnse.print_debug(2, "SMB: Lanman hash: %s", stdnse.tohex(lm_hash))
+       stdnse.print_debug(2, "SMB: NTLM   hash: %s", stdnse.tohex(ntlm_hash))
+                       
+       -- Hash the password the way the user wants
+       if(hash_type == "v1") then
+               -- LM and NTLM are hashed with their respective algorithms
+               stdnse.print_debug(2, "SMB: Creating v1 response")
+               status, lm_response   = lm_create_response(lm_hash, challenge)
+               status, ntlm_response = ntlm_create_response(ntlm_hash, challenge)
+       elseif(hash_type == "lm") then
+               -- LM is hashed with its algorithm, NTLM is blank
+               stdnse.print_debug(2, "SMB: Creating LMv1 response")
+               status, lm_response   = lm_create_response(lm_hash, challenge)
+                       ntlm_response = ""
+       elseif(hash_type == "ntlm") then
+               -- LM and NTLM both use the NTLM algorithm
+               stdnse.print_debug(2, "SMB: Creating NTLMv1 response")
+               status, lm_response   = ntlm_create_response(ntlm_hash, challenge)
+               status, ntlm_response = ntlm_create_response(ntlm_hash, challenge)
+       elseif(hash_type == "v2") then
+               -- LM and NTLM are hashed with their respective v2 algorithms
+               stdnse.print_debug(2, "SMB: Creating v2 response")
+               status, lm_response   = lmv2_create_response(ntlm_hash, username, domain, challenge)
+               status, ntlm_response = ntlmv2_create_response(ntlm_hash, username, domain, challenge, 24)
+       elseif(hash_type == "lmv2") then
+               -- LM is hashed with its v2 algorithm, NTLM is blank
+               stdnse.print_debug(2, "SMB: Creating LMv2 response")
+               status, lm_response   = lmv2_create_response(ntlm_hash, username, domain, challenge)
+                       ntlm_response = ""
+       else
+               -- Default to NTLMv1
+               stdnse.print_debug(1, "SMB: Invalid login type specified, using default (NTLM)")
+               status, lm_response   = ntlm_create_response(ntlm_hash, challenge)
+               status, ntlm_response = ntlm_create_response(ntlm_hash, challenge)
+       end
+
+       stdnse.print_debug(2, "SMB: Lanman response: %s", stdnse.tohex(lm_response))
+       stdnse.print_debug(2, "SMB: NTLM   response: %s", stdnse.tohex(ntlm_response))
+
+       return lm_response, ntlm_response
+end
+
+local function get_logins(ip, challenge, username, domain, password, password_hash, hash_type)
+
+       local response = {}
+
+       -- If we don't have OpenSSL, don't bother with any of this
+       if(have_ssl == true) then
+               -- We choose *one* username to try, here. First, see if the user set a username
+               -- in the function parameters, then in the registry [TODO], then as an nmap 
+               -- parameter, then disable it. 
+       
+               -- If a username was found, look for a domain and password
+               username = get_username(username)
+               domain   = get_domain(domain)
+               hash_type = get_hash_type(hash_type)
+
+               lm_response, ntlm_response = get_password_response(username, domain, password, password_hash, 
challenge, hash_type)
+
+               if(lm_response ~= nil and ntlm_response ~= nil) then
+                       local data = {}
+                       data['username'] = username
+                       data['domain']   = domain
+                       data['lanman']   = lm_response
+                       data['ntlm']     = ntlm_response
+                       response[#response + 1] = data
+               end
+
+       else
+               stdnse.print_debug(1, "SMB: Couldn't find OpenSSL library, only checking Guest and/or Anonymous 
accounts")
+       end
+
+       local data
+       -- Add guest account, if they requested
+       if(nmap.registry.args.smbguest == "true" or nmap.registry.args.smbguest == "1") then
+               stdnse.print_debug(2, "SMB: Going to try guest account before attempting anonymous")
+
+               data = {}
+               data['username'] = 'guest'
+               data['domain'] = ''
+               data['lanman'] = ''
+               data['ntlm'] = ''
+               response[#response + 1] = data
+       end
+
+       -- Add the anonymous account
+       data = {}
+       data['username'] = ''
+       data['domain'] = ''
+       data['lanman'] = ''
+       data['ntlm'] = ''
+       response[#response + 1] = data
+
+       return response
+end
+
 --- Sends out SMB_COM_SESSION_SETUP_ANDX, which attempts to log a user in. 
 -- Sends the following:\n
 -- * Negotiated parameters (multiplexed connections, virtual circuit, capabilities)\n
@@ -1230,10 +1137,12 @@
 -- * User ID\n
 -- * Server OS\n
 --\n
---@param socket       The socket, in the proper state (ie, after protocol has been negotiated).
---@param username     The account name to use. For Null sessions, leave it blank (''). 
---@param session_key  The session_key value, returned by SMB_COM_NEGOTIATE.  
---@param capabilities The server's capabilities, returned by SMB_COM_NEGOTIATE. 
+--@param smb          The SMB object associated with the connection
+--@param username     [optional] Overrides the account name to use. Will use Nmap parameters or registry by 
+--                    default, or NULL session if it isn't set anywhere
+--@param domain       [optional] Overrides the domain to use. 
+--@param password     [optional] Overrides the password to use. Will use Nmap parameters or registry by default.
+--@param hash_type    [optional] Overrides the hash type to use (can be v1, LM, NTLM, LMv2, v2). Default is 'NTLM'.
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a 
 --        table with the following elements:\n
 --      'uid'         The UserID for the session
@@ -1241,74 +1150,88 @@
 --                    as the guest account
 --      'os'          The operating system
 --      'lanmanager'  The servers's LAN Manager
-function start_session(socket, username, session_key, capabilities)
+function start_session(smb, username, domain, password, password_hash, hash_type)
+       local i
        local status, result
        local header, parameters, data
        local pos
        local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, 
pid, uid, mid 
        local andx_command, andx_reserved, andx_offset, action
        local os, lanmanager, domain
-       local response = {}
+       local logins = get_logins(smb['ip'], smb['server_challenge'], username, domain, password, password_hash, 
hash_type)
 
-       header     = smb_encode_header(command_codes['SMB_COM_SESSION_SETUP_ANDX'], 0, 0)
+       header     = smb_encode_header(smb, command_codes['SMB_COM_SESSION_SETUP_ANDX'])
 
-       -- Parameters
-       parameters = bin.pack("<CCSSSSISSII", 
-                               0xFF,        -- ANDX -- no further commands
-                               0x00,        -- ANDX -- Reserved (0)
-                               0x0000,      -- ANDX -- next offset
-                               0x1000,      -- Max buffer size
-                               0x0001,      -- Max multiplexes
-                               0x0000,      -- Virtual circuit num
-                               session_key, -- The session key
-                               0,           -- ANSI/Lanman password length
-                               0,           -- Unicode/NTLM password length
-                               0,           -- Reserved
-                capabilities -- Capabilities
-                       )
+       -- Loop through the credentials returned by get_logins() (there should be 2 or 3)
+       for i = 1, #logins, 1 do
+               -- Parameters
+               parameters = bin.pack("<CCSSSSISSII", 
+                                       0xFF,               -- ANDX -- no further commands
+                                       0x00,               -- ANDX -- Reserved (0)
+                                       0x0000,             -- ANDX -- next offset
+                                       0x1000,             -- Max buffer size
+                                       0x0001,             -- Max multiplexes
+                                       0x0000,             -- Virtual circuit num
+                                       smb['session_key'], -- The session key
+                                       #logins[i]['lanman'], -- ANSI/Lanman password length
+                                       #logins[i]['ntlm'],   -- Unicode/NTLM password length
+                                       0,                  -- Reserved
+                       0x00000050          -- Capabilities
+                               )
+       
+               -- Data is a list of strings, terminated by a blank one. 
+               data       = bin.pack("<AAzzzz", 
+                                       logins[i]['lanman'],   -- ANSI/Lanman password
+                                       logins[i]['ntlm'],     -- Unicode/NTLM password
+                                       logins[i]['username'], -- Account
+                                       logins[i]['domain'],   -- Domain
+                                       "Nmap",                -- OS
+                                       "Native Lanman"        -- Native LAN Manager
+                               )
+               -- Send the session setup request
+               stdnse.print_debug(2, "Sending SMB_COM_SESSION_SETUP_ANDX")
+               result, err = smb_send(smb, header, parameters, data)
+               if(result == false) then
+                       return false, err
+               end
+       
+               -- Read the result
+               status, header, parameters, data = smb_read(smb)
+               if(status ~= true) then
+                       return false, header
+               end
+       
+               -- Check if we were allowed in
+               pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, 
tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
 
-       -- Data is a list of strings, terminated by a blank one. 
-       data       = bin.pack("<zzzz", 
-                                               -- ANSI/Lanman password
-                                               -- Unicode/NTLM password
-                               username,       -- Account
-                               "",             -- Domain
-                               "Nmap",         -- OS
-                               "Native Lanman" -- Native LAN Manager
-                       )
-       -- Send the session setup request
-       stdnse.print_debug(2, "Sending SMB_COM_SESSION_SETUP_ANDX")
-       result, err = smb_send(socket, header, parameters, data)
-       if(result == false) then
-               return false, err
-       end
+               -- Check if we're successful
+               if(status == 0) then
+                       -- This login worked
+                       stdnse.print_debug(1, "Login as %s\\%s succeeded", logins[i]['domain'], logins[i]['username'])
 
-       -- Read the result
-       status, header, parameters, data = smb_read(socket)
-       if(status ~= true) then
-               return false, header
-       end
+                       -- Parse the parameters
+                       pos, andx_command, andx_reserved, andx_offset, action = bin.unpack("<CCSS", parameters)
+               
+                       -- Parse the data
+                       pos, os, lanmanager, domain = bin.unpack("<zzz", data)
+               
+                       -- Fill in the smb object and smb string
+                       smb['uid']        = uid
+                       smb['is_guest']   = bit.band(action, 1)
+                       smb['os']         = os
+                       smb['lanmanager'] = lanmanager
+               
+                       return true
 
-       -- Check if we were allowed in
-       pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, 
uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
-       if(status ~= 0) then
-               return false, get_status_name(status)
+               else
+                       -- This username failed, print a warning and keep going
+                       stdnse.print_debug(1, "Login as %s\\%s failed (%s), trying next login", logins[i]['domain'], 
logins[i]['username'], get_status_name(status))
+               end
        end
 
-       -- Parse the parameters
-       pos, andx_command, andx_reserved, andx_offset, action = bin.unpack("<CCSS", parameters)
+       stdnse.print_debug(1, "All logins failed, sorry it didn't work out!")
+       return false, get_status_name(status)
 
-       -- Parse the data
-       pos, os, lanmanager, domain = bin.unpack("<zzz", data)
-
-       -- Fill in the response string
-       response['uid']        = uid
-       response['is_guest']   = bit.band(action, 1)
-       response['os']         = os
-       response['lanmanager'] = lanmanager
-
-       return true, response
-
 end
  
 --- Sends out SMB_COM_SESSION_TREE_CONNECT_ANDX, which attempts to connect to a share. 
@@ -1320,20 +1243,18 @@
 -- Receives the following:\n
 -- * Tree ID\n
 --\n
---@param socket The socket, in the proper state. 
+--@param smb    The SMB object associated with the connection
 --@param path   The path to connect (eg, \\servername\C$)
---@param uid    The UserID, returned by SMB_COM_SESSION_SETUP_ANDX
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a 
 --        table with the following elements:\n
 --      'tid'         The TreeID for the session
-function tree_connect(socket, path, uid)
+function tree_connect(smb, path)
        local header, parameters, data
        local pos
        local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid 
        local andx_command, andx_reserved, andx_offset, action
-       local response = {}
 
-       header = smb_encode_header(command_codes['SMB_COM_TREE_CONNECT_ANDX'], uid, 0)
+       header = smb_encode_header(smb, command_codes['SMB_COM_TREE_CONNECT_ANDX'])
        parameters = bin.pack("<CCSSS", 
                                        0xFF,   -- ANDX no further commands
                                        0x00,   -- ANDX reserved
@@ -1349,13 +1270,13 @@
 
        -- Send the tree connect request
        stdnse.print_debug(2, "Sending SMB_COM_TREE_CONNECT_ANDX")
-       result, err = smb_send(socket, header, parameters, data)
+       result, err = smb_send(smb, header, parameters, data)
        if(result == false) then
                return false, err
        end
 
        -- Read the result
-       status, header, parameters, data = smb_read(socket)
+       status, header, parameters, data = smb_read(smb)
        if(status ~= true) then
                return false, header
        end
@@ -1366,35 +1287,31 @@
                return false, get_status_name(status)
        end
 
-       response['tid'] = tid
+       smb['tid'] = tid
 
-       return true, response
+       return true
        
 end
 
 --- Disconnects a tree session. Should be called before logging off and disconnecting. 
---@param socket The socket
---@param uid    The UserID, returned by SMB_COM_SESSION_SETUP_ANDX
---@param tid    The TreeID, returned by SMB_COM_TREE_CONNECT_ANDX
+--@param smb    The SMB object associated with the connection
 --@param return (status, result) If statis is false, result is an error message. If status is true, 
 --              the disconnect was successful. 
-function tree_disconnect(socket, uid, tid)
-       local response = ""
+function tree_disconnect(smb)
        local header
        local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid 
-       local response = {}
 
-       header = smb_encode_header(command_codes['SMB_COM_TREE_DISCONNECT'], uid, tid)
+       header = smb_encode_header(smb, command_codes['SMB_COM_TREE_DISCONNECT'])
 
        -- Send the tree disconnect request
        stdnse.print_debug(2, "Sending SMB_COM_TREE_DISCONNECT")
-       result, err = smb_send(socket, header, "", "")
+       result, err = smb_send(smb, header, "", "")
        if(result == false) then
                return false, err
        end
 
        -- Read the result
-       status, header, parameters, data = smb_read(socket)
+       status, header, parameters, data = smb_read(smb)
        if(status ~= true) then
                return false, header
        end
@@ -1405,20 +1322,21 @@
                return false, get_status_name(status)
        end
 
+       smb['tid'] = 0
+
        return true
        
 end
 
 ---Logs of the current user. Strictly speaking this isn't necessary, but it's the polite thing to do. 
---@param socket The socket. 
---@param uid    The user ID. 
+--@param smb    The SMB object associated with the connection
 --@param return (status, result) If statis is false, result is an error message. If status is true, 
 --              the logoff was successful. 
-function logoff(socket, uid)
+function logoff(smb)
        local header, parameters
        local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid 
 
-       header = smb_encode_header(command_codes['SMB_COM_LOGOFF_ANDX'], uid, 0)
+       header = smb_encode_header(smb, command_codes['SMB_COM_LOGOFF_ANDX'])
 
        -- Parameters are a blank ANDX block
        parameters = bin.pack("<CCS", 
@@ -1429,13 +1347,13 @@
 
        -- Send the tree disconnect request
        stdnse.print_debug(2, "Sending SMB_COM_LOGOFF_ANDX")
-       result, err = smb_send(socket, header, parameters, "")
+       result, err = smb_send(smb, header, parameters, "")
        if(result == false) then
                return false, err
        end
 
        -- Read the result
-       status, header, parameters, data = smb_read(socket)
+       status, header, parameters, data = smb_read(smb)
        if(status ~= true) then
                return false, header
        end
@@ -1446,6 +1364,8 @@
                return false, get_status_name(status)
        end
 
+       smb['uid'] = 0
+
        return true
        
 end
@@ -1454,21 +1374,18 @@
 --  Most of the parameters I pass here are used directly from a packetlog, especially the various permissions fields 
and flags. 
 --  I might make this more adjustable in the future, but this has been working for me. 
 --
---@param socket The socket, in the correct state
+--@param smb    The SMB object associated with the connection
 --@param path   The path of the file or pipe to open
---@param uid    The UserID
---@param tid    The TreeID
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table
 --        containing a lot of different elements, the most important one being 'fid', the handle to the opened file. 
-function create_file(socket, path, uid, tid)
+function create_file(smb, path)
        local header, parameters, data
        local pos
        local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid 
        local andx_command, andx_reserved, andx_offset
        local oplock_level, fid, create_action, created, last_access, last_write, last_change, attributes, 
allocation_size, end_of_file, filetype, ipc_state, is_directory
-       local response = {}
 
-       header = smb_encode_header(command_codes['SMB_COM_NT_CREATE_ANDX'], uid, tid)
+       header = smb_encode_header(smb, command_codes['SMB_COM_NT_CREATE_ANDX'])
        parameters = bin.pack("<CCSCSIIILIIIIIC", 
                                        0xFF,   -- ANDX no further commands
                                        0x00,   -- ANDX reserved
@@ -1491,13 +1408,13 @@
 
        -- Send the create file
        stdnse.print_debug(2, "Sending SMB_COM_NT_CREATE_ANDX")
-       result, err = smb_send(socket, header, parameters, data)
+       result, err = smb_send(smb, header, parameters, data)
        if(result == false) then
                return false, err
        end
 
        -- Read the result
-       status, header, parameters, data = smb_read(socket)
+       status, header, parameters, data = smb_read(smb)
        if(status ~= true) then
                return false, header
        end
@@ -1511,22 +1428,22 @@
        -- Parse the parameters
        pos, andx_command, andx_reserved, andx_offset, oplock_level, fid, create_action, created, last_access, 
last_write, last_change, attributes, allocation_size, end_of_file, filetype, ipc_state, is_directory = 
bin.unpack("<CCSCSILLLLILLSSC", parameters)
 
-       -- Fill in the response string
-       response['oplock_level']    = oplock_level
-       response['fid']             = fid
-       response['create_action']   = create_action
-       response['created']         = created
-       response['last_access']     = last_access
-       response['last_write']      = last_write
-       response['last_change']     = last_change
-       response['attributes']      = attributes
-       response['allocation_size'] = allocation_size
-       response['end_of_file']     = end_of_file
-       response['filetype']        = filetype
-       response['ipc_state']       = ipc_state
-       response['is_directory']    = is_directory
+       -- Fill in the smb string
+       smb['oplock_level']    = oplock_level
+       smb['fid']             = fid
+       smb['create_action']   = create_action
+       smb['created']         = created
+       smb['last_access']     = last_access
+       smb['last_write']      = last_write
+       smb['last_change']     = last_change
+       smb['attributes']      = attributes
+       smb['allocation_size'] = allocation_size
+       smb['end_of_file']     = end_of_file
+       smb['filetype']        = filetype
+       smb['ipc_state']       = ipc_state
+       smb['is_directory']    = is_directory
        
-       return true, response
+       return true
        
 end
 
@@ -1537,17 +1454,14 @@
 -- It is probably best to think of this as another protocol layer. This function will wrap SMB stuff around a 
 -- MSRPC call, make the call, then unwrap the SMB stuff from it before returning. 
 --
---@param socket The socket to send the packet on, in the proper state. 
+--@param smb    The SMB object associated with the connection
 --@param func   The function to call. The only one I've tested is 0x26, named pipes. 
 --@param function_parameters The parameter data to pass to the function. This is untested, since none of the
 --       transactions I've done have required parameters. 
 --@param data   The data to send with the packet. This is basically the next protocol layer
---@param uid    The UserID
---@param tid    The TreeID (handle to $IPC)
---@param fid    The FileID (opened by create_file)
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table 
 --        containing 'parameters' and 'data', representing the parameters and data returned by the server. 
-function send_transaction(socket, func, function_parameters, function_data, uid, tid, fid)
+function send_transaction(smb, func, function_parameters, function_data)
        local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid 
        local header, parameters, data
        local parameters_offset, data_offset
@@ -1555,7 +1469,7 @@
        local response = {}
 
        -- Header is 0x20 bytes long (not counting NetBIOS header).
-       header = smb_encode_header(command_codes['SMB_COM_TRANSACTION'], uid, tid) -- 0x25 = SMB_COM_TRANSACTION
+       header = smb_encode_header(smb, command_codes['SMB_COM_TRANSACTION']) -- 0x25 = SMB_COM_TRANSACTION
 
        -- 0x20 for SMB header, 0x01 for parameters header, 0x20 for parameters length, 0x02 for data header, 0x07 for 
"\PIPE\"
        parameters_offset = 0x20 + 0x01 + 0x20 + 0x02 + 0x07
@@ -1579,7 +1493,7 @@
                                        0x02,                            -- Number of 'setup' words (only ever seen 
'2').
                                        0x00,                            -- Reserved.
                                        func,                            -- Function to call.
-                                       fid                              -- Handle to open file
+                                       smb['fid']                       -- Handle to open file
                                )
 
        -- \PIPE\ is 0x07 bytes long. 
@@ -1589,13 +1503,13 @@
 
        -- Send the transaction request
        stdnse.print_debug(2, "Sending SMB_COM_TRANSACTION")
-       result, err = smb_send(socket, header, parameters, data)
+       result, err = smb_send(smb, header, parameters, data)
        if(result == false) then
                return false, err
        end
 
        -- Read the result
-       status, header, parameters, data = smb_read(socket)
+       status, header, parameters, data = smb_read(smb)
        if(status ~= true) then
                return false, header
        end
@@ -1625,3 +1539,604 @@
        return true, response
 end
 
+
+
+
+
+command_codes = 
+{
+       SMB_COM_CREATE_DIRECTORY          = 0x00,
+       SMB_COM_DELETE_DIRECTORY          = 0x01,
+       SMB_COM_OPEN                      = 0x02,
+       SMB_COM_CREATE                    = 0x03,
+       SMB_COM_CLOSE                     = 0x04,
+       SMB_COM_FLUSH                     = 0x05,
+       SMB_COM_DELETE                    = 0x06,
+       SMB_COM_RENAME                    = 0x07,
+       SMB_COM_QUERY_INFORMATION         = 0x08,
+       SMB_COM_SET_INFORMATION           = 0x09,
+       SMB_COM_READ                      = 0x0A,
+       SMB_COM_WRITE                     = 0x0B,
+       SMB_COM_LOCK_BYTE_RANGE           = 0x0C,
+       SMB_COM_UNLOCK_BYTE_RANGE         = 0x0D,
+       SMB_COM_CREATE_TEMPORARY          = 0x0E,
+       SMB_COM_CREATE_NEW                = 0x0F,
+       SMB_COM_CHECK_DIRECTORY           = 0x10,
+       SMB_COM_PROCESS_EXIT              = 0x11,
+       SMB_COM_SEEK                      = 0x12,
+       SMB_COM_LOCK_AND_READ             = 0x13,
+       SMB_COM_WRITE_AND_UNLOCK          = 0x14,
+       SMB_COM_READ_RAW                  = 0x1A,
+       SMB_COM_READ_MPX                  = 0x1B,
+       SMB_COM_READ_MPX_SECONDARY        = 0x1C,
+       SMB_COM_WRITE_RAW                 = 0x1D,
+       SMB_COM_WRITE_MPX                 = 0x1E,
+       SMB_COM_WRITE_MPX_SECONDARY       = 0x1F,
+       SMB_COM_WRITE_COMPLETE            = 0x20,
+       SMB_COM_QUERY_SERVER              = 0x21,
+       SMB_COM_SET_INFORMATION2          = 0x22,
+       SMB_COM_QUERY_INFORMATION2        = 0x23,
+       SMB_COM_LOCKING_ANDX              = 0x24,
+       SMB_COM_TRANSACTION               = 0x25,
+       SMB_COM_TRANSACTION_SECONDARY     = 0x26,
+       SMB_COM_IOCTL                     = 0x27,
+       SMB_COM_IOCTL_SECONDARY           = 0x28,
+       SMB_COM_COPY                      = 0x29,
+       SMB_COM_MOVE                      = 0x2A,
+       SMB_COM_ECHO                      = 0x2B,
+       SMB_COM_WRITE_AND_CLOSE           = 0x2C,
+       SMB_COM_OPEN_ANDX                 = 0x2D,
+       SMB_COM_READ_ANDX                 = 0x2E,
+       SMB_COM_WRITE_ANDX                = 0x2F,
+       SMB_COM_NEW_FILE_SIZE             = 0x30,
+       SMB_COM_CLOSE_AND_TREE_DISC       = 0x31,
+       SMB_COM_TRANSACTION2              = 0x32,
+       SMB_COM_TRANSACTION2_SECONDARY    = 0x33,
+       SMB_COM_FIND_CLOSE2               = 0x34,
+       SMB_COM_FIND_NOTIFY_CLOSE         = 0x35,
+       SMB_COM_TREE_CONNECT              = 0x70,
+       SMB_COM_TREE_DISCONNECT           = 0x71,
+       SMB_COM_NEGOTIATE                 = 0x72,
+       SMB_COM_SESSION_SETUP_ANDX        = 0x73,
+       SMB_COM_LOGOFF_ANDX               = 0x74,
+       SMB_COM_TREE_CONNECT_ANDX         = 0x75,
+       SMB_COM_QUERY_INFORMATION_DISK    = 0x80,
+       SMB_COM_SEARCH                    = 0x81,
+       SMB_COM_FIND                      = 0x82,
+       SMB_COM_FIND_UNIQUE               = 0x83,
+       SMB_COM_FIND_CLOSE                = 0x84,
+       SMB_COM_NT_TRANSACT               = 0xA0,
+       SMB_COM_NT_TRANSACT_SECONDARY     = 0xA1,
+       SMB_COM_NT_CREATE_ANDX            = 0xA2,
+       SMB_COM_NT_CANCEL                 = 0xA4,
+       SMB_COM_NT_RENAME                 = 0xA5,
+       SMB_COM_OPEN_PRINT_FILE           = 0xC0,
+       SMB_COM_WRITE_PRINT_FILE          = 0xC1,
+       SMB_COM_CLOSE_PRINT_FILE          = 0xC2,
+       SMB_COM_GET_PRINT_QUEUE           = 0xC3,
+       SMB_COM_READ_BULK                 = 0xD8,
+       SMB_COM_WRITE_BULK                = 0xD9,
+       SMB_COM_WRITE_BULK_DATA           = 0xDA,
+       SMB_NO_FURTHER_COMMANDS           = 0xFF
+}
+
+for i, v in pairs(command_codes) do
+       command_names[v] = i
+end
+
+
+
+status_names = 
+{
+       NT_STATUS_OK = 0x0000,
+       NT_STATUS_BUFFER_OVERFLOW = 0x80000005,
+       NT_STATUS_UNSUCCESSFUL = 0xc0000001,
+       NT_STATUS_NOT_IMPLEMENTED = 0xc0000002,
+       NT_STATUS_INVALID_INFO_CLASS = 0xc0000003,
+       NT_STATUS_INFO_LENGTH_MISMATCH = 0xc0000004,
+       NT_STATUS_ACCESS_VIOLATION = 0xc0000005,
+       NT_STATUS_IN_PAGE_ERROR = 0xc0000006,
+       NT_STATUS_PAGEFILE_QUOTA = 0xc0000007,
+       NT_STATUS_INVALID_HANDLE = 0xc0000008,
+       NT_STATUS_BAD_INITIAL_STACK = 0xc0000009,
+       NT_STATUS_BAD_INITIAL_PC = 0xc000000a,
+       NT_STATUS_INVALID_CID = 0xc000000b,
+       NT_STATUS_TIMER_NOT_CANCELED = 0xc000000c,
+       NT_STATUS_INVALID_PARAMETER = 0xc000000d,
+       NT_STATUS_NO_SUCH_DEVICE = 0xc000000e,
+       NT_STATUS_NO_SUCH_FILE = 0xc000000f,
+       NT_STATUS_INVALID_DEVICE_REQUEST = 0xc0000010,
+       NT_STATUS_END_OF_FILE = 0xc0000011,
+       NT_STATUS_WRONG_VOLUME = 0xc0000012,
+       NT_STATUS_NO_MEDIA_IN_DEVICE = 0xc0000013,
+       NT_STATUS_UNRECOGNIZED_MEDIA = 0xc0000014,
+       NT_STATUS_NONEXISTENT_SECTOR = 0xc0000015,
+       NT_STATUS_MORE_PROCESSING_REQUIRED = 0xc0000016,
+       NT_STATUS_NO_MEMORY = 0xc0000017,
+       NT_STATUS_CONFLICTING_ADDRESSES = 0xc0000018,
+       NT_STATUS_NOT_MAPPED_VIEW = 0xc0000019,
+       NT_STATUS_UNABLE_TO_FREE_VM = 0xc000001a,
+       NT_STATUS_UNABLE_TO_DELETE_SECTION = 0xc000001b,
+       NT_STATUS_INVALID_SYSTEM_SERVICE = 0xc000001c,
+       NT_STATUS_ILLEGAL_INSTRUCTION = 0xc000001d,
+       NT_STATUS_INVALID_LOCK_SEQUENCE = 0xc000001e,
+       NT_STATUS_INVALID_VIEW_SIZE = 0xc000001f,
+       NT_STATUS_INVALID_FILE_FOR_SECTION = 0xc0000020,
+       NT_STATUS_ALREADY_COMMITTED = 0xc0000021,
+       NT_STATUS_ACCESS_DENIED = 0xc0000022,
+       NT_STATUS_BUFFER_TOO_SMALL = 0xc0000023,
+       NT_STATUS_OBJECT_TYPE_MISMATCH = 0xc0000024,
+       NT_STATUS_NONCONTINUABLE_EXCEPTION = 0xc0000025,
+       NT_STATUS_INVALID_DISPOSITION = 0xc0000026,
+       NT_STATUS_UNWIND = 0xc0000027,
+       NT_STATUS_BAD_STACK = 0xc0000028,
+       NT_STATUS_INVALID_UNWIND_TARGET = 0xc0000029,
+       NT_STATUS_NOT_LOCKED = 0xc000002a,
+       NT_STATUS_PARITY_ERROR = 0xc000002b,
+       NT_STATUS_UNABLE_TO_DECOMMIT_VM = 0xc000002c,
+       NT_STATUS_NOT_COMMITTED = 0xc000002d,
+       NT_STATUS_INVALID_PORT_ATTRIBUTES = 0xc000002e,
+       NT_STATUS_PORT_MESSAGE_TOO_LONG = 0xc000002f,
+       NT_STATUS_INVALID_PARAMETER_MIX = 0xc0000030,
+       NT_STATUS_INVALID_QUOTA_LOWER = 0xc0000031,
+       NT_STATUS_DISK_CORRUPT_ERROR = 0xc0000032,
+       NT_STATUS_OBJECT_NAME_INVALID = 0xc0000033,
+       NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034,
+       NT_STATUS_OBJECT_NAME_COLLISION = 0xc0000035,
+       NT_STATUS_HANDLE_NOT_WAITABLE = 0xc0000036,
+       NT_STATUS_PORT_DISCONNECTED = 0xc0000037,
+       NT_STATUS_DEVICE_ALREADY_ATTACHED = 0xc0000038,
+       NT_STATUS_OBJECT_PATH_INVALID = 0xc0000039,
+       NT_STATUS_OBJECT_PATH_NOT_FOUND = 0xc000003a,
+       NT_STATUS_OBJECT_PATH_SYNTAX_BAD = 0xc000003b,
+       NT_STATUS_DATA_OVERRUN = 0xc000003c,
+       NT_STATUS_DATA_LATE_ERROR = 0xc000003d,
+       NT_STATUS_DATA_ERROR = 0xc000003e,
+       NT_STATUS_CRC_ERROR = 0xc000003f,
+       NT_STATUS_SECTION_TOO_BIG = 0xc0000040,
+       NT_STATUS_PORT_CONNECTION_REFUSED = 0xc0000041,
+       NT_STATUS_INVALID_PORT_HANDLE = 0xc0000042,
+       NT_STATUS_SHARING_VIOLATION = 0xc0000043,
+       NT_STATUS_QUOTA_EXCEEDED = 0xc0000044,
+       NT_STATUS_INVALID_PAGE_PROTECTION = 0xc0000045,
+       NT_STATUS_MUTANT_NOT_OWNED = 0xc0000046,
+       NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED = 0xc0000047,
+       NT_STATUS_PORT_ALREADY_SET = 0xc0000048,
+       NT_STATUS_SECTION_NOT_IMAGE = 0xc0000049,
+       NT_STATUS_SUSPEND_COUNT_EXCEEDED = 0xc000004a,
+       NT_STATUS_THREAD_IS_TERMINATING = 0xc000004b,
+       NT_STATUS_BAD_WORKING_SET_LIMIT = 0xc000004c,
+       NT_STATUS_INCOMPATIBLE_FILE_MAP = 0xc000004d,
+       NT_STATUS_SECTION_PROTECTION = 0xc000004e,
+       NT_STATUS_EAS_NOT_SUPPORTED = 0xc000004f,
+       NT_STATUS_EA_TOO_LARGE = 0xc0000050,
+       NT_STATUS_NONEXISTENT_EA_ENTRY = 0xc0000051,
+       NT_STATUS_NO_EAS_ON_FILE = 0xc0000052,
+       NT_STATUS_EA_CORRUPT_ERROR = 0xc0000053,
+       NT_STATUS_FILE_LOCK_CONFLICT = 0xc0000054,
+       NT_STATUS_LOCK_NOT_GRANTED = 0xc0000055,
+       NT_STATUS_DELETE_PENDING = 0xc0000056,
+       NT_STATUS_CTL_FILE_NOT_SUPPORTED = 0xc0000057,
+       NT_STATUS_UNKNOWN_REVISION = 0xc0000058,
+       NT_STATUS_REVISION_MISMATCH = 0xc0000059,
+       NT_STATUS_INVALID_OWNER = 0xc000005a,
+       NT_STATUS_INVALID_PRIMARY_GROUP = 0xc000005b,
+       NT_STATUS_NO_IMPERSONATION_TOKEN = 0xc000005c,
+       NT_STATUS_CANT_DISABLE_MANDATORY = 0xc000005d,
+       NT_STATUS_NO_LOGON_SERVERS = 0xc000005e,
+       NT_STATUS_NO_SUCH_LOGON_SESSION = 0xc000005f,
+       NT_STATUS_NO_SUCH_PRIVILEGE = 0xc0000060,
+       NT_STATUS_PRIVILEGE_NOT_HELD = 0xc0000061,
+       NT_STATUS_INVALID_ACCOUNT_NAME = 0xc0000062,
+       NT_STATUS_USER_EXISTS = 0xc0000063,
+       NT_STATUS_NO_SUCH_USER = 0xc0000064,
+       NT_STATUS_GROUP_EXISTS = 0xc0000065,
+       NT_STATUS_NO_SUCH_GROUP = 0xc0000066,
+       NT_STATUS_MEMBER_IN_GROUP = 0xc0000067,
+       NT_STATUS_MEMBER_NOT_IN_GROUP = 0xc0000068,
+       NT_STATUS_LAST_ADMIN = 0xc0000069,
+       NT_STATUS_WRONG_PASSWORD = 0xc000006a,
+       NT_STATUS_ILL_FORMED_PASSWORD = 0xc000006b,
+       NT_STATUS_PASSWORD_RESTRICTION = 0xc000006c,
+       NT_STATUS_LOGON_FAILURE = 0xc000006d,
+       NT_STATUS_ACCOUNT_RESTRICTION = 0xc000006e,
+       NT_STATUS_INVALID_LOGON_HOURS = 0xc000006f,
+       NT_STATUS_INVALID_WORKSTATION = 0xc0000070,
+       NT_STATUS_PASSWORD_EXPIRED = 0xc0000071,
+       NT_STATUS_ACCOUNT_DISABLED = 0xc0000072,
+       NT_STATUS_NONE_MAPPED = 0xc0000073,
+       NT_STATUS_TOO_MANY_LUIDS_REQUESTED = 0xc0000074,
+       NT_STATUS_LUIDS_EXHAUSTED = 0xc0000075,
+       NT_STATUS_INVALID_SUB_AUTHORITY = 0xc0000076,
+       NT_STATUS_INVALID_ACL = 0xc0000077,
+       NT_STATUS_INVALID_SID = 0xc0000078,
+       NT_STATUS_INVALID_SECURITY_DESCR = 0xc0000079,
+       NT_STATUS_PROCEDURE_NOT_FOUND = 0xc000007a,
+       NT_STATUS_INVALID_IMAGE_FORMAT = 0xc000007b,
+       NT_STATUS_NO_TOKEN = 0xc000007c,
+       NT_STATUS_BAD_INHERITANCE_ACL = 0xc000007d,
+       NT_STATUS_RANGE_NOT_LOCKED = 0xc000007e,
+       NT_STATUS_DISK_FULL = 0xc000007f,
+       NT_STATUS_SERVER_DISABLED = 0xc0000080,
+       NT_STATUS_SERVER_NOT_DISABLED = 0xc0000081,
+       NT_STATUS_TOO_MANY_GUIDS_REQUESTED = 0xc0000082,
+       NT_STATUS_GUIDS_EXHAUSTED = 0xc0000083,
+       NT_STATUS_INVALID_ID_AUTHORITY = 0xc0000084,
+       NT_STATUS_AGENTS_EXHAUSTED = 0xc0000085,
+       NT_STATUS_INVALID_VOLUME_LABEL = 0xc0000086,
+       NT_STATUS_SECTION_NOT_EXTENDED = 0xc0000087,
+       NT_STATUS_NOT_MAPPED_DATA = 0xc0000088,
+       NT_STATUS_RESOURCE_DATA_NOT_FOUND = 0xc0000089,
+       NT_STATUS_RESOURCE_TYPE_NOT_FOUND = 0xc000008a,
+       NT_STATUS_RESOURCE_NAME_NOT_FOUND = 0xc000008b,
+       NT_STATUS_ARRAY_BOUNDS_EXCEEDED = 0xc000008c,
+       NT_STATUS_FLOAT_DENORMAL_OPERAND = 0xc000008d,
+       NT_STATUS_FLOAT_DIVIDE_BY_ZERO = 0xc000008e,
+       NT_STATUS_FLOAT_INEXACT_RESULT = 0xc000008f,
+       NT_STATUS_FLOAT_INVALID_OPERATION = 0xc0000090,
+       NT_STATUS_FLOAT_OVERFLOW = 0xc0000091,
+       NT_STATUS_FLOAT_STACK_CHECK = 0xc0000092,
+       NT_STATUS_FLOAT_UNDERFLOW = 0xc0000093,
+       NT_STATUS_INTEGER_DIVIDE_BY_ZERO = 0xc0000094,
+       NT_STATUS_INTEGER_OVERFLOW = 0xc0000095,
+       NT_STATUS_PRIVILEGED_INSTRUCTION = 0xc0000096,
+       NT_STATUS_TOO_MANY_PAGING_FILES = 0xc0000097,
+       NT_STATUS_FILE_INVALID = 0xc0000098,
+       NT_STATUS_ALLOTTED_SPACE_EXCEEDED = 0xc0000099,
+       NT_STATUS_INSUFFICIENT_RESOURCES = 0xc000009a,
+       NT_STATUS_DFS_EXIT_PATH_FOUND = 0xc000009b,
+       NT_STATUS_DEVICE_DATA_ERROR = 0xc000009c,
+       NT_STATUS_DEVICE_NOT_CONNECTED = 0xc000009d,
+       NT_STATUS_DEVICE_POWER_FAILURE = 0xc000009e,
+       NT_STATUS_FREE_VM_NOT_AT_BASE = 0xc000009f,
+       NT_STATUS_MEMORY_NOT_ALLOCATED = 0xc00000a0,
+       NT_STATUS_WORKING_SET_QUOTA = 0xc00000a1,
+       NT_STATUS_MEDIA_WRITE_PROTECTED = 0xc00000a2,
+       NT_STATUS_DEVICE_NOT_READY = 0xc00000a3,
+       NT_STATUS_INVALID_GROUP_ATTRIBUTES = 0xc00000a4,
+       NT_STATUS_BAD_IMPERSONATION_LEVEL = 0xc00000a5,
+       NT_STATUS_CANT_OPEN_ANONYMOUS = 0xc00000a6,
+       NT_STATUS_BAD_VALIDATION_CLASS = 0xc00000a7,
+       NT_STATUS_BAD_TOKEN_TYPE = 0xc00000a8,
+       NT_STATUS_BAD_MASTER_BOOT_RECORD = 0xc00000a9,
+       NT_STATUS_INSTRUCTION_MISALIGNMENT = 0xc00000aa,
+       NT_STATUS_INSTANCE_NOT_AVAILABLE = 0xc00000ab,
+       NT_STATUS_PIPE_NOT_AVAILABLE = 0xc00000ac,
+       NT_STATUS_INVALID_PIPE_STATE = 0xc00000ad,
+       NT_STATUS_PIPE_BUSY = 0xc00000ae,
+       NT_STATUS_ILLEGAL_FUNCTION = 0xc00000af,
+       NT_STATUS_PIPE_DISCONNECTED = 0xc00000b0,
+       NT_STATUS_PIPE_CLOSING = 0xc00000b1,
+       NT_STATUS_PIPE_CONNECTED = 0xc00000b2,
+       NT_STATUS_PIPE_LISTENING = 0xc00000b3,
+       NT_STATUS_INVALID_READ_MODE = 0xc00000b4,
+       NT_STATUS_IO_TIMEOUT = 0xc00000b5,
+       NT_STATUS_FILE_FORCED_CLOSED = 0xc00000b6,
+       NT_STATUS_PROFILING_NOT_STARTED = 0xc00000b7,
+       NT_STATUS_PROFILING_NOT_STOPPED = 0xc00000b8,
+       NT_STATUS_COULD_NOT_INTERPRET = 0xc00000b9,
+       NT_STATUS_FILE_IS_A_DIRECTORY = 0xc00000ba,
+       NT_STATUS_NOT_SUPPORTED = 0xc00000bb,
+       NT_STATUS_REMOTE_NOT_LISTENING = 0xc00000bc,
+       NT_STATUS_DUPLICATE_NAME = 0xc00000bd,
+       NT_STATUS_BAD_NETWORK_PATH = 0xc00000be,
+       NT_STATUS_NETWORK_BUSY = 0xc00000bf,
+       NT_STATUS_DEVICE_DOES_NOT_EXIST = 0xc00000c0,
+       NT_STATUS_TOO_MANY_COMMANDS = 0xc00000c1,
+       NT_STATUS_ADAPTER_HARDWARE_ERROR = 0xc00000c2,
+       NT_STATUS_INVALID_NETWORK_RESPONSE = 0xc00000c3,
+       NT_STATUS_UNEXPECTED_NETWORK_ERROR = 0xc00000c4,
+       NT_STATUS_BAD_REMOTE_ADAPTER = 0xc00000c5,
+       NT_STATUS_PRINT_QUEUE_FULL = 0xc00000c6,
+       NT_STATUS_NO_SPOOL_SPACE = 0xc00000c7,
+       NT_STATUS_PRINT_CANCELLED = 0xc00000c8,
+       NT_STATUS_NETWORK_NAME_DELETED = 0xc00000c9,
+       NT_STATUS_NETWORK_ACCESS_DENIED = 0xc00000ca,
+       NT_STATUS_BAD_DEVICE_TYPE = 0xc00000cb,
+       NT_STATUS_BAD_NETWORK_NAME = 0xc00000cc,
+       NT_STATUS_TOO_MANY_NAMES = 0xc00000cd,
+       NT_STATUS_TOO_MANY_SESSIONS = 0xc00000ce,
+       NT_STATUS_SHARING_PAUSED = 0xc00000cf,
+       NT_STATUS_REQUEST_NOT_ACCEPTED = 0xc00000d0,
+       NT_STATUS_REDIRECTOR_PAUSED = 0xc00000d1,
+       NT_STATUS_NET_WRITE_FAULT = 0xc00000d2,
+       NT_STATUS_PROFILING_AT_LIMIT = 0xc00000d3,
+       NT_STATUS_NOT_SAME_DEVICE = 0xc00000d4,
+       NT_STATUS_FILE_RENAMED = 0xc00000d5,
+       NT_STATUS_VIRTUAL_CIRCUIT_CLOSED = 0xc00000d6,
+       NT_STATUS_NO_SECURITY_ON_OBJECT = 0xc00000d7,
+       NT_STATUS_CANT_WAIT = 0xc00000d8,
+       NT_STATUS_PIPE_EMPTY = 0xc00000d9,
+       NT_STATUS_CANT_ACCESS_DOMAIN_INFO = 0xc00000da,
+       NT_STATUS_CANT_TERMINATE_SELF = 0xc00000db,
+       NT_STATUS_INVALID_SERVER_STATE = 0xc00000dc,
+       NT_STATUS_INVALID_DOMAIN_STATE = 0xc00000dd,
+       NT_STATUS_INVALID_DOMAIN_ROLE = 0xc00000de,
+       NT_STATUS_NO_SUCH_DOMAIN = 0xc00000df,
+       NT_STATUS_DOMAIN_EXISTS = 0xc00000e0,
+       NT_STATUS_DOMAIN_LIMIT_EXCEEDED = 0xc00000e1,
+       NT_STATUS_OPLOCK_NOT_GRANTED = 0xc00000e2,
+       NT_STATUS_INVALID_OPLOCK_PROTOCOL = 0xc00000e3,
+       NT_STATUS_INTERNAL_DB_CORRUPTION = 0xc00000e4,
+       NT_STATUS_INTERNAL_ERROR = 0xc00000e5,
+       NT_STATUS_GENERIC_NOT_MAPPED = 0xc00000e6,
+       NT_STATUS_BAD_DESCRIPTOR_FORMAT = 0xc00000e7,
+       NT_STATUS_INVALID_USER_BUFFER = 0xc00000e8,
+       NT_STATUS_UNEXPECTED_IO_ERROR = 0xc00000e9,
+       NT_STATUS_UNEXPECTED_MM_CREATE_ERR = 0xc00000ea,
+       NT_STATUS_UNEXPECTED_MM_MAP_ERROR = 0xc00000eb,
+       NT_STATUS_UNEXPECTED_MM_EXTEND_ERR = 0xc00000ec,
+       NT_STATUS_NOT_LOGON_PROCESS = 0xc00000ed,
+       NT_STATUS_LOGON_SESSION_EXISTS = 0xc00000ee,
+       NT_STATUS_INVALID_PARAMETER_1 = 0xc00000ef,
+       NT_STATUS_INVALID_PARAMETER_2 = 0xc00000f0,
+       NT_STATUS_INVALID_PARAMETER_3 = 0xc00000f1,
+       NT_STATUS_INVALID_PARAMETER_4 = 0xc00000f2,
+       NT_STATUS_INVALID_PARAMETER_5 = 0xc00000f3,
+       NT_STATUS_INVALID_PARAMETER_6 = 0xc00000f4,
+       NT_STATUS_INVALID_PARAMETER_7 = 0xc00000f5,
+       NT_STATUS_INVALID_PARAMETER_8 = 0xc00000f6,
+       NT_STATUS_INVALID_PARAMETER_9 = 0xc00000f7,
+       NT_STATUS_INVALID_PARAMETER_10 = 0xc00000f8,
+       NT_STATUS_INVALID_PARAMETER_11 = 0xc00000f9,
+       NT_STATUS_INVALID_PARAMETER_12 = 0xc00000fa,
+       NT_STATUS_REDIRECTOR_NOT_STARTED = 0xc00000fb,
+       NT_STATUS_REDIRECTOR_STARTED = 0xc00000fc,
+       NT_STATUS_STACK_OVERFLOW = 0xc00000fd,
+       NT_STATUS_NO_SUCH_PACKAGE = 0xc00000fe,
+       NT_STATUS_BAD_FUNCTION_TABLE = 0xc00000ff,
+       NT_STATUS_DIRECTORY_NOT_EMPTY = 0xc0000101,
+       NT_STATUS_FILE_CORRUPT_ERROR = 0xc0000102,
+       NT_STATUS_NOT_A_DIRECTORY = 0xc0000103,
+       NT_STATUS_BAD_LOGON_SESSION_STATE = 0xc0000104,
+       NT_STATUS_LOGON_SESSION_COLLISION = 0xc0000105,
+       NT_STATUS_NAME_TOO_LONG = 0xc0000106,
+       NT_STATUS_FILES_OPEN = 0xc0000107,
+       NT_STATUS_CONNECTION_IN_USE = 0xc0000108,
+       NT_STATUS_MESSAGE_NOT_FOUND = 0xc0000109,
+       NT_STATUS_PROCESS_IS_TERMINATING = 0xc000010a,
+       NT_STATUS_INVALID_LOGON_TYPE = 0xc000010b,
+       NT_STATUS_NO_GUID_TRANSLATION = 0xc000010c,
+       NT_STATUS_CANNOT_IMPERSONATE = 0xc000010d,
+       NT_STATUS_IMAGE_ALREADY_LOADED = 0xc000010e,
+       NT_STATUS_ABIOS_NOT_PRESENT = 0xc000010f,
+       NT_STATUS_ABIOS_LID_NOT_EXIST = 0xc0000110,
+       NT_STATUS_ABIOS_LID_ALREADY_OWNED = 0xc0000111,
+       NT_STATUS_ABIOS_NOT_LID_OWNER = 0xc0000112,
+       NT_STATUS_ABIOS_INVALID_COMMAND = 0xc0000113,
+       NT_STATUS_ABIOS_INVALID_LID = 0xc0000114,
+       NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE = 0xc0000115,
+       NT_STATUS_ABIOS_INVALID_SELECTOR = 0xc0000116,
+       NT_STATUS_NO_LDT = 0xc0000117,
+       NT_STATUS_INVALID_LDT_SIZE = 0xc0000118,
+       NT_STATUS_INVALID_LDT_OFFSET = 0xc0000119,
+       NT_STATUS_INVALID_LDT_DESCRIPTOR = 0xc000011a,
+       NT_STATUS_INVALID_IMAGE_NE_FORMAT = 0xc000011b,
+       NT_STATUS_RXACT_INVALID_STATE = 0xc000011c,
+       NT_STATUS_RXACT_COMMIT_FAILURE = 0xc000011d,
+       NT_STATUS_MAPPED_FILE_SIZE_ZERO = 0xc000011e,
+       NT_STATUS_TOO_MANY_OPENED_FILES = 0xc000011f,
+       NT_STATUS_CANCELLED = 0xc0000120,
+       NT_STATUS_CANNOT_DELETE = 0xc0000121,
+       NT_STATUS_INVALID_COMPUTER_NAME = 0xc0000122,
+       NT_STATUS_FILE_DELETED = 0xc0000123,
+       NT_STATUS_SPECIAL_ACCOUNT = 0xc0000124,
+       NT_STATUS_SPECIAL_GROUP = 0xc0000125,
+       NT_STATUS_SPECIAL_USER = 0xc0000126,
+       NT_STATUS_MEMBERS_PRIMARY_GROUP = 0xc0000127,
+       NT_STATUS_FILE_CLOSED = 0xc0000128,
+       NT_STATUS_TOO_MANY_THREADS = 0xc0000129,
+       NT_STATUS_THREAD_NOT_IN_PROCESS = 0xc000012a,
+       NT_STATUS_TOKEN_ALREADY_IN_USE = 0xc000012b,
+       NT_STATUS_PAGEFILE_QUOTA_EXCEEDED = 0xc000012c,
+       NT_STATUS_COMMITMENT_LIMIT = 0xc000012d,
+       NT_STATUS_INVALID_IMAGE_LE_FORMAT = 0xc000012e,
+       NT_STATUS_INVALID_IMAGE_NOT_MZ = 0xc000012f,
+       NT_STATUS_INVALID_IMAGE_PROTECT = 0xc0000130,
+       NT_STATUS_INVALID_IMAGE_WIN_16 = 0xc0000131,
+       NT_STATUS_LOGON_SERVER_CONFLICT = 0xc0000132,
+       NT_STATUS_TIME_DIFFERENCE_AT_DC = 0xc0000133,
+       NT_STATUS_SYNCHRONIZATION_REQUIRED = 0xc0000134,
+       NT_STATUS_DLL_NOT_FOUND = 0xc0000135,
+       NT_STATUS_OPEN_FAILED = 0xc0000136,
+       NT_STATUS_IO_PRIVILEGE_FAILED = 0xc0000137,
+       NT_STATUS_ORDINAL_NOT_FOUND = 0xc0000138,
+       NT_STATUS_ENTRYPOINT_NOT_FOUND = 0xc0000139,
+       NT_STATUS_CONTROL_C_EXIT = 0xc000013a,
+       NT_STATUS_LOCAL_DISCONNECT = 0xc000013b,
+       NT_STATUS_REMOTE_DISCONNECT = 0xc000013c,
+       NT_STATUS_REMOTE_RESOURCES = 0xc000013d,
+       NT_STATUS_LINK_FAILED = 0xc000013e,
+       NT_STATUS_LINK_TIMEOUT = 0xc000013f,
+       NT_STATUS_INVALID_CONNECTION = 0xc0000140,
+       NT_STATUS_INVALID_ADDRESS = 0xc0000141,
+       NT_STATUS_DLL_INIT_FAILED = 0xc0000142,
+       NT_STATUS_MISSING_SYSTEMFILE = 0xc0000143,
+       NT_STATUS_UNHANDLED_EXCEPTION = 0xc0000144,
+       NT_STATUS_APP_INIT_FAILURE = 0xc0000145,
+       NT_STATUS_PAGEFILE_CREATE_FAILED = 0xc0000146,
+       NT_STATUS_NO_PAGEFILE = 0xc0000147,
+       NT_STATUS_INVALID_LEVEL = 0xc0000148,
+       NT_STATUS_WRONG_PASSWORD_CORE = 0xc0000149,
+       NT_STATUS_ILLEGAL_FLOAT_CONTEXT = 0xc000014a,
+       NT_STATUS_PIPE_BROKEN = 0xc000014b,
+       NT_STATUS_REGISTRY_CORRUPT = 0xc000014c,
+       NT_STATUS_REGISTRY_IO_FAILED = 0xc000014d,
+       NT_STATUS_NO_EVENT_PAIR = 0xc000014e,
+       NT_STATUS_UNRECOGNIZED_VOLUME = 0xc000014f,
+       NT_STATUS_SERIAL_NO_DEVICE_INITED = 0xc0000150,
+       NT_STATUS_NO_SUCH_ALIAS = 0xc0000151,
+       NT_STATUS_MEMBER_NOT_IN_ALIAS = 0xc0000152,
+       NT_STATUS_MEMBER_IN_ALIAS = 0xc0000153,
+       NT_STATUS_ALIAS_EXISTS = 0xc0000154,
+       NT_STATUS_LOGON_NOT_GRANTED = 0xc0000155,
+       NT_STATUS_TOO_MANY_SECRETS = 0xc0000156,
+       NT_STATUS_SECRET_TOO_LONG = 0xc0000157,
+       NT_STATUS_INTERNAL_DB_ERROR = 0xc0000158,
+       NT_STATUS_FULLSCREEN_MODE = 0xc0000159,
+       NT_STATUS_TOO_MANY_CONTEXT_IDS = 0xc000015a,
+       NT_STATUS_LOGON_TYPE_NOT_GRANTED = 0xc000015b,
+       NT_STATUS_NOT_REGISTRY_FILE = 0xc000015c,
+       NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED = 0xc000015d,
+       NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR = 0xc000015e,
+       NT_STATUS_FT_MISSING_MEMBER = 0xc000015f,
+       NT_STATUS_ILL_FORMED_SERVICE_ENTRY = 0xc0000160,
+       NT_STATUS_ILLEGAL_CHARACTER = 0xc0000161,
+       NT_STATUS_UNMAPPABLE_CHARACTER = 0xc0000162,
+       NT_STATUS_UNDEFINED_CHARACTER = 0xc0000163,
+       NT_STATUS_FLOPPY_VOLUME = 0xc0000164,
+       NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND = 0xc0000165,
+       NT_STATUS_FLOPPY_WRONG_CYLINDER = 0xc0000166,
+       NT_STATUS_FLOPPY_UNKNOWN_ERROR = 0xc0000167,
+       NT_STATUS_FLOPPY_BAD_REGISTERS = 0xc0000168,
+       NT_STATUS_DISK_RECALIBRATE_FAILED = 0xc0000169,
+       NT_STATUS_DISK_OPERATION_FAILED = 0xc000016a,
+       NT_STATUS_DISK_RESET_FAILED = 0xc000016b,
+       NT_STATUS_SHARED_IRQ_BUSY = 0xc000016c,
+       NT_STATUS_FT_ORPHANING = 0xc000016d,
+       NT_STATUS_PARTITION_FAILURE = 0xc0000172,
+       NT_STATUS_INVALID_BLOCK_LENGTH = 0xc0000173,
+       NT_STATUS_DEVICE_NOT_PARTITIONED = 0xc0000174,
+       NT_STATUS_UNABLE_TO_LOCK_MEDIA = 0xc0000175,
+       NT_STATUS_UNABLE_TO_UNLOAD_MEDIA = 0xc0000176,
+       NT_STATUS_EOM_OVERFLOW = 0xc0000177,
+       NT_STATUS_NO_MEDIA = 0xc0000178,
+       NT_STATUS_NO_SUCH_MEMBER = 0xc000017a,
+       NT_STATUS_INVALID_MEMBER = 0xc000017b,
+       NT_STATUS_KEY_DELETED = 0xc000017c,
+       NT_STATUS_NO_LOG_SPACE = 0xc000017d,
+       NT_STATUS_TOO_MANY_SIDS = 0xc000017e,
+       NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED = 0xc000017f,
+       NT_STATUS_KEY_HAS_CHILDREN = 0xc0000180,
+       NT_STATUS_CHILD_MUST_BE_VOLATILE = 0xc0000181,
+       NT_STATUS_DEVICE_CONFIGURATION_ERROR = 0xc0000182,
+       NT_STATUS_DRIVER_INTERNAL_ERROR = 0xc0000183,
+       NT_STATUS_INVALID_DEVICE_STATE = 0xc0000184,
+       NT_STATUS_IO_DEVICE_ERROR = 0xc0000185,
+       NT_STATUS_DEVICE_PROTOCOL_ERROR = 0xc0000186,
+       NT_STATUS_BACKUP_CONTROLLER = 0xc0000187,
+       NT_STATUS_LOG_FILE_FULL = 0xc0000188,
+       NT_STATUS_TOO_LATE = 0xc0000189,
+       NT_STATUS_NO_TRUST_LSA_SECRET = 0xc000018a,
+       NT_STATUS_NO_TRUST_SAM_ACCOUNT = 0xc000018b,
+       NT_STATUS_TRUSTED_DOMAIN_FAILURE = 0xc000018c,
+       NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE = 0xc000018d,
+       NT_STATUS_EVENTLOG_FILE_CORRUPT = 0xc000018e,
+       NT_STATUS_EVENTLOG_CANT_START = 0xc000018f,
+       NT_STATUS_TRUST_FAILURE = 0xc0000190,
+       NT_STATUS_MUTANT_LIMIT_EXCEEDED = 0xc0000191,
+       NT_STATUS_NETLOGON_NOT_STARTED = 0xc0000192,
+       NT_STATUS_ACCOUNT_EXPIRED = 0xc0000193,
+       NT_STATUS_POSSIBLE_DEADLOCK = 0xc0000194,
+       NT_STATUS_NETWORK_CREDENTIAL_CONFLICT = 0xc0000195,
+       NT_STATUS_REMOTE_SESSION_LIMIT = 0xc0000196,
+       NT_STATUS_EVENTLOG_FILE_CHANGED = 0xc0000197,
+       NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT = 0xc0000198,
+       NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT = 0xc0000199,
+       NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT = 0xc000019a,
+       NT_STATUS_DOMAIN_TRUST_INCONSISTENT = 0xc000019b,
+       NT_STATUS_FS_DRIVER_REQUIRED = 0xc000019c,
+       NT_STATUS_NO_USER_SESSION_KEY = 0xc0000202,
+       NT_STATUS_USER_SESSION_DELETED = 0xc0000203,
+       NT_STATUS_RESOURCE_LANG_NOT_FOUND = 0xc0000204,
+       NT_STATUS_INSUFF_SERVER_RESOURCES = 0xc0000205,
+       NT_STATUS_INVALID_BUFFER_SIZE = 0xc0000206,
+       NT_STATUS_INVALID_ADDRESS_COMPONENT = 0xc0000207,
+       NT_STATUS_INVALID_ADDRESS_WILDCARD = 0xc0000208,
+       NT_STATUS_TOO_MANY_ADDRESSES = 0xc0000209,
+       NT_STATUS_ADDRESS_ALREADY_EXISTS = 0xc000020a,
+       NT_STATUS_ADDRESS_CLOSED = 0xc000020b,
+       NT_STATUS_CONNECTION_DISCONNECTED = 0xc000020c,
+       NT_STATUS_CONNECTION_RESET = 0xc000020d,
+       NT_STATUS_TOO_MANY_NODES = 0xc000020e,
+       NT_STATUS_TRANSACTION_ABORTED = 0xc000020f,
+       NT_STATUS_TRANSACTION_TIMED_OUT = 0xc0000210,
+       NT_STATUS_TRANSACTION_NO_RELEASE = 0xc0000211,
+       NT_STATUS_TRANSACTION_NO_MATCH = 0xc0000212,
+       NT_STATUS_TRANSACTION_RESPONDED = 0xc0000213,
+       NT_STATUS_TRANSACTION_INVALID_ID = 0xc0000214,
+       NT_STATUS_TRANSACTION_INVALID_TYPE = 0xc0000215,
+       NT_STATUS_NOT_SERVER_SESSION = 0xc0000216,
+       NT_STATUS_NOT_CLIENT_SESSION = 0xc0000217,
+       NT_STATUS_CANNOT_LOAD_REGISTRY_FILE = 0xc0000218,
+       NT_STATUS_DEBUG_ATTACH_FAILED = 0xc0000219,
+       NT_STATUS_SYSTEM_PROCESS_TERMINATED = 0xc000021a,
+       NT_STATUS_DATA_NOT_ACCEPTED = 0xc000021b,
+       NT_STATUS_NO_BROWSER_SERVERS_FOUND = 0xc000021c,
+       NT_STATUS_VDM_HARD_ERROR = 0xc000021d,
+       NT_STATUS_DRIVER_CANCEL_TIMEOUT = 0xc000021e,
+       NT_STATUS_REPLY_MESSAGE_MISMATCH = 0xc000021f,
+       NT_STATUS_MAPPED_ALIGNMENT = 0xc0000220,
+       NT_STATUS_IMAGE_CHECKSUM_MISMATCH = 0xc0000221,
+       NT_STATUS_LOST_WRITEBEHIND_DATA = 0xc0000222,
+       NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID = 0xc0000223,
+       NT_STATUS_PASSWORD_MUST_CHANGE = 0xc0000224,
+       NT_STATUS_NOT_FOUND = 0xc0000225,
+       NT_STATUS_NOT_TINY_STREAM = 0xc0000226,
+       NT_STATUS_RECOVERY_FAILURE = 0xc0000227,
+       NT_STATUS_STACK_OVERFLOW_READ = 0xc0000228,
+       NT_STATUS_FAIL_CHECK = 0xc0000229,
+       NT_STATUS_DUPLICATE_OBJECTID = 0xc000022a,
+       NT_STATUS_OBJECTID_EXISTS = 0xc000022b,
+       NT_STATUS_CONVERT_TO_LARGE = 0xc000022c,
+       NT_STATUS_RETRY = 0xc000022d,
+       NT_STATUS_FOUND_OUT_OF_SCOPE = 0xc000022e,
+       NT_STATUS_ALLOCATE_BUCKET = 0xc000022f,
+       NT_STATUS_PROPSET_NOT_FOUND = 0xc0000230,
+       NT_STATUS_MARSHALL_OVERFLOW = 0xc0000231,
+       NT_STATUS_INVALID_VARIANT = 0xc0000232,
+       NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND = 0xc0000233,
+       NT_STATUS_ACCOUNT_LOCKED_OUT = 0xc0000234,
+       NT_STATUS_HANDLE_NOT_CLOSABLE = 0xc0000235,
+       NT_STATUS_CONNECTION_REFUSED = 0xc0000236,
+       NT_STATUS_GRACEFUL_DISCONNECT = 0xc0000237,
+       NT_STATUS_ADDRESS_ALREADY_ASSOCIATED = 0xc0000238,
+       NT_STATUS_ADDRESS_NOT_ASSOCIATED = 0xc0000239,
+       NT_STATUS_CONNECTION_INVALID = 0xc000023a,
+       NT_STATUS_CONNECTION_ACTIVE = 0xc000023b,
+       NT_STATUS_NETWORK_UNREACHABLE = 0xc000023c,
+       NT_STATUS_HOST_UNREACHABLE = 0xc000023d,
+       NT_STATUS_PROTOCOL_UNREACHABLE = 0xc000023e,
+       NT_STATUS_PORT_UNREACHABLE = 0xc000023f,
+       NT_STATUS_REQUEST_ABORTED = 0xc0000240,
+       NT_STATUS_CONNECTION_ABORTED = 0xc0000241,
+       NT_STATUS_BAD_COMPRESSION_BUFFER = 0xc0000242,
+       NT_STATUS_USER_MAPPED_FILE = 0xc0000243,
+       NT_STATUS_AUDIT_FAILED = 0xc0000244,
+       NT_STATUS_TIMER_RESOLUTION_NOT_SET = 0xc0000245,
+       NT_STATUS_CONNECTION_COUNT_LIMIT = 0xc0000246,
+       NT_STATUS_LOGIN_TIME_RESTRICTION = 0xc0000247,
+       NT_STATUS_LOGIN_WKSTA_RESTRICTION = 0xc0000248,
+       NT_STATUS_IMAGE_MP_UP_MISMATCH = 0xc0000249,
+       NT_STATUS_INSUFFICIENT_LOGON_INFO = 0xc0000250,
+       NT_STATUS_BAD_DLL_ENTRYPOINT = 0xc0000251,
+       NT_STATUS_BAD_SERVICE_ENTRYPOINT = 0xc0000252,
+       NT_STATUS_LPC_REPLY_LOST = 0xc0000253,
+       NT_STATUS_IP_ADDRESS_CONFLICT1 = 0xc0000254,
+       NT_STATUS_IP_ADDRESS_CONFLICT2 = 0xc0000255,
+       NT_STATUS_REGISTRY_QUOTA_LIMIT = 0xc0000256,
+       NT_STATUS_PATH_NOT_COVERED = 0xc0000257,
+       NT_STATUS_NO_CALLBACK_ACTIVE = 0xc0000258,
+       NT_STATUS_LICENSE_QUOTA_EXCEEDED = 0xc0000259,
+       NT_STATUS_PWD_TOO_SHORT = 0xc000025a,
+       NT_STATUS_PWD_TOO_RECENT = 0xc000025b,
+       NT_STATUS_PWD_HISTORY_CONFLICT = 0xc000025c,
+       NT_STATUS_PLUGPLAY_NO_DEVICE = 0xc000025e,
+       NT_STATUS_UNSUPPORTED_COMPRESSION = 0xc000025f,
+       NT_STATUS_INVALID_HW_PROFILE = 0xc0000260,
+       NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH = 0xc0000261,
+       NT_STATUS_DRIVER_ORDINAL_NOT_FOUND = 0xc0000262,
+       NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND = 0xc0000263,
+       NT_STATUS_RESOURCE_NOT_OWNED = 0xc0000264,
+       NT_STATUS_TOO_MANY_LINKS = 0xc0000265,
+       NT_STATUS_QUOTA_LIST_INCONSISTENT = 0xc0000266,
+       NT_STATUS_FILE_IS_OFFLINE = 0xc0000267,
+       NT_STATUS_DS_NO_MORE_RIDS = 0xc00002a8,
+       NT_STATUS_NOT_A_REPARSE_POINT = 0xc0000275,
+       NT_STATUS_NO_SUCH_JOB = 0xc000EDE
+}
+
+for i, v in pairs(status_codes) do
+       status_names[v] = i
+end
+
Index: nselib/msrpc.lua
===================================================================
--- nselib/msrpc.lua    (revision 10583)
+++ nselib/msrpc.lua    (working copy)
@@ -11,18 +11,25 @@
 --\n
 -- After that, you're free to call any function that's part of that interface. In
 -- other words, if you bind to the SAMR interface, you can only call the samr_
--- functions.\n
+-- functions, for lsa_ functions, bind to the LSA interface, etc.\n
 --\n
 -- Although functions can be called in any order, many functions depend on the
 -- value returned by other functions. I indicate those in the function comments, 
--- so keep an eye out. \n
+-- so keep an eye out.\n
 --\n
 -- Something to note is that these functions, for the most part, return a whole ton
--- of stuff in an array. I basically wrote them to return every possible value
+-- of stuff in a table. I basically wrote them to return every possible value
 -- they get their hands on. I don't expect that most of them will be used, and I'm 
 -- not going to document them all in the function header; rather, I will document
 -- the elements in the table that are more useful, you'll have to look at the actual
--- code (or the table) to see what else is available. \n
+-- code (or the table) to see what else is available.\n
+--\n
+-- When implementing this, I used Wireshark's output significantly, as well as Samba's
+-- "idl" files for reference:\n
+--  http://websvn.samba.org/cgi-bin/viewcvs.cgi/branches/SAMBA_4_0/source/librpc/idl/ \n
+-- I'm not a lawyer, but I don't expect that this is a breach of Samba's copyright -- 
+-- if it is, please talk to me and I'll make arrangements to re-license this or to 
+-- remove references to Samba. 
 --
 --@author Ron Bowes <ron () skullsecurity net>
 --@copyright See nmap's COPYING for licence
@@ -37,26 +44,26 @@
 
 -- The path, UUID, and version for SAMR
 SAMR_PATH       = "\\samr"
-SAMR_UUID       = bin.pack("CCCCCCCCCCCCCCCC", 0x78, 0x57, 0x34, 0x12, 0x34, 0x12, 0xcd, 0xab, 0xef, 0x00, 0x01, 0x23, 
0x45, 0x67, 0x89, 0xac)
+SAMR_UUID       = string.char(0x78, 0x57, 0x34, 0x12, 0x34, 0x12, 0xcd, 0xab, 0xef, 0x00, 0x01, 0x23, 0x45, 0x67, 
0x89, 0xac)
 SAMR_VERSION    = 0x01
 
 -- The path, UUID, and version for SRVSVC
 SRVSVC_PATH     = "\\srvsvc"
-SRVSVC_UUID     = bin.pack("CCCCCCCCCCCCCCCC", 0xc8, 0x4f, 0x32, 0x4b, 0x70, 0x16, 0xd3, 0x01, 0x12, 0x78, 0x5a, 0x47, 
0xbf, 0x6e, 0xe1, 0x88)
+SRVSVC_UUID     = string.char(0xc8, 0x4f, 0x32, 0x4b, 0x70, 0x16, 0xd3, 0x01, 0x12, 0x78, 0x5a, 0x47, 0xbf, 0x6e, 
0xe1, 0x88)
 SRVSVC_VERSION  = 0x03
 
 -- The path, UUID, and version for LSA
 LSA_PATH        = "\\lsarpc"
-LSA_UUID        = bin.pack("CCCCCCCCCCCCCCCC", 0x78, 0x57, 0x34, 0x12, 0x34, 0x12, 0xcd, 0xab, 0xef, 0x00, 0x01, 0x23, 
0x45, 0x67, 0x89, 0xab)
+LSA_UUID        = string.char(0x78, 0x57, 0x34, 0x12, 0x34, 0x12, 0xcd, 0xab, 0xef, 0x00, 0x01, 0x23, 0x45, 0x67, 
0x89, 0xab)
 LSA_VERSION     = 0
 
 -- This is the only transfer syntax I've seen in the wild, not that I've looked hard. It seems to work well. 
-TRANSFER_SYNTAX = bin.pack("CCCCCCCCCCCCCCCC", 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 
0x2b, 0x10, 0x48, 0x60)
+TRANSFER_SYNTAX = string.char(0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 
0x48, 0x60)
 
 -- The 'referent_id' value is ignored, as far as I can tell, so this value is passed for it. No, it isn't random. :)
 REFERENT_ID = 0x50414d4e
 
--- A few error codes
+-- A few error codes [TODO: Put these in a table, like SMB's error codes]
 STATUS_SOME_NOT_MAPPED        = 0x00000107
 STATUS_INVALID_PARAMETER      = 0xC000000D
 STATUS_ACCESS_DENIED          = 0xC0000022 
@@ -96,6 +103,7 @@
 --- Convert a string to fake unicode (ascii with null characters between them), optionally add a null terminator, 
 --  and optionally align it to 4-byte boundaries. This is frequently used in MSRPC calls, so I put it here, but
 --  it might be a good idea to move this function (and the converse one below) into a separate library. 
+--
 --@param string The string to convert. 
 --@param do_null [optional]  Add a null-terminator to the unicode string. Default false. 
 --@param do_align [optional] Align the string to a multiple of 4 bytes. Default false. 
@@ -133,6 +141,7 @@
 
 --- Read a unicode string from a buffer, similar to how bin.unpack() would, optionally eat the null terminator, 
 --  and optionally align it to 4-byte boundaries. 
+--
 --@param buffer   The buffer to read from, typically the full 'arguments' value for MSRPC
 --@param pos      The position in the buffer to start (just like bin.unpack())
 --@param length   The number of ascii characters that will be read (including the null, if do_null is set). 
@@ -201,73 +210,73 @@
 end
 
 
---- This is a wrapper around the SMB class, designed to get SMB going quickly in a script. This will
+--- This is a wrapper around the SMB class, designed to get SMB going quickly for MSRPC calls. This will
 --  connect to the SMB server, negotiate the protocol, open a session, connect to the IPC$ share, and
---  open the named pipe given by 'path'. When this successfully returns, the socket can be immediately 
---  used for MSRPC. 
+--  open the named pipe given by 'path'. When this successfully returns, the 'smbstate' table can be immediately 
+--  used for MSRPC (the bind() function should be called right after). \n
+--\n
+-- Note that the smbstate table is the same one used in the SMB files (obviously), so it will contain
+-- the various responses/information places in there by SMB functions. 
 --
 --@param host The host object. 
 --@param path The path to the named pipe; for example, msrpc.SAMR_PATH or msrpc.SRVSVC_PATH. 
---@return (status, socket, uid, tid, fid, negotiate_result, session_result, tree_result, create_result) 
---        if status is false, socket is an error message. Otherwise, the rest of the results are 
---        returned. 
+--@return (status, smbstate) if status is false, smbstate is an error message. Otherwise, smbstate is
+--        required for all further calls. 
 function start_smb(host, path)
-       local status, socket, negotiate_result, session_result, tree_result, create_result
+       local smbstate
+       local status, err
 
        -- Begin the SMB session
-    status, socket = smb.start(host)
+    status, smbstate = smb.start(host)
     if(status == false) then
-        return false, socket
+        return false, smbstate
     end
 
        -- Negotiate the protocol
-    status, negotiate_result = smb.negotiate_protocol(socket)
+    status, err = smb.negotiate_protocol(smbstate)
     if(status == false) then
-        smb.stop(socket)   
-        return false, negotiate_result
+        smb.stop(smbstate)   
+        return false, err
     end
 
     -- Start up a null session
-    status, session_result = smb.start_session(socket, "", negotiate_result['session_key'], 
negotiate_result['capabilities'])
+    status, err = smb.start_session(smbstate)
     if(status == false) then
-        smb.stop(socket)   
-        return false, session_result
+        smb.stop(smbstate)   
+        return false, err
     end
 
     -- Connect to IPC$ share
-    status, tree_result = smb.tree_connect(socket, "IPC$", session_result['uid'])
+    status, err = smb.tree_connect(smbstate, "IPC$")
     if(status == false) then
-        smb.stop(socket, session_result['uid'])   
-        return false, tree_result
+        smb.stop(smbstate)   
+        return false, err
     end
 
     -- Try to connect to requested pipe
-    status, create_result = smb.create_file(socket, path, session_result['uid'], tree_result['tid'])
+    status, err = smb.create_file(smbstate, path)
     if(status == false) then
-        smb.stop(socket, session_result['uid'], tree_result['tid'])   
-        return false, create_result
+        smb.stop(smbstate)   
+        return false, err
     end
 
        -- Return everything
-       return true, socket, session_result['uid'], tree_result['tid'], create_result['fid'], negotiate_result, 
session_result, tree_result, create_result
+       return true, smbstate
 end
 
---- A wrapper around the smb.stop() function. I only created it to add symmetry, so the code uses
---  the same class to start/stop the session. In the future, this may be expanded to close
---  handles before exiting. 
+--- A wrapper around the smb.stop() function. I only created it to add symmetry, so client code
+--  doesn't have to call both msrpc and smb functions.
 --
---@param socket The socket to close. 
---@param uid    The UserID, which will be logged off before closing the socket. 
---@param tid    The TreeID, which will be disconnected before closing the socket. 
-function stop_smb(socket, uid, tid)
-       smb.stop(socket, uid, tid)
+--@param smbstate The SMB state table. 
+function stop_smb(state)
+       smb.stop(state)
 end
 
 --- Bind to a MSRPC interface. Two common interfaces are SAML and SRVSVC, and can be found as
 --  constants at the top of this file. Once this function has successfully returned, any MSRPC
 --  call can be made (provided it doesn't depend on results from other MSRPC calls). 
 --
---@param socket The socket in the appropriate state
+--@param smbstate The SMB state table
 --@param interface_uuid The interface to bind to. There are constants defined for these (SAMR_UUID, 
 --       etc.)
 --@param interface_version The interface version to use. There are constants at the top (SAMR_VERSION, 
@@ -275,12 +284,9 @@
 --@param transfer_syntax The transfer syntax to use. I don't really know what this is, but the value
 --       was always the same on my tests. You can use the constant at the top (TRANSFER_SYNTAX), or
 --       just set this parameter to 'nil'. 
---@param uid The UserID we're sending the packets as
---@param tid The TreeID we're sending the packets to
---@param fid The FileID we're sending the packets to
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a 
 --        table of values, none of which are especially useful. 
-function bind(socket, interface_uuid, interface_version, transfer_syntax, uid, tid, fid)
+function bind(smbstate, interface_uuid, interface_version, transfer_syntax)
        local i
        local status, result
        local parameters, data
@@ -323,7 +329,7 @@
                                2                  -- Syntax version
                        )
 
-       status, result = smb.send_transaction(socket, 0x0026, "", data, uid, tid, fid)
+       status, result = smb.send_transaction(smbstate, 0x0026, "", data)
        if(status ~= true) then
                return false, result
        end
@@ -367,7 +373,7 @@
 
        -- Read the secondary address
        pos, response['secondary_address'] = bin.unpack(string.format("<A%d", response['secondary_address_length']), 
data, pos)
-       pos = pos + ((4 - ((pos - 1) % 4)) % 4); -- Alignment -- don't ask how I came up with this, it was a lot of 
drawing
+       pos = pos + ((4 - ((pos - 1) % 4)) % 4); -- Alignment -- don't ask how I came up with this, it was a lot of 
drawing, and there's probably a far better way
 
        -- Read the number of results
        pos, response['num_results'] = bin.unpack("<C", data, pos)
@@ -394,15 +400,12 @@
 --\n
 -- There's a reason that SMB is sometimes considered to be between layer 4 and 7 on the OSI model. :)\n
 --
---@param socket    The socket, in the correct state (SMB has been started, and bind() has been called). 
+--@param smbstate  The SMB state table (after bind() has been called). 
 --@param opnum     The operating number (ie, the function). Find this in the MSRPC documentation or with a packet 
logger. 
 --@param arguments The marshalled arguments to pass to the function. Currently, marshalling is all done manually. 
---@param uid       The UserID we're sending the packets as
---@param tid       The TreeID we're sending the packets to
---@param fid       The FileID we're sending the packets to
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the 
most
 --        useful one being 'arguments', which are the values returned by the server. 
-local function call_function(socket, opnum, arguments, uid, tid, fid)
+local function call_function(smbstate, opnum, arguments)
        local i
        local status, result
        local parameters, data
@@ -427,7 +430,7 @@
        stdnse.print_debug(3, "MSRPC: Calling function 0x%02x with %d bytes of arguments", string.len(arguments), opnum)
 
        -- Pass the information up to the smb layer
-       status, result = smb.send_transaction(socket, 0x0026, "", data, uid, tid, fid)
+       status, result = smb.send_transaction(smbstate, 0x0026, "", data)
        if(status ~= true) then
                return false, result
        end
@@ -473,14 +476,11 @@
 ---Call the MSRPC function netshareenumall() on the remote system. This function basically returns a list of all the 
shares
 -- on the system. 
 --
---@param socket The socket, with a proper MSRPC connection
---@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it)
---@param uid    The UserID we're sending the packets as
---@param tid    The TreeID we're sending the packets to
---@param fid    The FileID we're sending the packets to
+--@param smbstate The SMB state table
+--@param server   The IP or Hostname of the server (seems to be ignored but it's a good idea to have it)
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the 
most
 --        useful one being 'shares', which is a list of the system's shares. 
-function srvsvc_netshareenumall(socket, server, uid, tid, fid)
+function srvsvc_netshareenumall(smbstate, server)
        local i, j
        local status, result
        local arguments
@@ -523,7 +523,7 @@
 
 
        -- Do the call
-       status, result = call_function(socket, 0x0F, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x0F, arguments)
        if(status ~= true) then
                return false, result
        end
@@ -581,14 +581,11 @@
 ---Call the connect4() function, to obtain a "connect handle". This must be done before calling many 
 -- of the SAMR functions. 
 --
---@param socket The socket, with a proper MSRPC connection
---@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it)
---@param uid    The UserID we're sending the packets as
---@param tid    The TreeID we're sending the packets to
---@param fid    The FileID we're sending the packets to
+--@param smbstate  The SMB state table
+--@param server    The IP or Hostname of the server (seems to be ignored but it's a good idea to have it)
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the 
most
 --        useful one being 'connect_handle', which is required to call other functions. 
-function samr_connect4(socket, server, uid, tid, fid)
+function samr_connect4(smbstate, server)
        local i, j
        local status, result
        local arguments
@@ -618,7 +615,7 @@
 
 
        -- Do the call
-       status, result = call_function(socket, 0x3E, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x3E, arguments)
        if(status ~= true) then
                return false, result
        end
@@ -645,14 +642,11 @@
 
 ---Call the enumdomains() function, which returns a list of all domains in use by the system. 
 --
---@param socket         The socket, with a proper MSRPC connection
+--@param smbstate       The SMB state table
 --@param connect_handle The connect_handle, returned by samr_connect4()
---@param uid            The UserID we're sending the packets as
---@param tid            The TreeID we're sending the packets to
---@param fid            The FileID we're sending the packets to
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the 
most
 --        useful one being 'domains', which is a list of the domains. 
-function samr_enumdomains(socket, connect_handle, uid, tid, fid)
+function samr_enumdomains(smbstate, connect_handle)
        local i, j
        local status, result
        local arguments
@@ -675,7 +669,7 @@
 
 
        -- Do the call
-       status, result = call_function(socket, 0x06, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x06, arguments)
        if(status ~= true) then
                return false, result
        end
@@ -724,15 +718,12 @@
 ---Call the LookupDomain() function, which converts a domain's name into its sid, which is
 -- required to do operations on the domain. 
 --
---@param socket         The socket, with a proper MSRPC connection
+--@param smbstate       The SMB state table
 --@param connect_handle The connect_handle, returned by samr_connect4()
 --@param domain         The name of the domain (all domain names can be obtained with samr_enumdomains())
---@param uid            The UserID we're sending the packets as
---@param tid            The TreeID we're sending the packets to
---@param fid            The FileID we're sending the packets to
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the 
most
 --        useful one being 'sid', which is required to call other functions. 
-function samr_lookupdomain(socket, connect_handle, domain, uid, tid, fid)
+function samr_lookupdomain(smbstate, connect_handle, domain)
        local i, j
        local status, result
        local arguments
@@ -760,7 +751,7 @@
 
 
        -- Do the call
-       status, result = call_function(socket, 0x05, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x05, arguments)
        if(status ~= true) then
                return false, result
        end
@@ -796,15 +787,12 @@
 ---Call OpenDomain(), which returns a handle to the domain identified by the given sid. 
 -- This is required before calling certain functions. 
 --
---@param socket         The socket, with a proper MSRPC connection
+--@param smbstate       The SMB state table
 --@param connect_handle The connect_handle, returned by samr_connect4()
 --@param sid            The sid for the domain, returned by samr_lookupdomain()
---@param uid            The UserID we're sending the packets as
---@param tid            The TreeID we're sending the packets to
---@param fid            The FileID we're sending the packets to
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the 
most
 --        useful one being 'domain_handle', which is used to call other functions. 
-function samr_opendomain(socket, connect_handle, sid, uid, tid, fid)
+function samr_opendomain(smbstate, connect_handle, sid)
        local i, j
        local status, result
        local arguments
@@ -831,7 +819,7 @@
 
 
        -- Do the call
-       status, result = call_function(socket, 0x07, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x07, arguments)
        if(status ~= true) then
                return false, result
        end
@@ -859,7 +847,7 @@
 
 ---Call EnumDomainUsers(), which returns a list of users only. To get more information about the users, the 
 -- QueryDisplayInfo() function can be used. 
-function samr_enumdomainusers(socket, domain_handle, uid, tid, fid)
+function samr_enumdomainusers(smbstate, domain_handle)
        local i, j
        local status, result
        local arguments
@@ -886,7 +874,7 @@
 
 
        -- Do the call
-       status, result = call_function(socket, 0x0d, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x0d, arguments)
        if(status ~= true) then
                return false, result
        end
@@ -939,16 +927,13 @@
 -- only reading one user at a time, in a loop. So one call to this will actually send out a number of packets equal to 
the 
 -- number of users on the system. \n
 --
---@param socket         The socket, with a proper MSRPC connection
+--@param smbstate       The SMB state table
 --@param domain_handle  The domain handle, returned by samr_opendomain()
---@param uid            The UserID we're sending the packets as
---@param tid            The TreeID we're sending the packets to
---@param fid            The FileID we're sending the packets to
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the 
most
 --        useful ones being 'names', a list of all the usernames, and 'details', a further list of tables with the 
elements
 --        'name', 'fullname', and 'description' (note that any of them can be nil if the server didn't return a 
value). Finally,
 --        'flags' is the numeric flags for the user, while 'flags_list' is an array of strings, representing the flags.
-function samr_querydisplayinfo(socket, domain_handle, uid, tid, fid)
+function samr_querydisplayinfo(smbstate, domain_handle)
        local i, j
        local status, result
        local arguments
@@ -989,7 +974,7 @@
 
 
                -- Do the call
-               status, result = call_function(socket, 0x28, arguments, uid, tid, fid)
+               status, result = call_function(smbstate, 0x28, arguments)
                if(status ~= true) then
                        return false, result
                end
@@ -1083,13 +1068,10 @@
 
 ---Call QueryDomainInfo2(), which grabs various data about a domain. 
 --
---@param socket         The socket, with a proper MSRPC connection
+--@param smbstate       The SMB state table
 --@param domain_handle  The domain_handle, returned by samr_opendomain()
 --@param level          The level, which determines which type of information to query for. See the @return section
 --                      for details. 
---@param uid            The UserID we're sending the packets as
---@param tid            The TreeID we're sending the packets to
---@param fid            The FileID we're sending the packets to
 --@param response       [optional] A 'result' to add the entries to. This lets us call this function multiple times, 
 --                      for multiple levels, and keep the results in one place. 
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, 
@@ -1108,7 +1090,7 @@
 --         'lockout_duration' (in minutes)\n
 --         'lockout_window' (in minutes)\n
 --         'lockout_threshold' (in attempts)\n
-function samr_querydomaininfo2(socket, domain_handle, level, uid, tid, fid, response)
+function samr_querydomaininfo2(smbstate, domain_handle, level, response)
        local i, j
        local status, result
        local arguments
@@ -1129,7 +1111,7 @@
 --             [out,switch_is(level)] samr_DomainInfo *info
 
        -- Do the call
-       status, result = call_function(socket, 0x2e, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x2e, arguments)
        if(status ~= true) then
                return false, result
        end
@@ -1204,14 +1186,11 @@
 end
 
 ---Call the close() function, which closes a handle of any type (for example, domain_handle or connect_handle)
---@param socket The socket, with a proper MSRPC connection
---@param handle The handle to close
---@param uid    The UserID we're sending the packets as
---@param tid    The TreeID we're sending the packets to
---@param fid    The FileID we're sending the packets to
+--@param smbstate The SMB state table
+--@param handle   The handle to close
 --@return (status, result) If status is false, result is an error message. Otherwise, result is potentially
 --        a table of values, none of which are likely to be used. 
-function samr_close(socket, handle, uid, tid, fid)
+function samr_close(smbstate, handle)
        local i, j
        local status, result
        local arguments
@@ -1225,7 +1204,7 @@
        arguments = bin.pack("<A", handle)
 
        -- Do the call
-       status, result = call_function(socket, 0x01, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x01, arguments)
        if(status ~= true) then
                return false, result
        end
@@ -1252,14 +1231,11 @@
 ---Call the LsarOpenPolicy2() function, to obtain a "policy handle". This must be done before calling many 
 -- of the LSA functions. 
 --
---@param socket The socket, with a proper MSRPC connection
---@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it)
---@param uid    The UserID we're sending the packets as
---@param tid    The TreeID we're sending the packets to
---@param fid    The FileID we're sending the packets to
+--@param smbstate  The SMB state table
+--@param server    The IP or Hostname of the server (seems to be ignored but it's a good idea to have it)
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the 
most
 --        useful one being 'policy_handle', which is required to call other functions. 
-function lsa_openpolicy2(socket, server, uid, tid, fid)
+function lsa_openpolicy2(smbstate, server)
        local i, j
        local status, result
        local arguments
@@ -1299,7 +1275,7 @@
 --             [out] policy_handle *handle     
 
        -- Do the call
-       status, result = call_function(socket, 0x2C, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x2C, arguments)
        if(status ~= true) then
                return false, result
        end
@@ -1326,17 +1302,14 @@
 
 ---Call the LsarLookupNames2() function, to convert the server's name into a sid. 
 --
---@param socket The socket, with a proper MSRPC connection
+--@param smbstate      The SMB state table
 --@param policy_handle The policy handle returned by lsa_openpolicy2()
---@param names  An array of names to look up. To get a SID, only one of the names needs to be valid. 
---@param uid    The UserID we're sending the packets as
---@param tid    The TreeID we're sending the packets to
---@param fid    The FileID we're sending the packets to
+--@param names         An array of names to look up. To get a SID, only one of the names needs to be valid. 
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. 
 --        The most useful result is 'domains', which is a list of domains known to the server. And, for each of the
 --        domains, there is a 'name' entry, which is a string, and a 'sid' entry, which is yet another object which
 --        can be passed to functions that understand SIDs. 
-function lsa_lookupnames2(socket, policy_handle, names, uid, tid, fid)
+function lsa_lookupnames2(smbstate, policy_handle, names)
        local i, j
        local status, result
        local arguments
@@ -1399,7 +1372,7 @@
 
 
        -- Do the call
-       status, result = call_function(socket, 0x3a, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x3a, arguments)
        if(status ~= true) then
                return false, result
        end
@@ -1487,19 +1460,16 @@
 
 ---Call the LsarLookupSids2() function, to convert a list of SIDs to their names
 --
---@param socket The socket, with a proper MSRPC connection
+--@param smbstate      The SMB state table
 --@param policy_handle The policy handle returned by lsa_openpolicy2()
---@param sid    The SID object for the server
---@param rids   The RIDs of users to look up
---@param uid    The UserID we're sending the packets as
---@param tid    The TreeID we're sending the packets to
---@param fid    The FileID we're sending the packets to
+--@param sid           The SID object for the server
+--@param rids          The RIDs of users to look up
 --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. 
 --        The element 'domains' is identical to the lookupnames2() element called 'domains'. The element 'names' is a 
 --        list of strings, for the usernames (not necessary a 1:1 mapping with the RIDs), and the element 'details' is
 --        a table containing more information about each name, even if the name wasn't found (this one is a 1:1 mapping
 --        with the RIDs). 
-function lsa_lookupsids2(socket, policy_handle, sid, rids, uid, tid, fid)
+function lsa_lookupsids2(smbstate, policy_handle, sid, rids)
        local i, j
        local status, result
        local arguments
@@ -1548,7 +1518,7 @@
 
 
        -- Do the call
-       status, result = call_function(socket, 0x39, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x39, arguments)
        if(status ~= true) then
                return false, result
        end
@@ -1649,14 +1619,11 @@
 end
 
 ---Call the close() function, which closes a session created with a lsa_openpolicy()-style function
---@param socket The socket, with a proper MSRPC connection
---@param handle The handle to close
---@param uid    The UserID we're sending the packets as
---@param tid    The TreeID we're sending the packets to
---@param fid    The FileID we're sending the packets to
+--@param smbstate  The SMB state table
+--@param handle    The handle to close
 --@return (status, result) If status is false, result is an error message. Otherwise, result is potentially
 --        a table of values, none of which are likely to be used. 
-function lsa_close(socket, handle, uid, tid, fid)
+function lsa_close(smbstate, handle)
        local i, j
        local status, result
        local arguments
@@ -1670,7 +1637,7 @@
        arguments = bin.pack("<A", handle)
 
        -- Do the call
-       status, result = call_function(socket, 0x00, arguments, uid, tid, fid)
+       status, result = call_function(smbstate, 0x00, arguments)
        if(status ~= true) then
                return false, result
        end

_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://SecLists.Org

Current thread: