diff --git a/src/app-layer-smb.c b/src/app-layer-smb.c index 68479a39a7..b8f444f597 100644 --- a/src/app-layer-smb.c +++ b/src/app-layer-smb.c @@ -96,32 +96,285 @@ void hexdump(const void *buf, size_t len) { } } -/* For WriteAndX we need to get writeandxdataoffset */ -static int SMBParseAndX(void *smb_state, AppLayerParserState *pstate, +/** + * \brief SMB Write AndX Request Parsing + */ +static int SMBParseWriteAndX(void *smb_state, AppLayerParserState *pstate, uint8_t *input, uint32_t input_len, AppLayerParserResult *output) { SMBState *sstate = (SMBState *) smb_state; uint8_t *p = input; switch (sstate->andx.andxbytesprocessed) { case 0: - sstate->andx.andxcommand = *(p++); + sstate->andx.paddingparsed = 0; + if (input_len >= 28) { + sstate->andx.andxcommand = *p; + sstate->andx.andxoffset = *(p+2) << 8; + sstate->andx.andxoffset |= *(p+3); + sstate->andx.datalength = *(p+18) << 16; + sstate->andx.datalength |= *(p+19) << 24; + sstate->andx.datalength |= *(p+20) << 8; + sstate->andx.datalength |= *(p+21); + sstate->andx.dataoffset = *(p+22) << 8; + sstate->andx.dataoffset|= *(p+23); + sstate->andx.dataoffset|= (uint64_t) *(p+24) << 56; + sstate->andx.dataoffset|= (uint64_t) *(p+25) << 48; + sstate->andx.dataoffset|= (uint64_t) *(p+26) << 40; + sstate->andx.dataoffset|= (uint64_t) *(p+27) << 32; + input_len -= 28; + sstate->bytesprocessed += 28; + return 28; + } else { + sstate->andx.andxcommand = *(p++); + if (!(--input_len)) break; + } + case 1: + p++; // Reserved if (!(--input_len)) break; case 2: - p++; // Reserved + sstate->andx.andxoffset = *(p++) << 8; if (!(--input_len)) break; case 3: - sstate->andx.andxoffset |= *(p++) << 8; + sstate->andx.andxoffset |= *(p++); if (!(--input_len)) break; case 4: - sstate->andx.andxoffset |= *(p++); + // SMB_COM_WRITE_ANDX Fid 1 + p++; + if (!(--input_len)) break; + case 5: + // SMB_COM_WRITE_ANDX Fid 2 + p++; + if (!(--input_len)) break; + case 6: + // SMB_COM_WRITE_ANDX Offset 1 + p++; + if (!(--input_len)) break; + case 7: + // SMB_COM_WRITE_ANDX Offset 2 + p++; + if (!(--input_len)) break; + case 8: + // SMB_COM_WRITE_ANDX Offset 3 + p++; + if (!(--input_len)) break; + case 9: + // SMB_COM_WRITE_ANDX Offset 4 + p++; + if (!(--input_len)) break; + case 10: + // SMB_COM_WRITE_ANDX Reserved 1 + p++; + if (!(--input_len)) break; + case 11: + // SMB_COM_WRITE_ANDX Reserved 2 + p++; + if (!(--input_len)) break; + case 12: + // SMB_COM_WRITE_ANDX Reserved 3 + p++; + if (!(--input_len)) break; + case 13: + // SMB_COM_WRITE_ANDX Reserved 4 + p++; + if (!(--input_len)) break; + case 14: + // SMB_COM_WRITE_ANDX WriteMode 1 + p++; + if (!(--input_len)) break; + case 15: + // SMB_COM_WRITE_ANDX WriteMode 2 + p++; + if (!(--input_len)) break; + case 16: + // SMB_COM_WRITE_ANDX BytesRemaining 1 + p++; + if (!(--input_len)) break; + case 17: + // SMB_COM_WRITE_ANDX BytesRemaining 2 + p++; + if (!(--input_len)) break; + case 18: + // DataLengthHigh 1 + sstate->andx.datalength = *(p++) << 16; + if (!(--input_len)) break; + case 19: + // DataLengthHigh 2 + sstate->andx.datalength |= *(p++) << 24; + if (!(--input_len)) break; + case 20: + // DataLength 1 + sstate->andx.datalength |= *(p++) << 8; + if (!(--input_len)) break; + case 21: + // DataLength 2 + sstate->andx.datalength |= *(p++); + if (!(--input_len)) break; + case 22: + sstate->andx.dataoffset = *(p++) << 8; + if (!(--input_len)) break; + case 23: + sstate->andx.dataoffset |= *(p++); + if (!(--input_len)) break; + case 24: + sstate->andx.dataoffset|= (uint64_t) *(p++) << 56; + if (!(--input_len)) break; + case 25: + sstate->andx.dataoffset|= (uint64_t) *(p++) << 48; + if (!(--input_len)) break; + case 26: + sstate->andx.dataoffset|= (uint64_t) *(p++) << 40; if (!(--input_len)) break; + case 27: + sstate->andx.dataoffset|= (uint64_t) *(p++) << 32; + --input_len; + break; default: + // SHOULD NEVER OCCUR + return 0; + } + sstate->bytesprocessed += (p - input); + return (p - input); +} + +/** + * \brief SMB Read AndX Response Parsing + */ +static int SMBParseReadAndX(void *smb_state, AppLayerParserState *pstate, + uint8_t *input, uint32_t input_len, AppLayerParserResult *output) { + SMBState *sstate = (SMBState *) smb_state; + uint8_t *p = input; + switch (sstate->andx.andxbytesprocessed) { + case 0: + sstate->andx.paddingparsed = 0; + if (input_len >= 24) { + sstate->andx.andxcommand = *p; + sstate->andx.andxoffset = *(p+2) << 8; + sstate->andx.andxoffset |= *(p+3); + sstate->andx.datalength = *(p+10) << 8; + sstate->andx.datalength |= *(p+11); + sstate->andx.dataoffset = *(p+12) << 8; + sstate->andx.dataoffset |= *(p+13); + sstate->andx.datalength |= (uint64_t) *(p+14) << 56; + sstate->andx.datalength |= (uint64_t) *(p+15) << 48; + sstate->andx.datalength |= (uint64_t) *(p+16) << 40; + sstate->andx.datalength |= (uint64_t) *(p+17) << 32; + input_len -= 24; + sstate->bytesprocessed += 24; + return 24; + } else { + sstate->andx.andxcommand = *(p++); + if (!(--input_len)) break; + } + case 1: + p++; // Reserved + if (!(--input_len)) break; + case 2: + sstate->andx.andxoffset |= *(p++) << 8; + if (!(--input_len)) break; + case 3: + sstate->andx.andxoffset |= *(p++); + if (!(--input_len)) break; + case 4: + // SMB_COM_READ_ANDX Remaining Reserved must be 0xff + p++; + if (!(--input_len)) break; + case 5: + // SMB_COM_READ_ANDX Remaining Reserved must be 0xff + p++; + if (!(--input_len)) break; + case 6: + // SMB_COM_READ_ANDX DataCompactionMode 1 + p++; + if (!(--input_len)) break; + case 7: + // SMB_COM_READ_ANDX DataCompactionMode 1 + p++; + if (!(--input_len)) break; + case 8: + // SMB_COM_READ_ANDX Reserved + p++; + if (!(--input_len)) break; + case 9: + // SMB_COM_READ_ANDX Reserved + p++; + if (!(--input_len)) break; + case 10: + sstate->andx.datalength = *(p++) << 8; + if (!(--input_len)) break; + case 11: + sstate->andx.datalength |= *(p++); + if (!(--input_len)) break; + case 12: + sstate->andx.dataoffset = *(p++) << 8; + if (!(--input_len)) break; + case 13: + sstate->andx.dataoffset|= *(p++); + if (!(--input_len)) break; + case 14: + sstate->andx.datalength |= *(p++) << 24; + if (!(--input_len)) break; + case 15: + sstate->andx.datalength |= *(p++) << 16; + if (!(--input_len)) break; + case 16: + // SMB_COM_READ_ANDX Reserved + p++; + if (!(--input_len)) break; + case 17: + // SMB_COM_READ_ANDX Reserved + p++; + if (!(--input_len)) break; + case 18: + // SMB_COM_READ_ANDX Reserved + p++; + --input_len; break; + default: + // SHOULD NEVER OCCUR + return 0; } return 0; + sstate->bytesprocessed += (p - input); + return (p - input); } -/* - * Obtain SMB WordCount which is 2 times the value. +/** + * Handle variable length padding for WriteAndX and ReadAndX + */ +static int PaddingParser(void *smb_state, AppLayerParserState *pstate, + uint8_t *input, uint32_t input_len, AppLayerParserResult *output) { + SMBState *sstate = (SMBState *) smb_state; + uint8_t *p = input; + while (sstate->bytesprocessed++ < sstate->andx.dataoffset && sstate->bytecount.bytecount-- && input_len--) { + p++; + } + if (sstate->bytesprocessed == sstate->andx.dataoffset) { + sstate->andx.paddingparsed = 1; + } + sstate->bytesprocessed += (p - input); + return (p - input); +} + +/** + * \brief Parse WriteAndX and ReadAndX Data + * \todo Hand off to DCERPC parser for DCERPC over SMB + */ +static int DataParser(void *smb_state, AppLayerParserState *pstate, + uint8_t *input, uint32_t input_len, AppLayerParserResult *output) { + SMBState *sstate = (SMBState *) smb_state; + uint8_t *p = input; + + if (sstate->andx.paddingparsed) { + while (sstate->andx.datalength-- && sstate->bytecount.bytecount-- && input_len--) { + printf("0x%02x ", *(p++)); + } + } + sstate->bytesprocessed += (p - input); + return (p - input); +} + + +/** + * \brief Obtain SMB WordCount which is 2 times the value. * Reset bytecount.bytecountbytes to 0. * Determine if this is an SMB AndX Command */ @@ -169,34 +422,71 @@ static int SMBGetByteCount(void *smb_state, AppLayerParserState *pstate, SCReturnInt(p - input); } +/** + * \brief SMBParseWordCount parses the SMB Wordcount portion of the SMB Transaction. + * until sstate->wordcount.wordcount bytes are parsed. + */ static int SMBParseWordCount(void *smb_state, AppLayerParserState *pstate, uint8_t *input, uint32_t input_len, AppLayerParserResult *output) { SCEnter(); SMBState *sstate = (SMBState *) smb_state; uint8_t *p = input; - while (sstate->wordcount.wordcount > 0 && input_len > 0) { - if (sstate->andx.isandx) { - SMBParseAndX(smb_state, pstate, input, input_len, output); + uint32_t retval = 0; + uint32_t parsed = 0; + if ((sstate->smb.flags & SMB_FLAGS_SERVER_TO_REDIR) && sstate->smb.command == SMB_COM_READ_ANDX) { + retval = SMBParseReadAndX(sstate, pstate, input + parsed, input_len, output); + parsed += retval; + input_len -= retval; + sstate->wordcount.wordcount -= retval; + return retval; + } else if (((sstate->smb.flags & SMB_FLAGS_SERVER_TO_REDIR) == 0) && sstate->smb.command == SMB_COM_WRITE_ANDX) { + retval = SMBParseWriteAndX(sstate, pstate, input + parsed, input_len, output); + parsed += retval; + input_len -= retval; + sstate->wordcount.wordcount -= retval; + return retval; + } else { /* Generic WordCount Handler */ + while (sstate->wordcount.wordcount-- && input_len--) { + printf("0x%02x ", *(p++)); } - SCLogDebug("0x%02x", *(p++)); - - sstate->wordcount.wordcount--; - input_len--; + printf("\n"); + sstate->bytesprocessed += (p - input); + return (p - input); + SCReturnInt(p - input); } - sstate->bytesprocessed += (p - input); - SCReturnInt(p - input); } +/** + * \brief SMBParseByteCount parses the SMB ByteCount portion of the SMB Transaction. + * until sstate->bytecount.bytecount bytes are parsed. + */ + static int SMBParseByteCount(void *smb_state, AppLayerParserState *pstate, uint8_t *input, uint32_t input_len, AppLayerParserResult *output) { SCEnter(); SMBState *sstate = (SMBState *) smb_state; uint8_t *p = input; + uint32_t retval = 0; + uint32_t parsed = 0; + if (((sstate->smb.flags & SMB_FLAGS_SERVER_TO_REDIR) && sstate->smb.command == SMB_COM_READ_ANDX) || + (((sstate->smb.flags & SMB_FLAGS_SERVER_TO_REDIR) == 0) && sstate->smb.command == SMB_COM_WRITE_ANDX)) { + if (sstate->andx.paddingparsed == 0) { + retval = PaddingParser(sstate, pstate, input + parsed, input_len, output); + parsed += retval; + input_len -= retval; + } + if (sstate->andx.datalength) { + retval = DataParser(sstate, pstate, input + parsed, input_len, output); + parsed += retval; + input_len -= retval; + } + } + while (sstate->bytecount.bytecount && input_len) { SCLogDebug("0x%02x bytecount %u input_len %u", *(p++), - sstate->bytecount.bytecount, input_len); + sstate->bytecount.bytecount, input_len); sstate->wordcount.wordcount--; input_len--; @@ -486,6 +776,12 @@ static int SMBParse(void *smb_state, AppLayerParserState *pstate, SCReturnInt(1); } +/** +* \brief determines if the SMB command is an ANDX command +* \retval 1 if smb command is an AndX command +* \retval 0 if smb command is not an AndX command +*/ + int isAndX(SMBState *smb_state) { switch (smb_state->smb.command) { case SMB_NO_SECONDARY_ANDX_COMMAND: @@ -497,6 +793,7 @@ int isAndX(SMBState *smb_state) { case SMB_COM_LOGOFF_ANDX: case SMB_COM_TREE_CONNECT_ANDX: case SMB_COM_NT_CREATE_ANDX: + smb_state->andx.andxbytesprocessed = 0; return 1; default: return 0; @@ -522,36 +819,26 @@ static void SMBStateFree(void *s) { void RegisterSMBParsers(void) { AppLayerRegisterProto("smb", ALPROTO_SMB, STREAM_TOSERVER, SMBParse); AppLayerRegisterProto("smb", ALPROTO_SMB, STREAM_TOCLIENT, SMBParse); - /*AppLayerRegisterParser("nbss.hdr", ALPROTO_SMB, SMB_PARSE_NBSS_HEADER, - NBSSParseHeader, "smb"); - AppLayerRegisterParser("smb.hdr", ALPROTO_SMB, SMB_PARSE_SMB_HEADER, - SMBParseHeader, "smb"); - AppLayerRegisterParser("smb.getwordcount", ALPROTO_SMB, SMB_PARSE_GET_WORDCOUNT, - SMBGetWordCount, "smb"); - AppLayerRegisterParser("smb.wordcount", ALPROTO_SMB, SMB_PARSE_WORDCOUNT, - SMBParseWordCount, "smb"); - AppLayerRegisterParser("smb.getbytecount", ALPROTO_SMB, SMB_PARSE_GET_BYTECOUNT, - SMBGetByteCount, "smb"); - AppLayerRegisterParser("smb.bytecount", ALPROTO_SMB, SMB_PARSE_BYTECOUNT, - SMBParseByteCount, "smb"); - */ AppLayerRegisterStateFuncs(ALPROTO_SMB, SMBStateAlloc, SMBStateFree); } /* UNITTESTS */ #ifdef UNITTESTS +/** + * \test SMBParserTest01 tests the NBSS and SMB header decoding + */ int SMBParserTest01(void) { int result = 1; Flow f; uint8_t smbbuf[] = "\x00\x00\x00\x85" // NBSS - "\xff\x53\x4d\x42\x72\x00\x00\x00" // SMB - "\x00\x18\x53\xc8\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\xff\xfe\x00\x00\x00\x00" + "\xff\x53\x4d\x42\x72\x00\x00\x00" // SMB + "\x00\x18\x53\xc8\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\xff\xfe\x00\x00\x00\x00" "\x00" // WordCount - "\x62\x00" // ByteCount - "\x02\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20" + "\x62\x00" // ByteCount + "\x02\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20" "\x31\x2e\x30\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00\x02\x57\x69\x6e\x64\x6f\x77\x73" "\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00\x02\x4c" "\x4d\x31\x2e\x32\x58\x30\x30\x32\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00\x02\x4e\x54" diff --git a/src/app-layer-smb.h b/src/app-layer-smb.h index 2bbf97b14a..169390f31a 100644 --- a/src/app-layer-smb.h +++ b/src/app-layer-smb.h @@ -73,11 +73,13 @@ typedef struct bytecount_ { }bytecount_t, *pbytyecount_t; typedef struct andxcount_ { - uint8_t isandx; - uint8_t andxcommand; - uint16_t andxoffset; - uint16_t andxbytesprocessed; - uint64_t writeandxoffset; + uint8_t isandx; + uint8_t paddingparsed; + uint8_t andxcommand; + uint16_t andxoffset; + uint16_t andxbytesprocessed; + uint32_t datalength; + uint64_t dataoffset; }andx_t, *pandx_t; typedef struct SMBState_ {