From 99cbcd76a6cf3edb5128bb9c3c3ac814f8975fd9 Mon Sep 17 00:00:00 2001
From: Robert David Graham <robert_david_graham@yahoo.com>
Date: Mon, 28 May 2018 17:41:59 -0400
Subject: [PATCH] SMBv1 (SMBv2 not yet done)

---
 src/masscan-app.c                        |   2 +
 src/masscan-app.h                        |   1 +
 src/proto-banner1.c                      |  33 +-
 src/proto-banner1.h                      |  43 ++
 src/proto-smb.c                          | 860 +++++++++++++++++++++++
 src/proto-smb.h                          |   8 +
 xcode4/masscan.xcodeproj/project.pbxproj |   6 +
 7 files changed, 952 insertions(+), 1 deletion(-)
 create mode 100644 src/proto-smb.c
 create mode 100644 src/proto-smb.h

diff --git a/src/masscan-app.c b/src/masscan-app.c
index a7d88a4..9241c40 100644
--- a/src/masscan-app.c
+++ b/src/masscan-app.c
@@ -21,6 +21,7 @@ masscan_app_to_string(enum ApplicationProtocol proto)
     case PROTO_SNMP: return "snmp";
     case PROTO_NBTSTAT: return "nbtstat";
     case PROTO_SSL3:    return "ssl";
+    case PROTO_SMB:     return "smb";
     case PROTO_SMTP:    return "smtp";
     case PROTO_POP3:    return "pop";
     case PROTO_IMAP4:   return "imap";
@@ -61,6 +62,7 @@ masscan_string_to_app(const char *str)
         {"ssh2",    PROTO_SSH2},
         {"nbtstat", PROTO_NBTSTAT},
         {"ssl",     PROTO_SSL3},
+        {"smb",     PROTO_SMB},
         {"pop",     PROTO_POP3},
         {"imap",    PROTO_IMAP4},
         {"x509",    PROTO_X509_CERT},
