/*++ Copyright (c) 1989 Microsoft Corporation Module Name: smbdump.c Abstract: This module contains various debug routines for dumping structures maintained by the LAN Manager server. Author: David Treadwell (davidtr) 28-Sept-1990 Revision History: Peter Gray (w-peterg) 13-Apr-1992 Support for NT style SMBs added. Stephan Mueller (t-stephm) 15-June-1992 Changes required to display NT Smbs (according to Spec revision 2.22, dated 19-June-1992). Many bug fixes --*/ #define INCLUDE_SMB_TRANSACTION #define INCLUDE_SMB_SEARCH #include "smbdump.h" #include #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) // FIXFIX: The following macro is bogus. It will only work on little-endian // FIXFIX: machines. There should be a global macro (like SmbGetUlong) that // FIXFIX: will allow the NT to be ported to other machines. #define SmbPutUquad(to,from) \ (to)->LowPart = SmbGetUlong(&(from)->LowPart); \ (to)->HighPart = SmbGetUlong(&(from)->HighPart) VOID SmbDump ( IN PVOID Smb, IN CLONG SmbLength, IN PVOID SmbAddress, IN CLONG SmbDumpVerbosityLevel, IN CLONG SmbDumpRawLength, IN BOOLEAN IsServer ) { UCHAR command = ((PNT_SMB_HEADER)Smb)->Command; USHORT subcommand; PCHAR parameters = (PCHAR)( (PNT_SMB_HEADER)Smb + 1 ); PSMB_FIELD_DESCRIPTION descriptor; BOOLEAN isResponse; BOOLEAN ntFormatStatus; BOOLEAN unicodeStrings; USHORT usError = 0; ULONG ulError = 0L; if ( SmbLength == 0 ) { // // This is an empty raw buffer // if ( SmbDumpVerbosityLevel > SMBTRACE_VERBOSITY_OFF ) { printf( "(EMPTY BUFFER)\n" ); } return; } if ( SmbGetUlong( (PULONG)Smb ) != SMB_HEADER_PROTOCOL ) { // // This is a raw buffer // if ( SmbDumpVerbosityLevel > SMBTRACE_VERBOSITY_OFF ) { printf( "(RAW BUFFER, %ld BYTES)\n", SmbLength ); if ( SmbDumpRawLength > 0 ) { SmbDumpRawData( Smb, MIN( SmbLength, SmbDumpRawLength ), 0 ); } } return; } isResponse = (BOOLEAN)( ( ((PNT_SMB_HEADER)Smb)->Flags & SMB_FLAGS_SERVER_TO_REDIR) ? TRUE : FALSE ); ntFormatStatus = (BOOLEAN)( ( ((PNT_SMB_HEADER)Smb)->Flags2 & SMB_FLAGS2_NT_STATUS) ? TRUE : FALSE ); unicodeStrings = (BOOLEAN)( ( ((PNT_SMB_HEADER)Smb)->Flags2 & SMB_FLAGS2_UNICODE) ? TRUE : FALSE ); if ( !isResponse && command == SMB_COM_TRANSACTION || command == SMB_COM_TRANSACTION2 || command == SMB_COM_NT_TRANSACT ) { SmbDumpSetTransSubCommand( (PNT_SMB_HEADER)Smb, SmbGetUshort( (PUSHORT)((PREQ_TRANSACTION)( (PNT_SMB_HEADER)Smb + 1 ))->Buffer ) ); } if ( SmbDumpVerbosityLevel == SMBTRACE_VERBOSITY_OFF ) { return; } if ( isResponse && !(SmbDumpHeuristics & SMB_DUMP_RESPONSE) ) { return; } if ( !isResponse && !(SmbDumpHeuristics & SMB_DUMP_REQUEST) ) { return; } SmbDumpSingleLine( Smb, SmbLength, SmbAddress, IsServer, isResponse, ntFormatStatus ); if ( SmbDumpRawLength > 0 ) { SmbDumpRawData( Smb, MIN( SmbLength, SmbDumpRawLength ), 0 ); } if ( SmbDumpVerbosityLevel <= SMBTRACE_VERBOSITY_SINGLE_LINE ) { return; } if ( !ntFormatStatus && ( (usError = SmbGetUshort( &((PNT_SMB_HEADER)Smb)->Status.DosError.Error )) != 0 ) ) { SmbDumpError( ((PNT_SMB_HEADER)Smb)->Status.DosError.ErrorClass, SmbGetUshort( &((PNT_SMB_HEADER)Smb)->Status.DosError.Error ) ); } else if( ntFormatStatus && ( (ulError = SmbGetUlong( &((PNT_SMB_HEADER)Smb)->Status.NtStatus)) != STATUS_SUCCESS ) ) { SmbDumpNtError( SmbGetUlong( &((PNT_SMB_HEADER)Smb)->Status.NtStatus) ); } if ( SmbDumpVerbosityLevel == SMBTRACE_VERBOSITY_ERROR ) { return; } SmbDumpHeader( SmbDumpVerbosityLevel, Smb, ntFormatStatus ); if ( SmbDumpVerbosityLevel == SMBTRACE_VERBOSITY_HEADER ) { printf( "\n" ); return; } // // As long as there are more parameter blocks (And X commands), print // out the fields. // if ( isResponse ) { descriptor = SmbDumpTable[command].ResponseDescriptor; } else { descriptor = SmbDumpTable[command].RequestDescriptor; } do { // // If write and close SMB, choose the correct descriptor depending // on the word count. // if ( command == SMB_COM_WRITE_AND_CLOSE ) { if ( *(PCHAR)( (PNT_SMB_HEADER)Smb + 1 ) == 6 ) { if ( isResponse ) { descriptor = SmbDumpTable[SMB_COM_WRITE].ResponseDescriptor; } else { descriptor = SmbDumpTable[SMB_COM_WRITE].RequestDescriptor; } } else { if ( isResponse ) { descriptor = SmbDumpTable[SMB_COM_WRITE_ANDX].ResponseDescriptor; } else { descriptor = SmbDumpTable[SMB_COM_WRITE_ANDX].RequestDescriptor; } } } else { // // Check if this is an NT variant of a common SMB by looking // at the word count and checking in the table. Usually, only // requests are modified in this way, often adding two words // to allow for 64bit offsets. // if ( ( SmbDumpTable[command].NtDescriptor !=NULL ) && ( *(PCHAR)( (PNT_SMB_HEADER)Smb + 1 ) == SmbDumpTable[command].NtSpecialWordCount ) ) { if( !isResponse && SmbDumpTable[command].NtIsRequest ) { descriptor = SmbDumpTable[command].NtDescriptor; } if( isResponse && !SmbDumpTable[command].NtIsRequest ) { descriptor = SmbDumpTable[command].NtDescriptor; } } } // // Call SmbDumpDescriptor to display the fields in the SMB. // If this routine returns 0, then there was no parameter // information for the SMB, so just quit. // if ( SmbDumpDescriptor( SmbDumpVerbosityLevel, descriptor, parameters, unicodeStrings ) == 0 ) { return; } // // If the SMB is a Transact SMB, dump the parameter block. // if ( command == SMB_COM_TRANSACTION2 || command == SMB_COM_NT_TRANSACT ) { if ( isResponse ) { subcommand = (UCHAR)SmbDumpGetTransSubCommand( (PNT_SMB_HEADER)Smb ); if ( command == 0xFF ) { printf( "Transaction not found in list!!!\n" ); return; } parameters = (PCHAR)Smb + SmbGetUshort( &((PRESP_TRANSACTION)parameters)-> ParameterOffset ); if( command == SMB_COM_TRANSACTION2 ) { descriptor = SmbTrans2DumpTable[subcommand].ResponseDescriptor; } else { descriptor = SmbNtTransDumpTable[subcommand].ResponseDescriptor; } } else { subcommand = *(PUCHAR)&(((PREQ_TRANSACTION)parameters)->Buffer[0]); parameters = (PCHAR)Smb + SmbGetUshort( &((PREQ_TRANSACTION)parameters)-> ParameterOffset ); if( command == SMB_COM_TRANSACTION2 ) { descriptor = SmbTrans2DumpTable[subcommand].RequestDescriptor; } else { descriptor = SmbNtTransDumpTable[subcommand].RequestDescriptor; } } (VOID)SmbDumpDescriptor( SmbDumpVerbosityLevel, descriptor, parameters, unicodeStrings ); } else if( command == SMB_COM_TRANSACTION ) { if ( SmbDumpVerbosityLevel >= SMBTRACE_VERBOSITY_NONESSENTIAL ) printf("Would dump Transaction's parameter block here.\n"); } // // Break or setup for dumping a followon command if there is one. // if ( !SmbDumpTable[command].IsAndXCommand || ((PGENERIC_ANDX)parameters)->AndXCommand == SMB_COM_NO_ANDX_COMMAND || ( usError | ulError ) ) { break; } else { command = ((PGENERIC_ANDX)parameters)->AndXCommand; parameters = (PCHAR)Smb + SmbGetUshort( &((PGENERIC_ANDX)parameters)->AndXOffset ); if ( isResponse ) { descriptor = SmbDumpTable[command].ResponseDescriptor; } else { descriptor = SmbDumpTable[command].RequestDescriptor; } printf( "And--%s:\n", SmbDumpTable[command].SmbName ); } } while ( TRUE ); printf( "\n" ); return; } // SmbDump // // SMB field dump routines. All of these conform to the // SMB_FIELD_DUMP_ROUTINE declaration. // #define NON_ESSENTIAL_RETURN(a) \ if ( !EssentialField && \ SmbDumpVerbosityLevel < SMBTRACE_VERBOSITY_NONESSENTIAL ) { \ return a; \ } USHORT SmbDumpAccess ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { USHORT value = SmbGetUshort( (PUSHORT)Field ); USHORT share = (USHORT)(value & SMB_DA_SHARE_MASK); USHORT _access = (USHORT)(value & SMB_DA_ACCESS_MASK); Context; Result; UnicodeStrings; NON_ESSENTIAL_RETURN( 2 ); printf( "%04lx - ", value ); if ( value & SMB_DA_WRITE_THROUGH ) { printf( "WriteThrough, " ); } switch ( _access ) { case SMB_DA_ACCESS_READ: printf( "Read Access, " ); break; case SMB_DA_ACCESS_WRITE: printf( "Write Access, " ); break; case SMB_DA_ACCESS_READ_WRITE: printf( "Read/Write Access, " ); break; case SMB_DA_ACCESS_EXECUTE: printf( "Execute Access, " ); break; case SMB_DA_FCB: printf( "FCB Open" ); return 2; default: printf( "INVALID" ); } switch ( share ) { case SMB_DA_SHARE_COMPATIBILITY: printf( "Compatibility Mode" ); break; case SMB_DA_SHARE_EXCLUSIVE: printf( "Exclusive" ); break; case SMB_DA_SHARE_DENY_WRITE: printf( "Deny Write" ); break; case SMB_DA_SHARE_DENY_READ: printf( "Deny Read" ); break; case SMB_DA_SHARE_DENY_NONE: printf( "Deny None" ); break; } return 2; } // SmbDumpAccess USHORT SmbDumpAction ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { USHORT value = SmbGetUshort( (PUSHORT)Field ); USHORT action = (USHORT)(value & 0x03); Context; Result; UnicodeStrings; NON_ESSENTIAL_RETURN( 2 ); printf( "%04lx - ", value ); if ( value & SMB_OACT_OPLOCK ) { printf( "Oplock Granted, " ); } else { printf( "Oplock Not Granted, " ); } switch ( action ) { case SMB_OACT_OPENED: printf( "Opened" ); break; case SMB_OACT_CREATED: printf( "Created" ); break; case SMB_OACT_TRUNCATED: printf( "Truncated" ); break; default: printf( "INVALID" ); break; } return 2; } // SmbDumpAction // dump ASCIIZ prefixed by SMB_FORMAT_ASCII format byte USHORT SmbDumpFAsciiZ ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { PCHAR FieldText = (PCHAR)Field + 1; // +1 for Field format, +1 for \0 character USHORT retval = (USHORT)( strlen( (PSZ)FieldText ) + 2 ); NON_ESSENTIAL_RETURN( retval ); if ( *(PUCHAR)Field != SMB_FORMAT_ASCII ) { printf( "(Bad token == %02x) ", *(PUCHAR)Field ); } printf( "%s", FieldText ); return ( retval ); Context; Result; } // SmbDumpFAsciiZ // dump UnicodeZ prefixed by SMB_FORMAT_ASCII format byte USHORT SmbDumpFUnicodeZ ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { PCHAR FieldText = (PCHAR)Field + 1; // +1 for Field format, +2 for two byte string terminator USHORT retval = 1 + sizeof(WCHAR); // Ensure string is aligned: if at odd address, round up, // and remember that we ate one more byte. This is a hack; // alignment should be with respect to the start of the SMB, // not address 0, but RtlAllocateHeap, used to allocate our // packets, is guaranteed to give properly aligned memory. if ( (ULONG)FieldText & 1 ) { FieldText++; retval++; } retval += (USHORT)( wcslen( (PWSTR)FieldText ) * sizeof(WCHAR) ); NON_ESSENTIAL_RETURN( retval ); if ( *(PUCHAR)Field != SMB_FORMAT_ASCII ) { printf( "(Bad token == %02x) ", *(PUCHAR)Field ); } printf( "%ws" , FieldText ); return ( retval ); Context; Result; } // SmbDumpFUnicodeZ // dump ASCIIZ or UnicodeZ (as determined by UnicodeStrings) // prefixed by SMB_FORMAT_ASCII format byte USHORT SmbDumpFStringZ ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { if ( UnicodeStrings ) { return SmbDumpFUnicodeZ( SmbDumpVerbosityLevel, Context, Result, EssentialField, Field, UnicodeStrings ); } else { return SmbDumpFAsciiZ( SmbDumpVerbosityLevel, Context, Result, EssentialField, Field, UnicodeStrings ); } } // SmbDumpFStringZ USHORT SmbDumpAsciiZ ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { // +1 for \0 character USHORT retval = (USHORT)( strlen( (PSZ)Field) + 1 ); NON_ESSENTIAL_RETURN( retval ); printf( "%s", Field ); return retval; Context; Result; UnicodeStrings; } // SmbDumpAsciiZ USHORT SmbDumpUnicodeZ ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { // +2 for two byte string terminator USHORT retval = sizeof(WCHAR); // Ensure string is aligned: if at odd address, round up, // and remember that we ate one more byte. This is a hack; // alignment should be with respect to the start of the SMB, // not address 0, but RtlAllocateHeap, used to allocate our // packets, is guaranteed to give properly aligned memory. if ( (ULONG)Field & 1 ) { Field = (PCHAR)Field + 1; retval++; } retval += (USHORT)( wcslen( (PWSTR)Field ) * sizeof(WCHAR) ); NON_ESSENTIAL_RETURN( retval ); printf( "%ws", Field ); return ( retval ); Context; Result; UnicodeStrings; } // SmbDumpUnicodeZ // dump ASCIIZ or UnicodeZ (as determined by UnicodeStrings) USHORT SmbDumpStringZ ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { if ( UnicodeStrings ) { return SmbDumpUnicodeZ( SmbDumpVerbosityLevel, Context, Result, EssentialField, Field, UnicodeStrings ); } else { return SmbDumpAsciiZ( SmbDumpVerbosityLevel, Context, Result, EssentialField, Field, UnicodeStrings ); } } // SmbDumpStringZ // dump non-\0 terminated Ascii, using length found in *(PULONG)Context USHORT SmbDumpAscii ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { USHORT retval; if (Context == NULL) { printf( "String length unavailable.\n" ); return 0; } retval = (USHORT)(*(PULONG)Context); // we still lose on very long strings NON_ESSENTIAL_RETURN( retval ); // note that if string does contain a \0, the rest of the string // won't be displayed printf( "%.*s", retval, (PSZ)Field ); return ( retval ); Result; UnicodeStrings; } // SmbDumpAscii // dump non terminated Unicode, using length (in bytes, not characters) // found in *(PULONG)Context USHORT SmbDumpUnicode ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { USHORT retval; USHORT length; if (Context == NULL) { printf( "String length unavailable.\n" ); return 0; } length = retval = (USHORT)(*(PULONG)Context); // still lose on long strings // Ensure string is aligned: if at odd address, round up, // and remember that we ate one more byte. This is a hack; // alignment should be with respect to the start of the SMB, // not address 0, but RtlAllocateHeap, used to allocate our // packets, is guaranteed to give properly aligned memory. if ( (ULONG)Field & 1 ) { Field = (PCHAR)Field + 1; retval++; } NON_ESSENTIAL_RETURN( retval ); // note that if string does contain a terminator, the rest of the string // won't be displayed printf( "%.*ws", length/sizeof(WCHAR), (PSZ)Field ); return ( retval ); Result; UnicodeStrings; } // SmbDumpUnicode // dump non terminated ASCII or Unicode (as determined by UnicodeStrings) // using length (in bytes, not characters) found in *(PULONG)Context USHORT SmbDumpString ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { if ( UnicodeStrings ) { return SmbDumpUnicode( SmbDumpVerbosityLevel, Context, Result, EssentialField, Field, UnicodeStrings ); } else { return SmbDumpAscii( SmbDumpVerbosityLevel, Context, Result, EssentialField, Field, UnicodeStrings ); } } // SmbDumpString // Dunno what the difference between these two is USHORT SmbDumpDataBuffer ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { // BUGBUG: incomplete printf( "Would dump data here.\n" ); return 2; SmbDumpVerbosityLevel, Context, Result, Field,EssentialField,UnicodeStrings; } // SmbDumpDataBuffer // Dunno what the difference between these two is USHORT SmbDumpDataBuffer2 ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { // BUGBUG: incomplete printf( "Would dump data here.\n" ); return 2; SmbDumpVerbosityLevel, Context, Result, Field,EssentialField,UnicodeStrings; } // SmbDumpDataBuffer2 // dump length data bytes, where length is found in *(PULONG)Context USHORT SmbDumpDataBytes ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { USHORT length; if ( Context == NULL ) { printf( "Data length unavailable.\n" ); return 0; } length = (USHORT)(*(PULONG)Context); printf("\n"); SmbDumpRawData( Field, length, 0 ); return length; } // SmbDumpDataBytes USHORT SmbDumpDate ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { SMB_DATE date; Context; Result; UnicodeStrings; NON_ESSENTIAL_RETURN( 2 ); date.Ushort = SmbGetUshort( (PUSHORT)Field ); printf( "%02d/%02d/%04d", date.Struct.Month, date.Struct.Day, date.Struct.Year + 1980 ); return 2; } // SmbDumpDate // Dump Protocol Dialects. Note that these strings are always ANSI, // never Unicode. USHORT SmbDumpDialects ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { USHORT len = SmbGetUshort( (PUSHORT)(Field)-1 ); Context; Result; UnicodeStrings; NON_ESSENTIAL_RETURN( len ); printf( "\n" ); // start on a new line do { if ( *(PCHAR)Field == SMB_FORMAT_DIALECT ) { Field = (PVOID)((PCHAR)Field + 1); printf( "\t%s\n", (PCHAR)Field ); Field = (PVOID)((PCHAR)Field + strlen((PCHAR)Field) + 1); } else if ( *(PCHAR)Field == 0 ) { break; } else { printf( "(Bad token == %02x) ", *(PUCHAR)Field ); break; } } while( 1 ); return len; } // SmbDumpDialects USHORT SmbDumpFlags4 ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { ULONG c = SmbGetUlong( (PULONG)Field ); CLONG i; PSMB_FLAGS_DESCRIPTION flagsDesc = Context; ULONG bitMask = 0; NON_ESSENTIAL_RETURN( 4 ); Result; UnicodeStrings; printf( "%04lx - ", c ); if ( Context == NULL ) { printf( "No flags information available.\n" ); return 4; } for ( i = 0; flagsDesc[i].BitMask != 0; i++ ) { bitMask |= flagsDesc[i].BitMask; if ( (flagsDesc[i].BitMask & c) != 0 ) { if ( flagsDesc[i].OnLabel != NULL ) { printf( "%s", flagsDesc[i].OnLabel ); } } else { if ( flagsDesc[i].OffLabel != NULL ) { printf( "%s", flagsDesc[i].OffLabel ); } } } if ( (~bitMask & c ) != 0 ) { printf( "UNKNOWN BIT(S) ON: %04lx\n", ~bitMask & c ); } return 4; } // SmbDumpFlags4 USHORT SmbDumpFlags2 ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { ULONG c = (ULONG) SmbGetUshort( (PUSHORT)Field ); SmbDumpFlags4( SmbDumpVerbosityLevel, Context, Result, EssentialField, &c, UnicodeStrings ); return 2; } // SmbDumpFlags2 // dump USHORT containing DeviceState, using USHORT FileType found // in *(PULONG)Context to indicate whether DeviceState is applicable USHORT SmbDumpDeviceState ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { ULONG c; USHORT FileType; c = (ULONG) SmbGetUshort( (PUSHORT)Field ); if (Context == NULL) { // Assume that the field is inapplicable FileType = FileTypeDisk; } else { FileType = (USHORT)(*(PULONG)Context); } if (FileType == FileTypeByteModePipe || FileType == FileTypeMessageModePipe) { SmbDumpUchar( SmbDumpVerbosityLevel, NULL, NULL, EssentialField, Field, UnicodeStrings ); // mask off instance count c = c & ~0xf; SmbDumpFlags4( SmbDumpVerbosityLevel, SmbDeviceStateFlags, Result, EssentialField, &c, UnicodeStrings ); } else { printf( "%04lx - Field inapplicable for FileType", c ); } return 2; } // SmbDumpDeviceState USHORT SmbDumpFlags1 ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { ULONG c = (ULONG) *((PUCHAR)Field); SmbDumpFlags4( SmbDumpVerbosityLevel, Context, Result, EssentialField, &c, UnicodeStrings ); return 1; } // SmbDumpFlags1 // dump ULONG enum, leave dumped value in *(PULONG)Result if it's non-NULL USHORT SmbDumpEnum4 ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { ULONG c = SmbGetUlong( (PULONG)Field ); CLONG i; PSMB_ENUM_DESCRIPTION enumDesc = Context; BOOLEAN found=FALSE; if (Result != NULL) { *(PULONG)Result = (ULONG)c; } NON_ESSENTIAL_RETURN( 4 ); UnicodeStrings; printf( "%04lx - ", c ); if ( Context == NULL ) { printf( "No enumerated information available.\n" ); return 4; } for ( i = 0; enumDesc[i].Label != NULL; i++ ) { if ( enumDesc[i].EnumValue == c) { printf( "%s", enumDesc[i].Label ); found=TRUE; } } if(!found) { printf( "UNKNOWN VALUE: %04lx\n", c ); } return 4; } // SmbDumpEnum4 // dump USHORT enum, leave dumped value in *(PULONG)Result if it's non-NULL USHORT SmbDumpEnum2 ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { ULONG c = (ULONG) SmbGetUshort( (PUSHORT)Field ); SmbDumpEnum4( SmbDumpVerbosityLevel, Context, Result, EssentialField, &c, UnicodeStrings ); return 2; } // SmbDumpEnum2 // dump UCHAR enum, leave dumped value in *(PULONG)Result if it's non-NULL USHORT SmbDumpEnum1 ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { ULONG c = (ULONG) *((PUCHAR)Field); SmbDumpEnum4( SmbDumpVerbosityLevel, Context, Result, EssentialField, &c, UnicodeStrings ); return 1; } // SmbDumpEnum1 USHORT SmbDumpOpenFunction ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { USHORT value = SmbGetUshort( (PUSHORT)Field ); USHORT _open = (USHORT)(value & SMB_OFUN_OPEN_MASK); USHORT create = (USHORT)(value & SMB_OFUN_CREATE_MASK); Context; Result; UnicodeStrings; NON_ESSENTIAL_RETURN( 2 ); printf( "%04lx - Create: ", value ); switch ( create ) { case SMB_OFUN_CREATE_FAIL: printf( "Fail, " ); break; case SMB_OFUN_CREATE_CREATE: printf( "Succeed, " ); break; default: printf( "INVALID" ); break; } printf( "Open: " ); switch ( _open ) { case SMB_OFUN_OPEN_FAIL: printf( "Fail" ); break; //case SMB_OFUN_OPEN_APPEND: case SMB_OFUN_OPEN_OPEN: printf( "Succeed/Append" ); break; case SMB_OFUN_OPEN_TRUNCATE: printf( "Truncate" ); break; default: printf( "INVALID" ); break; } return 2; } // SmbDumpOpenFunction USHORT SmbDumpResumeKey ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { PCHAR p = Field; ULONG value; ANSI_STRING fileName; Context; Result; UnicodeStrings; NON_ESSENTIAL_RETURN( sizeof(SMB_RESUME_KEY) ); if ( *(PCHAR)Field != SMB_FORMAT_VARIABLE ) { printf( "(Bad token == %02lx)\n", *(PUCHAR)Field & (UCHAR)0xFF ); } p++; value = SmbGetUshort( p ); printf( "\n Variable Block Length.: %lx\n", value ); p += sizeof(USHORT); value = *p; printf( " Reserved..............: %lx\n", value ); p += sizeof(UCHAR); fileName.Buffer = p; fileName.Length = 11; printf( " FileName..............: %Z\n", &fileName ); p += 11; value = *p; printf( " Sid...................: %lx\n", value ); p += sizeof(UCHAR); value = SmbGetUlong( p ); printf( " File Index............: %lx\n", value ); p += sizeof(ULONG); value = SmbGetUlong( p ); printf( " Consumer..............: %lx\n", value ); return sizeof(SMB_RESUME_KEY); } // SmbDumpResumeKey USHORT SmbDumpNtTime ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { TIME_FIELDS timeFields; NON_ESSENTIAL_RETURN( 8 ); Context; Result; UnicodeStrings; // // special case when time is 'all-zeroes'. // if ( ((PLARGE_INTEGER)Field)->LowPart == 0 && ((PLARGE_INTEGER)Field)->HighPart == 0 ) { printf("[Time unspecified]"); } else { RtlTimeToTimeFields( (PLARGE_INTEGER)Field, &timeFields ); printf( "%02ld/%02ld/%04ld %02ld:%02ld:%02ld", timeFields.Month, timeFields.Day, timeFields.Year, timeFields.Hour, timeFields.Minute, timeFields.Second ); } return 8; } // SmbDumpNtTime USHORT SmbDumpTime ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { SMB_TIME time; Context; Result; UnicodeStrings; NON_ESSENTIAL_RETURN( 2 ); time.Ushort = SmbGetUshort( (PUSHORT)Field ); printf( "%02d/%02d/%04d", time.Struct.Hours, time.Struct.Minutes, time.Struct.TwoSeconds * 2 ); return 2; } // SmbDumpTime USHORT SmbDumpTimeSince1970 ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { struct tm timeFields; Context, Result, UnicodeStrings; NON_ESSENTIAL_RETURN( 4 ); // This is midnight, January 1, 1970 + a LOT of seconds // as long as ints and longs are marginally compatible // FIXFIX: we could improve this by doing some of the division manually // FIXFIX: to ensure the various values are well within the range of // FIXFIX: even 16 bit ints. timeFields.tm_isdst = 0; timeFields.tm_year = 70; timeFields.tm_mon = 0; timeFields.tm_mday = 0; timeFields.tm_hour = 0; timeFields.tm_min = 0; timeFields.tm_sec = SmbGetUlong( (PULONG)Field ); // mktime will normalize the value given, which is all we want // i.e. we can ignore the return value mktime( &timeFields ); // printf( "Sec. since 1970: %08lx", SmbGetUlong( (PULONG)Field ) ); printf( "%02d/%02d/%04d %02d:%02d:%02d", timeFields.tm_mon, timeFields.tm_mday, timeFields.tm_year, timeFields.tm_hour, timeFields.tm_min, timeFields.tm_sec ); return 4; } // SmbDumpTimeSince1970 // dump UCHAR, leave dumped value in *(PULONG)Result if it's non-NULL USHORT SmbDumpUchar ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { UCHAR c = *(PUCHAR)Field; if (Result != NULL) { *(PULONG)Result = (ULONG)c; } NON_ESSENTIAL_RETURN( 1 ); printf( "%02lx", c ); return 1; Context; UnicodeStrings; } // SmbDumpUchar // dump USHORT, leave dumped value in *(PULONG)Result if it's non-NULL USHORT SmbDumpUshort ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { USHORT c = SmbGetUshort( (PUSHORT)Field ); if (Result != NULL) { *(PULONG)Result = (ULONG)c; } NON_ESSENTIAL_RETURN( 2 ); printf( "%04lx", c ); return 2; Context; UnicodeStrings; } // SmbDumpUshort // dump ULONG, leave dumped value in *(PULONG)Result if it's non-NULL USHORT SmbDumpUlong ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { ULONG c = SmbGetUlong( (PULONG)Field ); if (Result != NULL) { *(PULONG)Result = c; } NON_ESSENTIAL_RETURN( 4 ); printf( "%08lx", c ); return 4; Context; UnicodeStrings; } // SmbDumpUlong USHORT SmbDumpUquad ( IN CLONG SmbDumpVerbosityLevel, IN PVOID Context, OUT PVOID Result, IN BOOLEAN EssentialField, IN PVOID Field, IN BOOLEAN UnicodeStrings ) { LARGE_INTEGER c; SmbPutUquad( &c, (PLARGE_INTEGER)Field ); NON_ESSENTIAL_RETURN( 8 ); printf( "%08lx:%08lx", c.HighPart, c.LowPart ); return 8; Context; UnicodeStrings; } // SmbDumpUquad // // Other routines used in displaying SMBs. // USHORT SmbDumpDescriptor ( IN CLONG SmbDumpVerbosityLevel, IN PSMB_FIELD_DESCRIPTION Descriptor, IN PCHAR Parameters, IN BOOLEAN UnicodeStrings ) { PCHAR parameters = Parameters; CSHORT i; if ( Descriptor == NULL ) { printf( " No parameter information available for this command.\n\n" ); return 0; } for ( i = 0; Descriptor[i].DumpRoutine != NULL; i++ ) { if ( SmbDumpVerbosityLevel >= SMBTRACE_VERBOSITY_NONESSENTIAL || Descriptor[i].EssentialField ) { printf( " %s", Descriptor[i].Label ); } parameters += Descriptor[i].DumpRoutine( SmbDumpVerbosityLevel, Descriptor[i].Context, Descriptor[i].Result, Descriptor[i].EssentialField, parameters, UnicodeStrings ); if ( Descriptor[i].EndLabel != NULL && ( SmbDumpVerbosityLevel >= SMBTRACE_VERBOSITY_NONESSENTIAL || Descriptor[i].EssentialField ) ) { printf( "%s", Descriptor[i].EndLabel ); } } return (USHORT)(parameters - Parameters); } // SmbDumpDescriptor VOID SmbDumpError ( IN UCHAR ErrorClass, IN USHORT ErrorCode ) { CSHORT i; printf( " ERROR -- Class %ld: ", ErrorClass ); switch ( ErrorClass ) { case SMB_ERR_CLASS_DOS: printf( "DOS " ); break; case SMB_ERR_CLASS_SERVER: printf( "Server " ); break; case SMB_ERR_CLASS_HARDWARE: printf( "Hardware " ); break; default: printf( "Unknown Code %ld\n", ErrorCode ); return; } printf( "Code %ld: ", ErrorCode ); for ( i = 0; SmbErrors[ErrorClass][i].ErrorValue != 0; i++ ) { if ( SmbErrors[ErrorClass][i].ErrorValue == ErrorCode ) { printf( "%s\n", SmbErrors[ErrorClass][i].ErrorName ); return; } } printf( "Unknown\n" ); return; } // SmbDumpError VOID SmbDumpNtError ( NTSTATUS Status ) { printf( " ERROR -- Code %X: (error text here)\n", Status ); // BUGBUG: incomplete. Perhaps use FormatMessage to produce proper // BUGBUG: text. return; } USHORT SmbDumpGetTransSubCommand ( IN PNT_SMB_HEADER SmbHeader ) { CSHORT index; USHORT tid = SmbGetAlignedUshort( &SmbHeader->Tid ); USHORT pid = SmbGetAlignedUshort( &SmbHeader->Pid ); USHORT uid = SmbGetAlignedUshort( &SmbHeader->Uid ); USHORT mid = SmbGetAlignedUshort( &SmbHeader->Mid ); for ( index = 0; index < SMB_DUMP_MAX_TRANSACTIONS; index++ ) { if ( tid == SmbDumpTransList[index].Tid && pid == SmbDumpTransList[index].Pid && uid == SmbDumpTransList[index].Uid && mid == SmbDumpTransList[index].Mid ) { return SmbDumpTransList[index].SubCommand; } } printf( "TRANSACTION NOT FOUND!!!\n" ); return (USHORT)0xFFFF; } // SmbDumpGetTransSubCommand VOID SmbDumpHeader ( IN CLONG SmbDumpVerbosityLevel, IN PNT_SMB_HEADER SmbHeader, IN BOOLEAN NtFormatStatus ) { UCHAR flags = SmbHeader->Flags; USHORT flags2 = SmbGetAlignedUshort( &SmbHeader->Flags2 ); USHORT unknownFlags2; printf( "Pid: %04lx, Tid: %04lx, Uid: %04lx, Mid: %04lx\n", SmbGetAlignedUshort( &SmbHeader->Pid ), SmbGetAlignedUshort( &SmbHeader->Tid ), SmbGetAlignedUshort( &SmbHeader->Uid ), SmbGetAlignedUshort( &SmbHeader->Mid ) ); printf( "Flags: %02lx - ", flags & (UCHAR)0xFF ); if ( flags & SMB_FLAGS_LOCK_AND_READ_OK ) { printf( "LockAndReadSupported " ); } if ( flags & SMB_FLAGS_SEND_NO_ACK ) { printf( "SendNoAck " ); } if ( flags & SMB_FLAGS_CASE_INSENSITIVE ) { printf( "CaseInsensitive " ); } if ( flags & SMB_FLAGS_CANONICALIZED_PATHS ) { printf( "CanonicalizedPaths " ); } if ( flags & SMB_FLAGS_OPLOCK ) { printf( "Oplock " ); } if ( flags & SMB_FLAGS_OPLOCK_NOTIFY_ANY ) { printf( "NotifyAny " ); } if ( flags & SMB_FLAGS_SERVER_TO_REDIR ) { printf( "Response " ); } printf( " Flags2: %04lx - ", flags2 & (USHORT)0xFFFF ); if ( flags2 & SMB_FLAGS2_KNOWS_LONG_NAMES ) { printf( "LongNames " ); } if ( flags2 & SMB_FLAGS2_KNOWS_EAS ) { printf( "EAs " ); } if ( flags2 & SMB_FLAGS2_IS_LONG_NAME ) { printf( "IsLongName " ); } if ( flags2 & SMB_FLAGS2_PAGING_IO ) { printf( "PagingIO " ); } if ( flags2 & SMB_FLAGS2_NT_STATUS ) { printf( "NtFormatStatus " ); } if ( flags2 & SMB_FLAGS2_UNICODE ) { printf( "Unicode " ); } unknownFlags2 = flags2 & ~(USHORT)( SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_KNOWS_EAS | SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_PAGING_IO | SMB_FLAGS2_NT_STATUS | SMB_FLAGS2_UNICODE ); if ( unknownFlags2 ) { printf( "UNKNOWN BITS ON: %04lx", unknownFlags2 ); } printf( "\n" ); if ( SmbDumpVerbosityLevel >= SMBTRACE_VERBOSITY_NONESSENTIAL ) { if ( !NtFormatStatus && ( SmbGetUshort( &SmbHeader->Status.DosError.Error ) == 0) ) { printf( "ErrorClass: 0, ErrorCode: 0\n" ); } else if( NtFormatStatus && ( SmbGetUlong( &SmbHeader->Status.NtStatus ) == STATUS_SUCCESS ) ) { printf( "NtStatus: STATUS_SUCCESS\n" ); } printf( "Command: %02lx, Reserved: %02lx\n", SmbHeader->Command, SmbHeader->Status.DosError.Reserved ); printf( "Reserved2[]: %04lx %04lx %04lx %04lx %04lx %04lx\n", SmbGetUshort( &SmbHeader->Reserved2[0] ), SmbGetUshort( &SmbHeader->Reserved2[1] ), SmbGetUshort( &SmbHeader->Reserved2[2] ), SmbGetUshort( &SmbHeader->Reserved2[3] ), SmbGetUshort( &SmbHeader->Reserved2[4] ), SmbGetUshort( &SmbHeader->Reserved2[5] ) ); } return; } // SmbDumpHeader VOID SmbDumpSetTransSubCommand ( IN PNT_SMB_HEADER SmbHeader, IN USHORT SubCommand ) { USHORT tid = SmbGetAlignedUshort( &SmbHeader->Tid ); USHORT pid = SmbGetAlignedUshort( &SmbHeader->Pid ); USHORT uid = SmbGetAlignedUshort( &SmbHeader->Uid ); USHORT mid = SmbGetAlignedUshort( &SmbHeader->Mid ); SmbDumpTransList[SmbDumpTransIndex].Tid = tid; SmbDumpTransList[SmbDumpTransIndex].Pid = pid; SmbDumpTransList[SmbDumpTransIndex].Uid = uid; SmbDumpTransList[SmbDumpTransIndex].Mid = mid; SmbDumpTransList[SmbDumpTransIndex].SubCommand = SubCommand; if ( ++SmbDumpTransIndex >= SMB_DUMP_MAX_TRANSACTIONS ) { SmbDumpTransIndex = 0; } return; } // SmbDumpSetTransSubCommand // ASCII-specific #undef isprint #define isprint(c) ( (c) >= ' ' && (c) <= '~' ) VOID SmbDumpRawData ( IN PCHAR DataStart, IN CLONG DataLength, IN CLONG Offset ) { CLONG lastByte; CCHAR lineBuffer[80]; PCCHAR bufferPtr; for ( lastByte = Offset + DataLength; Offset < lastByte; Offset += 16 ) { CLONG i; bufferPtr = lineBuffer; sprintf( bufferPtr, " %04lx: ", Offset ); bufferPtr += 10; for ( i = 0; i < 16 && Offset + i < lastByte; i++ ) { sprintf( bufferPtr, "%02lx", (UCHAR)DataStart[Offset + i] & (UCHAR)0xFF ); bufferPtr += 2; if ( i == 7 ) { *bufferPtr++ = '-'; } else { *bufferPtr++ = ' '; } } // // Print enough spaces so that the ASCII display lines up. // for ( ; i < 16; i++ ) { *bufferPtr++ = ' '; *bufferPtr++ = ' '; *bufferPtr++ = ' '; } *bufferPtr++ = ' '; *bufferPtr++ = ' '; *bufferPtr++ = '*'; for ( i = 0; i < 16 && Offset + i < lastByte; i++ ) { if ( isprint( DataStart[Offset + i] ) ) { *bufferPtr++ = (CCHAR)DataStart[Offset + i]; } else { *bufferPtr++ = '.'; } } *bufferPtr = 0; printf( "%s*\n", lineBuffer ); } return; } // SmbDumpRawData VOID SmbDumpSingleLine ( IN PNT_SMB_HEADER SmbHeader, IN CLONG SmbLength, IN PVOID SmbAddress, IN BOOLEAN IsServer, IN BOOLEAN IsResponse, IN BOOLEAN ntFormatStatus ) { UCHAR command = SmbHeader->Command; PGENERIC_ANDX parameters = (PGENERIC_ANDX)(SmbHeader + 1); if ( IsResponse ) { if ( IsServer ) { printf( "SEND RESP: " ); } else { printf( "RECV RESP: " ); } } else { if ( IsServer ) { printf( "RECV REQ: " ); } else { printf( "SEND REQ: " ); } } // // print an original address if one is provided // if ( SmbAddress != NULL ) { printf( "%08lx ", SmbAddress ); } else { printf( "-------- " ); } printf( "tid %04lx, pid %04lx, len %04lx, ", SmbGetAlignedUshort( &SmbHeader->Tid ), SmbGetAlignedUshort( &SmbHeader->Pid ), SmbLength ); printf( "%s%s", ( (SmbDumpTable[command].NtDescriptor !=NULL) && ( *(PCHAR)( SmbHeader + 1 ) == SmbDumpTable[command].NtSpecialWordCount ) // && !( !IsResponse ^^ SmbDumpTable[command].NtIsRequest ) ) ? "(NT) " : "", SmbDumpTable[command].SmbName ); if ( command == SMB_COM_TRANSACTION2 ) { USHORT subCommand; if ( IsResponse ) { subCommand = SmbDumpGetTransSubCommand( SmbHeader ); if ( subCommand == (USHORT)0xFF ) { printf( " USRV test" ); } else if ( subCommand > 0x0D ) { printf( "Error in SmbDumpTransTable!!! - SubCommand = 0x%lx\n", subCommand ); return; } else { printf( " %s", SmbTrans2DumpTable[subCommand].SmbName ); } } else { subCommand = SmbGetUshort( (PUSHORT)((PREQ_TRANSACTION)(SmbHeader + 1))->Buffer ); if ( subCommand == 0xFF ) { printf(" Usrv Test"); } else if ( subCommand > 0x0D ) { printf( "Error in SmbDumpTransTable!!! - SubCommand = 0x%lx\n", subCommand ); return; } else { printf( " %s", SmbTrans2DumpTable[subCommand].SmbName ); } } } else if ( command == SMB_COM_NT_TRANSACT ) { USHORT subCommand; if ( IsResponse ) { subCommand = SmbDumpGetTransSubCommand( SmbHeader ); if ( subCommand > 0x05 ) { printf("Error in SmbDumpNtTransTable!!! - SubCommand = 0x%lx\n", subCommand ); return; } else { printf( " %s", SmbNtTransDumpTable[subCommand].SmbName ); } } else { subCommand = SmbGetUshort( (PUSHORT)((PREQ_TRANSACTION)(SmbHeader + 1))->Buffer ); if ( subCommand > 0x05 ) { printf("Error in SmbDumpNtTransTable!!! - SubCommand = 0x%lx\n", subCommand ); return; } else { printf( " %s", SmbNtTransDumpTable[subCommand].SmbName ); } } } while ( SmbDumpTable[command].IsAndXCommand ) { command = parameters->AndXCommand; // Breakout for errors if ( (!ntFormatStatus && SmbGetUshort( &((PNT_SMB_HEADER)SmbHeader)->Status.DosError.Error ) != 0 ) || ( ntFormatStatus && SmbGetUlong( &((PNT_SMB_HEADER)SmbHeader)->Status.NtStatus) != STATUS_SUCCESS )) { break; } if ( command != SMB_COM_NO_ANDX_COMMAND ) { printf( ", %s", SmbDumpTable[command].SmbName ); parameters = (PGENERIC_ANDX)( (PCHAR)SmbHeader + SmbGetUshort( ¶meters->AndXOffset ) ); } else { break; } } printf( "\n" ); return; } // SmbDumpSingleLine