diff --git a/src/masscan-app.h b/src/masscan-app.h
index 402bd1b..3dcfaff 100644
--- a/src/masscan-app.h
+++ b/src/masscan-app.h
@@ -16,6 +16,7 @@ enum ApplicationProtocol {
     PROTO_SNMP,             /* simple network management protocol, udp/161 */
     PROTO_NBTSTAT,          /* netbios, udp/137 */
     PROTO_SSL3,
+    PROTO_SMB,              /* SMB tcp/139 and tcp/445 */
     PROTO_SMTP,
     PROTO_POP3,
     PROTO_IMAP4,
diff --git a/src/proto-banner1.c b/src/proto-banner1.c
index b1d8b03..1bc30d3 100644
--- a/src/proto-banner1.c
+++ b/src/proto-banner1.c
@@ -8,6 +8,7 @@
 #include "proto-banner1.h"
 #include "proto-http.h"
 #include "proto-ssl.h"
+#include "proto-smb.h"
 #include "proto-ssh.h"
 #include "proto-ftp.h"
 #include "proto-smtp.h"
@@ -24,6 +25,16 @@
 
 
 struct Patterns patterns[] = {
+    {"\x00\x00" "**" "\xff" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS},
+    {"\x00\x00" "**" "\xfe" "SMB", 8, PROTO_SMB, SMACK_ANCHOR_BEGIN | SMACK_WILDCARDS},
+    
+    {"\x83\x00\x00\x01\x80", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Not listening on called name */
+    {"\x83\x00\x00\x01\x81", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Not listening for calling name */
+    {"\x83\x00\x00\x01\x82", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Called name not present */
+    {"\x83\x00\x00\x01\x83", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Called name present, but insufficient resources */
+    {"\x83\x00\x00\x01\x8f", 5, PROTO_SMB, SMACK_ANCHOR_BEGIN}, /* Unspecified error */
+
+    /* ...the remainder can be in any order */
     {"SSH-1.",      6, PROTO_SSH1, SMACK_ANCHOR_BEGIN},
     {"SSH-2.",      6, PROTO_SSH2, SMACK_ANCHOR_BEGIN},
     {"HTTP/1.",     7, PROTO_HTTP, SMACK_ANCHOR_BEGIN},
@@ -31,6 +42,7 @@ struct Patterns patterns[] = {
     {"220 ",        4, PROTO_FTP, SMACK_ANCHOR_BEGIN, 1},
     {"+OK ",        4, PROTO_POP3, SMACK_ANCHOR_BEGIN},
     {"* OK ",       5, PROTO_IMAP4, SMACK_ANCHOR_BEGIN},
+    {"521 ",        4, PROTO_SMTP, SMACK_ANCHOR_BEGIN},
     {"\x16\x03\x00",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN},
     {"\x16\x03\x01",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN},
     {"\x16\x03\x02",3, PROTO_SSL3, SMACK_ANCHOR_BEGIN},
@@ -197,6 +209,15 @@ banner1_parse(
                         banout,
                         more);
         break;
+    case PROTO_SMB:
+        banner_smb1.parse(
+                        banner1,
+                        banner1->http_fields,
+                        tcb_state,
+                        px, length,
+                        banout,
+                        more);
+        break;
     case PROTO_VNC_RFB:
         banner_vnc.parse(    banner1,
                              banner1->http_fields,
@@ -265,10 +286,14 @@ banner1_create(void)
     banner_smtp.init(b);
     banner_ssh.init(b);
     banner_ssl.init(b);
+    banner_smb0.init(b);
+    banner_smb1.init(b);
     banner_vnc.init(b);
 
     b->tcp_payloads[80] = &banner_http;
     b->tcp_payloads[8080] = &banner_http;
+    b->tcp_payloads[139] = (void*)&banner_smb0;
+    b->tcp_payloads[445] = (void*)&banner_smb1;
     
     b->tcp_payloads[443] = (void*)&banner_ssl;   /* HTTP/s */
     b->tcp_payloads[465] = (void*)&banner_ssl;   /* SMTP/s */
@@ -449,7 +474,13 @@ banner1_selftest()
             fprintf(stderr, "SSL banner: selftest failed\n");
             return 1;
         }
-
+        
+        x = banner_smb1.selftest();
+        if (x) {
+            fprintf(stderr, "SMB banner: selftest failed\n");
+            return 1;
+        }
+        
         x = banner_http.selftest();
         if (x) {
             fprintf(stderr, "HTTP banner: selftest failed\n");
diff --git a/src/proto-banner1.h b/src/proto-banner1.h
index ce99ce7..21a572d 100644
--- a/src/proto-banner1.h
+++ b/src/proto-banner1.h
@@ -135,6 +135,48 @@ struct MEMCACHEDSTUFF {
     unsigned match;
 };
 
+struct Smb72_Negotiate {
+    uint16_t DialectIndex;
+    uint16_t SecurityMode;
+    uint64_t SystemTime;
+    uint32_t Capabilities;
+    uint16_t ServerTimeZone;
+    uint8_t  ChallengeLength;
+    uint8_t  ChallengeOffset;
+};
+
+struct SMBSTUFF {
+    unsigned char nbt_type;
+    unsigned char nbt_flags;
+    unsigned length;
+    unsigned nbt_err;
+    
+    struct {
+        unsigned char   command;
+        unsigned        status;
+        unsigned char   flags1;
+        unsigned short  flags2;
+        unsigned        pid;
+        unsigned char   signature[8];
+        unsigned short  tid;
+        unsigned short  uid;
+        unsigned short  mid;
+        unsigned short  param_length;
+        unsigned short  param_offset;
+        unsigned short  byte_count;
+        unsigned short  byte_offset;
+        unsigned short  byte_state;
+        unsigned short  unicode_char;
+    } smb1;
+    union {
+        struct Smb72_Negotiate negotiate;
+    } parms1;
+    
+    union {
+        
+    } pkt;
+};
+
 struct ProtocolState {
     unsigned state;
     unsigned remaining;
@@ -151,6 +193,7 @@ struct ProtocolState {
         struct SMTPSTUFF smtp;
         struct POP3STUFF pop3;
         struct MEMCACHEDSTUFF memcached;
+        struct SMBSTUFF smb;
     } sub;
 };
 
diff --git a/src/proto-smb.c b/src/proto-smb.c
new file mode 100644
index 0000000..28663a1
--- /dev/null
+++ b/src/proto-smb.c
@@ -0,0 +1,860 @@
+/*
+    SMB parser
+ 
+ */
+#include "proto-smb.h"
+#include "proto-interactive.h"
+#include "unusedparm.h"
+#include "masscan-app.h"
+#include "siphash24.h"
+#include "string_s.h"
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stddef.h>
+
+
+/*
+    "NT LM 0.12"    -   Win2k
+    "SMB 2.002"     0x0202      Vista
+    "SMB 2.???"     0x02FF      Win7, Windows 2008
+    "PC NETWORK PROGRAM 1.0"    MS-DOS
+    "MICROSOFT NETWORKS 1.03"   MS-DOS
+    "MICROSOFT NETWORKS 3.0"    MS-DOS
+    "LANMAN 1.0"                OS/2
+    "LM1.2X002"                 OS/2
+ */
+/*
+PC NETWORK PROGRAM 1.0
+LANMAN1.0
+Windows for Workgroups 3.1a
+LM1.2X002
+LANMAN2.1
+NT LM 0.12
+SMB 2.002
+SMB 2.???
+Samba
+XENIX CORE
+*/
+/*
+  References:
+ http://pubs.opengroup.org/onlinepubs/9697999099/toc.pdf
+ */
+
+struct SmbParams {
+    unsigned short command;
+    unsigned short external_offset;
+    unsigned char external_length;
+    unsigned char internal_type;
+    unsigned short internal_offset;
+};
+
+enum InternalType {
+    IT_uint0,
+    IT_uint8,
+    IT_uint16,
+    IT_uint32,
+    IT_uint64,
+};
+
+struct SmbParams params[] = {
+/*
+ USHORT DialectIndex;
+ UCHAR SecurityMode;
+ USHORT MaxMpxCount;
+ USHORT MaxNumberVcs;
+ ULONG MaxBufferSize;
+ ULONG MaxRawSize;
+ ULONG SessionKey;
+ ULONG Capabilities;
+ FILETIME SystemTime;
+ SHORT ServerTimeZone;
+ UCHAR ChallengeLength;
+ */
+    {0x72,  0,   2, IT_uint16, offsetof(struct Smb72_Negotiate, DialectIndex)},
+    {0x72,  2,   1, IT_uint8,  offsetof(struct Smb72_Negotiate, SecurityMode)},
+    //{0x72,  3,   2, IT_uint16, offsetof(struct Smb72_Negotiate, MaxMpxCount)},
+    //{0x72,  5,   2, IT_uint16, offsetof(struct Smb72_Negotiate, MaxNumberVcs)},
+    //{0x72,  7,   4, IT_uint32, offsetof(struct Smb72_Negotiate, MaxBufferSize)},
+    //{0x72, 11,   4, IT_uint32, offsetof(struct Smb72_Negotiate, MaxRawSize)},
+    //{0x72, 15,   4, IT_uint32, offsetof(struct Smb72_Negotiate, SessionKey)},
+    {0x72, 19,   4, IT_uint32, offsetof(struct Smb72_Negotiate, Capabilities)},
+    {0x72, 23,   8, IT_uint64, offsetof(struct Smb72_Negotiate, SystemTime)},
+    {0x72, 31,   2, IT_uint16, offsetof(struct Smb72_Negotiate, ServerTimeZone)},
+    {0x72, 33,   1, IT_uint8,  offsetof(struct Smb72_Negotiate, ChallengeLength)},
+    
+    {0xFF, 0,   65536, IT_uint0,  0},
+    
+};
+
+#define memberat(t, s, offset) (t*)((char*)(s)+(offset))
+
+static char smb1_null_session_setup[] = {
+    0x00, 0x00, 0x00, 0xbe, 0xff, 0x53, 0x4d, 0x42,
+    0x73, 0x00, 0x00, 0x00, 0x00, 0x18, 0x03, 0x80,
+    0x00, 0x00, 0x5d, 0xa8, 0x8f, 0x55, 0x48, 0x06,
+    0xe8, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xca,
+    0x00, 0x00, 0x00, 0x00, 0x0d, 0x75, 0x00, 0x84,
+    0x00, 0x04, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x47,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00,
+    0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
+    0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x4e, 0x00,
+    0x54, 0x00, 0x20, 0x00, 0x31, 0x00, 0x33, 0x00,
+    0x38, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00,
+    0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00,
+    0x4e, 0x00, 0x54, 0x00, 0x20, 0x00, 0x34, 0x00,
+    0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x2f, 0x00, 0x00, 0x5c, 0x00, 0x5c, 0x00,
+    0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x00,
+    0x30, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x30, 0x00,
+    0x2e, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00,
+    0x5c, 0x00, 0x49, 0x00, 0x50, 0x00, 0x43, 0x00,
+    0x24, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f,
+    0x3f, 0x00
+};
+
+
+/*****************************************************************************
+ *
+ * ****** WARNING: UGLY HACK !!!! ******
+ *
+ * This code is an ugly hack so I can express SMB parameters (word_count)
+ * headers as a structure instead of writing individual parsers for them.
+ * This code makes no sense. If you find a bug in it, it's probably worth
+ * rewriting rather than figure out its convoluted logic. No really, I mean
+ * this.
+ *
+ *****************************************************************************/
+static size_t
+smb_params_parse(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max)
+{
+    size_t original_offset = offset;
+    size_t c;
+    
+    if (max > offset + (smb->smb1.param_length - smb->smb1.param_offset))
+        max = offset + (smb->smb1.param_length - smb->smb1.param_offset);
+    
+    //printf("\n max=%04x  \n", *(unsigned short*)(px+max));
+    
+    /* Find the correct header */
+    for (c=0; params[c].command != smb->smb1.command && params[c].command != 0xFF; c++)
+        ;
+    
+    for (; offset < max; offset++, smb->smb1.param_offset++) {
+        again:
+        
+        //printf("\n%u/%u %u\n", (unsigned)smb->smb1.param_offset, (unsigned)smb->smb1.param_length, (unsigned)c);
+        
+        /* If we've gone past our header, just continue consuming bytes */
+        if (params[c].command != smb->smb1.command)
+            continue;
+        
+        /* If we've gone past the end of this field, goto next field */
+        if (params[c].external_offset + params[c].external_length <= smb->smb1.param_offset) {
+            c++;
+            goto again;
+        }
+        /* Haven't reached the next field yet */
+        if (params[c].external_offset > smb->smb1.param_offset)
+            continue;
+        
+        //printf("\n%u/%u %u [%02x]\n", (unsigned)smb->smb1.param_offset, (unsigned)smb->smb1.param_length, (unsigned)c, px[offset]);
+        
+        /* Shift the type, because all fields little-endian */
+        switch (params[c].internal_type) {
+            case IT_uint0:
+            default:
+                break;
+            case IT_uint8:
+            {
+                uint8_t *x = memberat(uint8_t, &smb->parms1, params[c].internal_offset);
+                *x = px[offset];
+            }
+                break;
+            case IT_uint16:
+            {
+                uint16_t *x = memberat(uint16_t, &smb->parms1, params[c].internal_offset);
+                //*x <<= 8;
+                *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8);
+            }
+                break;
+            case IT_uint32:
+            {
+                uint32_t *x = memberat(uint32_t, &smb->parms1, params[c].internal_offset);
+                //*x <<= 8;
+                *x |= px[offset] << ((smb->smb1.param_offset - params[c].external_offset)*8);
+            }
+                break;
+            case IT_uint64:
+            {
+                uint64_t *x = memberat(uint64_t, &smb->parms1, params[c].internal_offset);
+                //*x <<= 8;
+                *x |= (uint64_t)px[offset] << (uint64_t)((smb->smb1.param_offset - params[c].external_offset)*8);
+            }
+                break;
+        }
+        
+    }
+    
+    /* Return the number of bytes processed */
+    return offset - original_offset;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static const unsigned long long TICKS_PER_SECOND = 10000000LL;
+static const unsigned long long EPOCH_DIFFERENCE = 11644473600LL;
+static time_t
+convert_windows_time(long long int filetime)
+{
+    unsigned long long seconds = filetime / TICKS_PER_SECOND;
+    seconds -= EPOCH_DIFFERENCE;
+    return (time_t)seconds;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+enum {
+    D_START,
+    D_NEGOT_CHALLENGE,
+    D_NEGOT_DOMAINA,
+    D_NEGOT_NAMEA,
+    D_NEGOT_DOMAIN1,
+    D_NEGOT_DOMAIN2,
+    D_NEGOT_NAME1,
+    D_NEGOT_NAME2,
+    D_NEGOT_END,
+    
+    D_UNKNOWN,
+};
+
+/*****************************************************************************
+ *****************************************************************************/
+static void
+name_append_char(struct BannerOutput *banout, unsigned c)
+{
+    if (c & 0xFF80)
+        banout_append_char(banout, PROTO_SMB, '.');
+    else if (isalnum(c))
+        banout_append_char(banout, PROTO_SMB, c);
+    else switch (c) {
+        case '-':
+        case '_':
+        case '$':
+        case '*':
+            banout_append_char(banout, PROTO_SMB, c);
+            break;
+        default:
+            banout_append_char(banout, PROTO_SMB, '.');
+            break;
+            
+    }
+}
+/*****************************************************************************
+ *****************************************************************************/
+static size_t
+smb1_parse_data(struct SMBSTUFF *smb, const unsigned char *px, size_t offset, size_t max, struct BannerOutput *banout)
+{
+    size_t original_offset = offset;
+    unsigned state = smb->smb1.byte_state;
+    
+    
+    if (max > offset + (smb->smb1.byte_count - smb->smb1.byte_offset))
+        max = offset + (smb->smb1.byte_count - smb->smb1.byte_offset);
+    
+    for (;offset<max; offset++)
+    switch (state) {
+        case D_START:
+            state = D_UNKNOWN;
+            break;
+        case D_NEGOT_CHALLENGE:
+            if (smb->parms1.negotiate.ChallengeLength == 0) {
+                if (smb->smb1.flags2 & 0x0080) {
+                    state = D_NEGOT_DOMAIN1;
+                } else {
+                    state = D_NEGOT_DOMAINA;
+                }
+                offset--;
+            } else
+                smb->parms1.negotiate.ChallengeLength--;
+            break;
+        case D_NEGOT_DOMAIN1:
+        case D_NEGOT_NAME1:
+            smb->smb1.unicode_char = px[offset];
+            state++;
+            break;
+        case D_NEGOT_DOMAIN2:
+        case D_NEGOT_DOMAINA:
+        case D_NEGOT_NAME2:
+        case D_NEGOT_NAMEA:
+            smb->smb1.unicode_char |= px[offset] << 8;
+            if (state == D_NEGOT_DOMAINA || state == D_NEGOT_NAMEA)
+                smb->smb1.unicode_char >>= 8;
+            if (smb->smb1.unicode_char == 0) {
+                banout_append_char(banout, PROTO_SMB, ' ');
+                state++;
+            } else {
+                name_append_char(banout, smb->smb1.unicode_char);
+                state--;
+            }
+            break;
+        default:
+            break;
+    }
+    
+    smb->smb1.byte_state = state;
+    smb->smb1.byte_offset += (offset - original_offset);
+    return offset - original_offset;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static void
+smb_parse_record(
+                 const struct Banner1 *banner1,
+                 void *banner1_private,
+                 struct ProtocolState *pstate,
+                 const unsigned char *px, size_t length,
+                 struct BannerOutput *banout,
+                 struct InteractiveData *more)
+{
+    size_t len; /*scratch variables used in a couple places */
+    size_t max;
+    unsigned state = pstate->state;
+    struct SMBSTUFF *smb = &pstate->sub.smb;
+    size_t i;
+    enum {
+        NBT_TYPE,
+        NBT_FLAGS,
+        NBT_LEN1,
+        NBT_LEN2,
+        NBT_ERR,
+        NBT_DRAIN,
+        
+        SMB_VER,
+        SMB1_VER_S, SMB1_VER_M, SMB1_VER_B,
+        
+        SMB1_CMD,
+        SMB1_STATUS1, SMB1_STATUS2, SMB1_STATUS3, SMB1_STATUS4,
+        SMB1_FLAGS1,
+        SMB1_FLAGS2,
+        SMB1_FLAGS3,
+        SMB1_PID1, SMB1_PID2,
+        SMB1_SIG1, SMB1_SIG2, SMB1_SIG3, SMB1_SIG4,
+        SMB1_SIG5, SMB1_SIG6, SMB1_SIG7, SMB1_SIG8,
+        SMB1_RSVD1,SMB1_RSVD2,
+        SMB1_TID1, SMB1_TID2,
+        SMB1_PID3, SMB1_PID4,
+        SMB1_UID1, SMB1_UID2,
+        SMB1_MID1, SMB1_MID2,
+        SMB1_WORD_COUNT,
+        SMB1_PARAMETERS,
+        SMB1_BYTE_COUNT1,
+        SMB1_BYTE_COUNT2,
+        SMB1_DATA,
+        SMB1_DATA_AFTER,
+        /*
+            UCHAR Protocol[4];
+            UCHAR Command;
+            SMB_ERROR Status;
+            UCHAR Flags;
+            USHORT Flags2;
+            USHORT PIDHigh;
+            UCHAR SecurityFeatures[8];
+            USHORT Reserved;
+            USHORT TID;
+            USHORT PIDLow;
+            USHORT UID;
+            USHORT MID;
+        */
+        
+        
+        SMB2_VER_S, SMB2_VER_M, SMB2_VER_B,
+        NBT_UNKNOWN,
+    };
+    
+    /*
+     * On first run thourhg, the offset will be zero
+     */
+    i = 0;
+    
+again:
+    
+    /*
+     * Make sure we don't go past the end of the NetBIOS header portion
+     */
+    max = length;
+    if (state > NBT_LEN2) {
+        if (max > i + smb->length)
+            max = i + smb->length;
+    }
+    
+    
+    /*
+     * `for all bytes in the segment`
+     *   `do a state transition for that byte `
+     */
+    for (; i<max; i++)
+    switch (state) {
+            /*
+             All session packets are of the following general structure:
+             
+             1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+             0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+             |      TYPE     |     FLAGS     |            LENGTH             |
+             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+             |                                                               |
+             /               TRAILER (Packet Type Dependent)                 /
+             |                                                               |
+             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+             */
+        case NBT_TYPE:
+            smb->nbt_type = px[i];
+            state++;
+            break;
+        case NBT_FLAGS:
+            smb->nbt_flags = px[i];
+            smb->length = 0;
+            state++;
+            break;
+        case NBT_LEN1:
+            smb->length <<= 8;
+            smb->length |= px[i];
+            state++;
+            break;
+        case NBT_LEN2:
+            smb->length <<= 8;
+            smb->length |= px[i];
+            if (max > i + smb->length)
+                max = i + smb->length;
+            
+            /*
+             00 -  SESSION MESSAGE
+             81 -  SESSION REQUEST
+             82 -  POSITIVE SESSION RESPONSE
+             83 -  NEGATIVE SESSION RESPONSE
+             84 -  RETARGET SESSION RESPONSE
+             85 -  SESSION KEEP ALIVE
+             */
+            switch (smb->nbt_type) {
+                case 0x00:
+                    state = SMB_VER;
+                    break;
+                case 0x81:
+                    banout_append(banout, PROTO_SMB, "ERR session request", AUTO_LEN);
+                    state = NBT_UNKNOWN;
+                    break;
+                case 0x82:
+                case 0x85:
+                    state = NBT_DRAIN;
+                    break;
+                case 0x83:
+                    state = NBT_ERR;
+                    break;
+                case 0x84:
+                    banout_append(banout, PROTO_SMB, "ERR retarget", AUTO_LEN);
+                    state = NBT_UNKNOWN;
+                    break;
+                default:
+                    banout_append(banout, PROTO_SMB, "ERR unknown response", AUTO_LEN);
+                    break;
+            }
+            break;
+        case NBT_ERR:
+            smb->nbt_err = px[i];
+            /*
+             80 -  Not listening on called name
+             81 -  Not listening for calling name
+             82 -  Called name not present
+             83 -  Called name present, but insufficient resources
+             8F -  Unspecified error
+             */
+            switch (smb->nbt_err) {
+                case 0x80:
+                    banout_append(banout, PROTO_SMB, "ERROR(Not listening on called name)", AUTO_LEN);
+                    break;
+                case 0x81:
+                    banout_append(banout, PROTO_SMB, "ERROR(Not listening for calling name)", AUTO_LEN);
+                    break;
+                case 0x82:
+                    banout_append(banout, PROTO_SMB, "ERROR(Called name not present)", AUTO_LEN);
+                    break;
+                case 0x83:
+                    banout_append(banout, PROTO_SMB, "ERROR(Called name present, but insufficient resources)", AUTO_LEN);
+                    break;
+                case 0x8F:
+                    banout_append(banout, PROTO_SMB, "ERROR(Unspecified error)", AUTO_LEN);
+                    break;
+                default:
+                    banout_append(banout, PROTO_SMB, "ERROR(UNKNOWN)", AUTO_LEN);
+                    break;
+
+            }
+            state = NBT_DRAIN;
+            break;
+            
+        case NBT_DRAIN:
+            state = NBT_DRAIN;
+            break;
+            
+        case SMB_VER:
+            switch (px[i]) {
+                case 0xFF:
+                    banout_append(banout, PROTO_SMB, "SMBv1 ", AUTO_LEN);
+                    state = SMB1_VER_S;
+                    break;
+                case 0xFE:
+                    banout_append(banout, PROTO_SMB, "SMBv2 ", AUTO_LEN);
+                    state = SMB2_VER_S;
+                    break;
+                default:
+                    banout_append(banout, PROTO_SMB, "SMBv? ", AUTO_LEN);
+                    state = NBT_UNKNOWN;
+            }
+            break;
+        case SMB1_VER_S:
+        case SMB2_VER_S:
+            
+            if (px[i] != 'S')
+                state = NBT_UNKNOWN;
+            else
+                state++;
+            break;
+        case SMB1_VER_M:
+        case SMB2_VER_M:
+            if (px[i] != 'M')
+                state = NBT_UNKNOWN;
+            else
+                state++;
+            break;
+        case SMB1_VER_B:
+        case SMB2_VER_B:
+            if (px[i] != 'B')
+                state = NBT_UNKNOWN;
+            else
+                state++;
+            break;
+            
+        case SMB1_CMD:
+            memset(&smb->smb1, 0, sizeof(smb->smb1));
+            smb->smb1.command = px[i];
+            state++;
+            break;
+        case SMB1_STATUS1: case SMB1_STATUS2: case SMB1_STATUS3: case SMB1_STATUS4:
+            smb->smb1.status <<= 8;
+            smb->smb1.status |= px[i];
+            state++;
+            break;
+        case SMB1_FLAGS1:
+            smb->smb1.flags1 = px[i];
+            state++;
+            break;
+        case SMB1_FLAGS2:
+        case SMB1_FLAGS3:
+            smb->smb1.flags2 <<= 8;
+            smb->smb1.flags2 |= px[i];
+            state++;
+            break;
+        case SMB1_PID1: case SMB1_PID2:
+            smb->smb1.pid <<= 8;
+            smb->smb1.pid |= px[i];
+            state++;
+            break;
+        case SMB1_SIG1: case SMB1_SIG2: case SMB1_SIG3: case SMB1_SIG4:
+        case SMB1_SIG5: case SMB1_SIG6: case SMB1_SIG7: case SMB1_SIG8:
+            state++;
+            break;
+        case SMB1_RSVD1:case SMB1_RSVD2:
+            state++;
+            break;
+        case SMB1_TID1: case SMB1_TID2:
+            smb->smb1.tid <<= 8;
+            smb->smb1.tid |= px[i];
+            state++;
+            break;
+        case SMB1_PID3: case SMB1_PID4:
+            smb->smb1.pid <<= 8;
+            smb->smb1.pid |= px[i];
+            state++;
+            break;
+        case SMB1_UID1: case SMB1_UID2:
+            smb->smb1.uid <<= 8;
+            smb->smb1.uid |= px[i];
+            state++;
+            break;
+        case SMB1_MID1: case SMB1_MID2:
+            smb->smb1.mid <<= 8;
+            smb->smb1.mid |= px[i];
+            state++;
+            break;
+        case SMB1_WORD_COUNT:
+            smb->smb1.param_length = px[i]*2;
+            state++;
+            break;
+        case SMB1_PARAMETERS:
+            /* Transfer control to a sub-parser, which may consume zero
+             * or more bytes, up to the end of the parameters field
+             * (meaning, up to word_count*2 bytes) */
+            len = smb_params_parse(smb, px, i, max);
+            i += len;
+            if (smb->smb1.param_offset < smb->smb1.param_length)
+                break;
+            
+            /* We've reached the end of the parameters field, so go onto 
+             * read the byte-count/data field */
+            state = SMB1_BYTE_COUNT1;
+            
+            /* Unconsume the next byte. The "word-count" field may have been
+             * zero when we get to this state, so therefore the logic needs
+             * to be written to handle this. That means when we loop around
+             * again, we need to counter-act the fact that we will automatically
+             * increment the index, so we substract one from it here. */
+            i--;
+            
+            /* Process the parameter/word-count field according to what it
+             * actually contained
+             * TODO: I should make this a function, but I'm lazy 
+             */
+            switch (smb->smb1.command) {
+                case 0x72:
+                {
+                    char str[64] = "(err)";
+                    time_t timestamp = convert_windows_time(smb->parms1.negotiate.SystemTime);
+                    struct tm tm = {0};
+                    
+                    gmtime_s(&tm, &timestamp);
+                    
+                    len = strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S ", &tm);
+                    banout_append(banout, PROTO_SMB, str, len);
+                    sprintf_s(str, sizeof(str), "TZ%+d ", (short)smb->parms1.negotiate.ServerTimeZone);
+                    banout_append(banout, PROTO_SMB, str, AUTO_LEN);
+                    smb->smb1.byte_state = D_NEGOT_CHALLENGE;
+                    
+                    //more->payload = smb1_null_session_setup;
+                    //more->length = sizeof(smb1_null_session_setup);
+                    
+                }
+                    break;
+                default:
+                    banout_append(banout, PROTO_SMB, "-- ", AUTO_LEN);
+                    smb->smb1.byte_state = D_UNKNOWN;
+            }
+            
+            break;
+            
+        case SMB1_BYTE_COUNT1:
+            smb->smb1.byte_count = px[i];
+            state++;
+            break;
+        case SMB1_BYTE_COUNT2:
+            smb->smb1.byte_count |= px[i]<<8;
+            state++;
+            break;
+        case SMB1_DATA:
+            i += smb1_parse_data(smb, px, i, max, banout);
+            if (smb->smb1.byte_offset >= smb->smb1.byte_count) {
+                state = SMB1_DATA_AFTER;
+                i--; /* unconsume byte because of auto-increment */
+            }
+            break;
+            
+        case SMB1_DATA_AFTER:
+            if (i < max) {
+                ;
+            } else {
+                state = 0;
+                i--;
+            }
+            break;
+    
+        default:
+            i = length;
+            break;
+    }
+    
+    /*
+     * If there are multiple response packets, then
+     * loop around and process the next one
+     */
+    if (i < length) {
+        state = 0;
+        goto again;
+    }
+
+    pstate->state = state;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static void *
+smb_init(struct Banner1 *banner1)
+{
+    UNUSEDPARM(banner1);
+    return 0;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+
+static const char
+smb0_hello_template[] = {
+    0x81, 0x00, 0x00, 0x44, 0x20, 0x43, 0x4b, 0x46,
+    0x44, 0x45, 0x4e, 0x45, 0x43, 0x46, 0x44, 0x45,
+    0x46, 0x46, 0x43, 0x46, 0x47, 0x45, 0x46, 0x46,
+    0x43, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+    0x41, 0x43, 0x41, 0x43, 0x41, 0x00, 0x20, 0x45,
+    0x44, 0x46, 0x43, 0x45, 0x46, 0x45, 0x46, 0x45,
+    0x44, 0x45, 0x49, 0x45, 0x46, 0x46, 0x43, 0x43,
+    0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43,
+    0x41, 0x43, 0x41, 0x43, 0x41, 0x41, 0x41, 0x00,
+
+    0x00, 0x00, 0x00, 0x45, 0xff, 0x53, 0x4d, 0x42,
+    0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc8,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00,
+    0xff, 0xff, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02,
+    0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e,
+    0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20,
+    0x32, 0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53,
+    0x4d, 0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f,
+    0x00
+};
+static const char
+smb1_hello_template[] = {
+    0x00, 0x00, 0x00, 0x2f, 0xff, 0x53, 0x4d, 0x42,
+    0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0xc0, /* */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00,
+    0xff, 0xff, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02,
+    0x4e, 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e,
+    0x31, 0x32, 0x00
+};
+
+/*****************************************************************************
+ *****************************************************************************/
+static int
+smb_selftest(void)
+{
+    struct Banner1 *banner1;
+    struct ProtocolState state[1];
+    struct BannerOutput banout1[1];
+    struct InteractiveData more;
+    int x;
+    size_t i;
+
+    unsigned char packet_bytes[] = {
+        0x00, 0x00, 0x00, 0x69, 0xff, 0x53, 0x4d, 0x42,
+        0x72, 0x00, 0x00, 0x00, 0x00, 0x88, 0x01, 0xc0,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00,
+        0xff, 0xff, 0x00, 0x00, 0x11, 0x00, 0x00, 0x03,
+        0x10, 0x00, 0x01, 0x00, 0x04, 0x11, 0x00, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xfc, 0xe3, 0x01, 0x00, 0x1f, 0xac, 0xe7, 0x7f,
+        0x8a, 0xf0, 0xd3, 0x01, 0xf0, 0x00, 0x08, 0x24,
+        0x00, 0xc2, 0xe5, 0x34, 0x10, 0xfd, 0x29, 0xa7,
+        0x75, 0x42, 0x00, 0x4e, 0x00, 0x43, 0x00, 0x00,
+        0x00, 0x53, 0x00, 0x48, 0x00, 0x49, 0x00, 0x50,
+        0x00, 0x42, 0x00, 0x41, 0x00, 0x52, 0x00, 0x42,
+        0x00, 0x4f, 0x00, 0x00, 0x00,
+
+        /*0x00, 0x00, 0x00, 0x90, 0xff, 0x53, 0x4d, 0x42,
+        0x73, 0x00, 0x00, 0x00, 0x00, 0x98, 0x03, 0x80,
+        0x00, 0x00, 0x5d, 0xa8, 0x8f, 0x55, 0x48, 0x06,
+        0xe8, 0xfc, 0x00, 0x00, 0x00, 0x08, 0xfe, 0xca,
+        0x00, 0x08, 0x00, 0x00, 0x03, 0x75, 0x00, 0x81,
+        0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x57, 0x00,
+        0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
+        0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x35, 0x00,
+        0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00,
+        0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00,
+        0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32, 0x00,
+        0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, 0x00,
+        0x4c, 0x00, 0x41, 0x00, 0x4e, 0x00, 0x20, 0x00,
+        0x4d, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x61, 0x00,
+        0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00,
+        0x52, 0x00, 0x45, 0x00, 0x53, 0x00, 0x45, 0x00,
+        0x41, 0x00, 0x55, 0x00, 0x00, 0x03, 0xff, 0x00,
+        0x90, 0x00, 0x01, 0x00, 0x06, 0x00, 0x49, 0x50,
+        0x43, 0x00, 0x00, 0x00*/
+
+    };
+    
+    banner1 = banner1_create();
+    banout_init(banout1);
+    memset(&state[0], 0, sizeof(state[0]));
+    
+    smb_parse_record(banner1,
+                     0,
+                     state,
+                     packet_bytes,
+                     sizeof(packet_bytes),
+                     banout1,
+                     &more);
+    x = banout_is_contains(banout1, PROTO_SMB,
+                           "SHIPBAR");
+    if (!x) {
+        printf("smb parser failure: google.com\n");
+        return 1;
+    }
+    banner1_destroy(banner1);
+    banout_release(banout1);
+    
+    /*
+     *  LET'S FUZZ THIS CRAP!!!
+     *
+     * We are going to re-parse the response packet as many times as needed,
+     * each time flipping one bit in the packet. This should crash the
+     * parser if it has such a bug that will crash it.
+     */
+    for (i=2; i< 5 && i<sizeof(packet_bytes); i++) {
+        size_t j;
+        
+        for (j=0; j<8; j++) {
+            size_t flip = 1<<j;
+            
+            packet_bytes[i] ^= flip;
+            
+            banner1 = banner1_create();
+            banout_init(banout1);
+            memset(&state[0], 0, sizeof(state[0]));
+            
+            smb_parse_record(banner1,
+                             0,
+                             state,
+                             packet_bytes,
+                             sizeof(packet_bytes),
+                             banout1,
+                             &more);
+            banner1_destroy(banner1);
+            banout_release(banout1);
+
+            packet_bytes[i] ^= flip;
+            
+        }
+    }
+    return 0;
+}
+
+/*****************************************************************************
+ * This is the 'plugin' structure that registers callbacks for this parser in
+ * the main system.
+ *****************************************************************************/
+struct ProtocolParserStream banner_smb0 = {
+    "smb", 139, smb0_hello_template, sizeof(smb0_hello_template), 0,
+    smb_selftest,
+    smb_init,
+    smb_parse_record,
+};
+struct ProtocolParserStream banner_smb1 = {
+    "smb", 445, smb1_hello_template, sizeof(smb1_hello_template), 0,
+    smb_selftest,
+    smb_init,
+    smb_parse_record,
+};
+
diff --git a/src/proto-smb.h b/src/proto-smb.h
new file mode 100644
index 0000000..98649e8
--- /dev/null
+++ b/src/proto-smb.h
@@ -0,0 +1,8 @@
+#ifndef PROTO_SMB_H
+#define PROTO_SMB_H
+#include "proto-banner1.h"
+
+extern struct ProtocolParserStream banner_smb0;
+extern struct ProtocolParserStream banner_smb1;
+
+#endif
diff --git a/xcode4/masscan.xcodeproj/project.pbxproj b/xcode4/masscan.xcodeproj/project.pbxproj
index c008f4f..b36144a 100644
--- a/xcode4/masscan.xcodeproj/project.pbxproj
+++ b/xcode4/masscan.xcodeproj/project.pbxproj
@@ -89,6 +89,7 @@
 		11C936C31EDCE77F0023D32E /* in-filter.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936BF1EDCE77F0023D32E /* in-filter.c */; };
 		11C936C41EDCE77F0023D32E /* in-report.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936C11EDCE77F0023D32E /* in-report.c */; };
 		11C936C71EDCE8B40023D32E /* rawsock-pcap.c in Sources */ = {isa = PBXBuildFile; fileRef = 11C936C51EDCE8B40023D32E /* rawsock-pcap.c */; };
+		11DE129620ABC2650041135D /* proto-smb.c in Sources */ = {isa = PBXBuildFile; fileRef = 11DE129520ABC2650041135D /* proto-smb.c */; };
 		11E76DB41889BC5200061F45 /* pixie-backtrace.c in Sources */ = {isa = PBXBuildFile; fileRef = 11E76DB21889BC5200061F45 /* pixie-backtrace.c */; };
 		11F9375419F1A54200C1947F /* script-sslv3.c in Sources */ = {isa = PBXBuildFile; fileRef = 11F9375319F1A54200C1947F /* script-sslv3.c */; };
 		11F9375719F1AD5000C1947F /* script-heartbleed.c in Sources */ = {isa = PBXBuildFile; fileRef = 11F9375619F1AD5000C1947F /* script-heartbleed.c */; };
@@ -261,6 +262,8 @@
 		11C936C21EDCE77F0023D32E /* in-report.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "in-report.h"; sourceTree = "<group>"; };
 		11C936C51EDCE8B40023D32E /* rawsock-pcap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "rawsock-pcap.c"; sourceTree = "<group>"; };
 		11C936C61EDCE8B40023D32E /* rawsock-pcap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "rawsock-pcap.h"; sourceTree = "<group>"; };
+		11DE129420ABC2650041135D /* proto-smb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "proto-smb.h"; sourceTree = "<group>"; };
+		11DE129520ABC2650041135D /* proto-smb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "proto-smb.c"; sourceTree = "<group>"; };
 		11E76DB21889BC5200061F45 /* pixie-backtrace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pixie-backtrace.c"; sourceTree = "<group>"; };
 		11E76DB31889BC5200061F45 /* pixie-backtrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "pixie-backtrace.h"; sourceTree = "<group>"; };
 		11F9375319F1A54200C1947F /* script-sslv3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "script-sslv3.c"; sourceTree = "<group>"; };
@@ -388,6 +391,8 @@
 		11B360CA1F9016C00020F3A3 /* proto */ = {
 			isa = PBXGroup;
 			children = (
+				11DE129420ABC2650041135D /* proto-smb.h */,
+				11DE129520ABC2650041135D /* proto-smb.c */,
 				119AB2042051FFED008E4DDD /* proto-memcached.c */,
 				119AB2052051FFED008E4DDD /* proto-memcached.h */,
 				11A921AC17DBCC7E00DDFD32 /* proto-arp.c */,
@@ -604,6 +609,7 @@
 				11A921F817DBCC7E00DDFD32 /* syn-cookie.c in Sources */,
 				11A921F917DBCC7E00DDFD32 /* templ-pkt.c in Sources */,
 				11A921FA17DBCC7E00DDFD32 /* xring.c in Sources */,
+				11DE129620ABC2650041135D /* proto-smb.c in Sources */,
 				11B2DD9E17DE4DD8007FC363 /* templ-payloads.c in Sources */,
 				11AC80ED17E0DAD4001BCE3A /* proto-http.c in Sources */,
 				11AC80EE17E0DAD4001BCE3A /* proto-icmp.c in Sources */,
-- 
GitLab