diff --git a/SMBLibrary/Authentication/AuthenticateMessage/AVPairUtils.cs b/SMBLibrary/Authentication/AuthenticateMessage/AVPairUtils.cs new file mode 100644 index 0000000..12d4396 --- /dev/null +++ b/SMBLibrary/Authentication/AuthenticateMessage/AVPairUtils.cs @@ -0,0 +1,74 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.Authentication +{ + public class AVPairUtils + { + public static byte[] GetAVPairSequence(string domainName, string computerName) + { + KeyValuePairList pairs = new KeyValuePairList(); + pairs.Add(AVPairKey.NbDomainName, UnicodeEncoding.Unicode.GetBytes(domainName)); + pairs.Add(AVPairKey.NbComputerName, UnicodeEncoding.Unicode.GetBytes(computerName)); + return AVPairUtils.GetAVPairSequenceBytes(pairs); + } + + public static byte[] GetAVPairSequenceBytes(KeyValuePairList pairs) + { + int length = 0; + foreach (KeyValuePair pair in pairs) + { + length += 4 + pair.Value.Length; + } + length += 4; + + byte[] result = new byte[length]; + int offset = 0; + foreach (KeyValuePair pair in pairs) + { + WriteAVPair(result, ref offset, pair.Key, pair.Value); + } + LittleEndianWriter.WriteUInt16(result, ref offset, (ushort)AVPairKey.EOL); + LittleEndianWriter.WriteUInt16(result, ref offset, 0); + + return result; + } + + public static void WriteAVPair(byte[] buffer, ref int offset, AVPairKey key, byte[] value) + { + LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)key); + LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)value.Length); + ByteWriter.WriteBytes(buffer, ref offset, value); + } + + public static KeyValuePairList ReadAVPairSequence(byte[] buffer, int offset) + { + KeyValuePairList result = new KeyValuePairList(); + AVPairKey key = (AVPairKey)LittleEndianConverter.ToUInt16(buffer, offset); + while (key != AVPairKey.EOL) + { + KeyValuePair pair = ReadAVPair(buffer, ref offset); + result.Add(pair); + key = (AVPairKey)LittleEndianConverter.ToUInt16(buffer, offset); + } + + return result; + } + + public static KeyValuePair ReadAVPair(byte[] buffer, ref int offset) + { + AVPairKey key = (AVPairKey)LittleEndianReader.ReadUInt16(buffer, ref offset); + ushort length = LittleEndianReader.ReadUInt16(buffer, ref offset); + byte[] value = ByteReader.ReadBytes(buffer, ref offset, length); + return new KeyValuePair(key, value); + } + } +} diff --git a/SMBLibrary/Authentication/AuthenticateMessage/AuthenticateMessage.cs b/SMBLibrary/Authentication/AuthenticateMessage/AuthenticateMessage.cs new file mode 100644 index 0000000..79e50a9 --- /dev/null +++ b/SMBLibrary/Authentication/AuthenticateMessage/AuthenticateMessage.cs @@ -0,0 +1,94 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.Authentication +{ + /// + /// [MS-NLMP] AUTHENTICATE_MESSAGE (Type 3 Message) + /// + public class AuthenticateMessage + { + public string Signature; // 8 bytes + public MessageTypeName MessageType; + public byte[] LmChallengeResponse; + public byte[] NtChallengeResponse; + public string DomainName; + public string UserName; + public string WorkStation; + public byte[] EncryptedRandomSessionKey; + public NegotiateFlags NegotiateFlags; + public Version Version; + // 16-byte MIC field is omitted for Windows NT / 2000 / XP / Server 2003 + + public AuthenticateMessage() + { + Signature = AuthenticationMessageUtils.ValidSignature; + MessageType = MessageTypeName.Authenticate; + DomainName = String.Empty; + UserName = String.Empty; + WorkStation = String.Empty; + EncryptedRandomSessionKey = new byte[0]; + } + + public AuthenticateMessage(byte[] buffer) + { + Signature = ByteReader.ReadAnsiString(buffer, 0, 8); + MessageType = (MessageTypeName)LittleEndianConverter.ToUInt32(buffer, 8); + LmChallengeResponse = AuthenticationMessageUtils.ReadBufferPointer(buffer, 12); + NtChallengeResponse = AuthenticationMessageUtils.ReadBufferPointer(buffer, 20); + DomainName = AuthenticationMessageUtils.ReadUnicodeStringBufferPointer(buffer, 28); + UserName = AuthenticationMessageUtils.ReadUnicodeStringBufferPointer(buffer, 36); + WorkStation = AuthenticationMessageUtils.ReadUnicodeStringBufferPointer(buffer, 44); + EncryptedRandomSessionKey = AuthenticationMessageUtils.ReadBufferPointer(buffer, 52); + NegotiateFlags = (NegotiateFlags)LittleEndianConverter.ToUInt32(buffer, 60); + if ((NegotiateFlags & NegotiateFlags.NegotiateVersion) > 0) + { + Version = new Version(buffer, 64); + } + } + + public byte[] GetBytes() + { + int fixedLength = 64; + + if ((NegotiateFlags & NegotiateFlags.NegotiateVersion) > 0) + { + fixedLength += 8; + } + + int payloadLength = LmChallengeResponse.Length + NtChallengeResponse.Length + DomainName.Length * 2 + UserName.Length * 2 + WorkStation.Length * 2 + EncryptedRandomSessionKey.Length; + byte[] buffer = new byte[fixedLength + payloadLength]; + ByteWriter.WriteAnsiString(buffer, 0, AuthenticationMessageUtils.ValidSignature, 8); + LittleEndianWriter.WriteUInt32(buffer, 8, (uint)MessageType); + LittleEndianWriter.WriteUInt32(buffer, 60, (uint)NegotiateFlags); + if ((NegotiateFlags & NegotiateFlags.NegotiateVersion) > 0) + { + Version.WriteBytes(buffer, 64); + } + + int offset = fixedLength; + AuthenticationMessageUtils.WriteBufferPointer(buffer, 12, (ushort)LmChallengeResponse.Length, (uint)offset); + ByteWriter.WriteBytes(buffer, ref offset, LmChallengeResponse); + AuthenticationMessageUtils.WriteBufferPointer(buffer, 20, (ushort)NtChallengeResponse.Length, (uint)offset); + ByteWriter.WriteBytes(buffer, ref offset, NtChallengeResponse); + AuthenticationMessageUtils.WriteBufferPointer(buffer, 28, (ushort)(DomainName.Length * 2), (uint)offset); + ByteWriter.WriteUTF16String(buffer, ref offset, DomainName); + AuthenticationMessageUtils.WriteBufferPointer(buffer, 36, (ushort)(UserName.Length * 2), (uint)offset); + ByteWriter.WriteUTF16String(buffer, ref offset, UserName); + AuthenticationMessageUtils.WriteBufferPointer(buffer, 44, (ushort)(WorkStation.Length * 2), (uint)offset); + ByteWriter.WriteUTF16String(buffer, ref offset, WorkStation); + AuthenticationMessageUtils.WriteBufferPointer(buffer, 52, (ushort)EncryptedRandomSessionKey.Length, (uint)offset); + ByteWriter.WriteBytes(buffer, ref offset, EncryptedRandomSessionKey); + + return buffer; + } + } +} diff --git a/SMBLibrary/Authentication/AuthenticateMessage/AuthenticationMessageUtils.cs b/SMBLibrary/Authentication/AuthenticateMessage/AuthenticationMessageUtils.cs new file mode 100644 index 0000000..78b6385 --- /dev/null +++ b/SMBLibrary/Authentication/AuthenticateMessage/AuthenticationMessageUtils.cs @@ -0,0 +1,64 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.Authentication +{ + public class AuthenticationMessageUtils + { + public const string ValidSignature = "NTLMSSP\0"; + + public static string ReadAnsiStringBufferPointer(byte[] buffer, int offset) + { + byte[] bytes = ReadBufferPointer(buffer, offset); + return ASCIIEncoding.Default.GetString(bytes); + } + + public static string ReadUnicodeStringBufferPointer(byte[] buffer, int offset) + { + byte[] bytes = ReadBufferPointer(buffer, offset); + return UnicodeEncoding.Unicode.GetString(bytes); + } + + public static byte[] ReadBufferPointer(byte[] buffer, int offset) + { + ushort length = LittleEndianConverter.ToUInt16(buffer, offset); + ushort maxLength = LittleEndianConverter.ToUInt16(buffer, offset + 2); + uint bufferOffset = LittleEndianConverter.ToUInt32(buffer, offset + 4); + + if (length == 0) + { + return new byte[0]; + } + else + { + return ByteReader.ReadBytes(buffer, (int)bufferOffset, length); + } + } + + public static void WriteBufferPointer(byte[] buffer, int offset, ushort bufferLength, uint bufferOffset) + { + LittleEndianWriter.WriteUInt16(buffer, offset, bufferLength); + LittleEndianWriter.WriteUInt16(buffer, offset + 2, bufferLength); + LittleEndianWriter.WriteUInt32(buffer, offset + 4, bufferOffset); + } + + public static bool IsSignatureValid(byte[] messageBytes) + { + string signature = ByteReader.ReadAnsiString(messageBytes, 0, 8); + return (signature == ValidSignature); + } + + public static MessageTypeName GetMessageType(byte[] messageBytes) + { + return (MessageTypeName)LittleEndianConverter.ToUInt32(messageBytes, 8); + } + } +} diff --git a/SMBLibrary/Authentication/AuthenticateMessage/ChallengeMessage.cs b/SMBLibrary/Authentication/AuthenticateMessage/ChallengeMessage.cs new file mode 100644 index 0000000..31797e3 --- /dev/null +++ b/SMBLibrary/Authentication/AuthenticateMessage/ChallengeMessage.cs @@ -0,0 +1,77 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.Authentication +{ + /// + /// [MS-NLMP] CHALLENGE_MESSAGE (Type 2 Message) + /// + public class ChallengeMessage + { + public string Signature; // 8 bytes + public MessageTypeName MessageType; + public string TargetName; + public NegotiateFlags NegotiateFlags; + public byte[] ServerChallenge; // 8 bytes + // Reserved - 8 bytes + public byte[] TargetInfo; // sequence of AV_PAIR structures + public Version Version; + + public ChallengeMessage() + { + Signature = AuthenticationMessageUtils.ValidSignature; + MessageType = MessageTypeName.Challenge; + } + + public ChallengeMessage(byte[] buffer) + { + Signature = ByteReader.ReadAnsiString(buffer, 0, 8); + MessageType = (MessageTypeName)LittleEndianConverter.ToUInt32(buffer, 8); + TargetName = AuthenticationMessageUtils.ReadUnicodeStringBufferPointer(buffer, 12); + NegotiateFlags = (NegotiateFlags)LittleEndianConverter.ToUInt32(buffer, 20); + ServerChallenge = ByteReader.ReadBytes(buffer, 24, 8); + // Reserved + TargetInfo = AuthenticationMessageUtils.ReadBufferPointer(buffer, 40); + if ((NegotiateFlags & NegotiateFlags.NegotiateVersion) > 0) + { + Version = new Version(buffer, 48); + } + } + + public byte[] GetBytes() + { + int fixedLength = 48; + if ((NegotiateFlags & NegotiateFlags.NegotiateVersion) > 0) + { + fixedLength += 8; + } + + int payloadLength = TargetName.Length * 2 + TargetInfo.Length; + byte[] buffer = new byte[fixedLength + payloadLength]; + ByteWriter.WriteAnsiString(buffer, 0, AuthenticationMessageUtils.ValidSignature, 8); + LittleEndianWriter.WriteUInt32(buffer, 8, (uint)MessageType); + LittleEndianWriter.WriteUInt32(buffer, 20, (uint)NegotiateFlags); + ByteWriter.WriteBytes(buffer, 24, ServerChallenge); + if ((NegotiateFlags & NegotiateFlags.NegotiateVersion) > 0) + { + Version.WriteBytes(buffer, 48); + } + + int offset = fixedLength; + AuthenticationMessageUtils.WriteBufferPointer(buffer, 12, (ushort)(TargetName.Length * 2), (uint)offset); + ByteWriter.WriteUTF16String(buffer, ref offset, TargetName); + AuthenticationMessageUtils.WriteBufferPointer(buffer, 40, (ushort)TargetInfo.Length, (uint)offset); + ByteWriter.WriteBytes(buffer, ref offset, TargetInfo); + + return buffer; + } + } +} diff --git a/SMBLibrary/Authentication/AuthenticateMessage/Enums/AVPairKey.cs b/SMBLibrary/Authentication/AuthenticateMessage/Enums/AVPairKey.cs new file mode 100644 index 0000000..3fede6a --- /dev/null +++ b/SMBLibrary/Authentication/AuthenticateMessage/Enums/AVPairKey.cs @@ -0,0 +1,18 @@ + +namespace SMBLibrary.Authentication +{ + public enum AVPairKey : ushort + { + EOL = 0x0000, + NbComputerName = 0x0001, // Unicode + NbDomainName = 0x0002, // Unicode + DnsComputerName = 0x0003, // Unicode + DnsDomainName = 0x0004, // Unicode + DnsTreeName = 0x0005, // Unicode + Flags = 0x0006, // UInt32 + Timestamp = 0x0006, // Filetime + SingleHost = 0x0008, // platform-specific BLOB + TargetName = 0x0009, // Unicode + ChannelBindings = 0x000A, // MD5 Hash + } +} diff --git a/SMBLibrary/Authentication/AuthenticateMessage/Enums/MessageTypeName.cs b/SMBLibrary/Authentication/AuthenticateMessage/Enums/MessageTypeName.cs new file mode 100644 index 0000000..f75dcae --- /dev/null +++ b/SMBLibrary/Authentication/AuthenticateMessage/Enums/MessageTypeName.cs @@ -0,0 +1,10 @@ + +namespace SMBLibrary.Authentication +{ + public enum MessageTypeName : uint + { + Negotiate = 0x01, + Challenge = 0x02, + Authenticate = 0x03, + } +} diff --git a/SMBLibrary/Authentication/AuthenticateMessage/Enums/NegotiateFlags.cs b/SMBLibrary/Authentication/AuthenticateMessage/Enums/NegotiateFlags.cs new file mode 100644 index 0000000..38cd8bf --- /dev/null +++ b/SMBLibrary/Authentication/AuthenticateMessage/Enums/NegotiateFlags.cs @@ -0,0 +1,48 @@ +using System; + +namespace SMBLibrary.Authentication +{ + [Flags] + public enum NegotiateFlags : uint + { + NegotiateUnicode = 0x01, // NTLMSSP_NEGOTIATE_UNICODE + NegotiateOEM = 0x02, // NTLM_NEGOTIATE_OEM + RequestTarget = 0x04, // NTLMSSP_REQUEST_TARGET + NegotiateSign = 0x10, // NTLMSSP_NEGOTIATE_SIGN + NegotiateSeal = 0x20, // NTLMSSP_NEGOTIATE_SEAL + NegotiateDatagram = 0x40, // NTLMSSP_NEGOTIATE_DATAGRAM + + /// + /// NegotiateLanManagerKey and NegotiateExtendedSecurity are mutually exclusive + /// If both are set then NegotiateLanManagerKey must be ignored + /// + NegotiateLanManagerKey = 0x80, // NTLMSSP_NEGOTIATE_LM_KEY + NegotiateNTLMKey = 0x200, // NTLMSSP_NEGOTIATE_NTLM + //NegotiateNTOnly = 0x400, // Unused, must be clear + + /// + /// If set, the connection SHOULD be anonymous + /// + NegotiateAnonymous = 0x800, + + NegotiateOEMDomainSupplied = 0x1000, // NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED + NegotiateOEMWorkstationSupplied = 0x2000, // NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED + NegotiateAlwaysSign = 0x8000, // NTLMSSP_NEGOTIATE_ALWAYS_SIGN + NegotiateTargetTypeDomain = 0x10000, // NTLMSSP_TARGET_TYPE_DOMAIN + NegotiateTargetTypeServer = 0x20000, // NTLMSSP_TARGET_TYPE_SERVER + NegotiateTargetTypeShare = 0x40000, // Unused, must be clear + + /// + /// NegotiateLanManagerKey and NegotiateExtendedSecurity are mutually exclusive + /// If both are set then NegotiateLanManagerKey must be ignored + /// + NegotiateExtendedSecurity = 0x80000, // NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY + NegotiateIdentify = 0x100000, // NTLMSSP_NEGOTIATE_IDENTIFY + RequestNonNTSession = 0x400000, // NTLMSSP_REQUEST_NON_NT_SESSION_KEY + NegotiateTargetInfo = 0x800000, // NTLMSSP_NEGOTIATE_TARGET_INFO + NegotiateVersion = 0x2000000, // NTLMSSP_NEGOTIATE_VERSION + Negotiate128 = 0x20000000, // NTLMSSP_NEGOTIATE_128 + NegotiateKeyExchange = 0x40000000, // NTLMSSP_NEGOTIATE_KEY_EXCH + Negotiate56 = 0x80000000, // NTLMSSP_NEGOTIATE_56 + } +} diff --git a/SMBLibrary/Authentication/AuthenticateMessage/NTLMv2ClientChallenge.cs b/SMBLibrary/Authentication/AuthenticateMessage/NTLMv2ClientChallenge.cs new file mode 100644 index 0000000..8f4daa2 --- /dev/null +++ b/SMBLibrary/Authentication/AuthenticateMessage/NTLMv2ClientChallenge.cs @@ -0,0 +1,79 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.Authentication +{ + /// + /// NTLMv2_CLIENT_CHALLENGE + /// + public class NTLMv2ClientChallengeStructure + { + public static readonly DateTime EpochTime = DateTime.FromFileTimeUtc(0); + + public byte ResponseVersion; + public byte ResponseVersionHigh; + public DateTime Time; + public byte[] ClientChallenge; + public KeyValuePairList AVPairs; + + public NTLMv2ClientChallengeStructure() + { + } + + public NTLMv2ClientChallengeStructure(DateTime time, byte[] clientChallenge, string domainName, string computerName) + { + ResponseVersion = 1; + ResponseVersionHigh = 1; + Time = time; + ClientChallenge = clientChallenge; + AVPairs = new KeyValuePairList(); + AVPairs.Add(AVPairKey.NbDomainName, UnicodeEncoding.Unicode.GetBytes(domainName)); + AVPairs.Add(AVPairKey.NbComputerName, UnicodeEncoding.Unicode.GetBytes(computerName)); + } + + public NTLMv2ClientChallengeStructure(byte[] buffer) : this(buffer, 0) + { + } + + public NTLMv2ClientChallengeStructure(byte[] buffer, int offset) + { + ResponseVersion = ByteReader.ReadByte(buffer, offset + 0); + ResponseVersionHigh = ByteReader.ReadByte(buffer, offset + 1); + long temp = LittleEndianConverter.ToInt64(buffer, offset + 8); + Time = DateTime.FromFileTimeUtc(temp); + ClientChallenge = ByteReader.ReadBytes(buffer, offset + 16, 8); + AVPairs = AVPairUtils.ReadAVPairSequence(buffer, offset + 28); + } + + public byte[] GetBytes() + { + byte[] sequenceBytes = AVPairUtils.GetAVPairSequenceBytes(AVPairs); + byte[] timeBytes = LittleEndianConverter.GetBytes((ulong)Time.ToFileTimeUtc()); + + byte[] buffer = new byte[28 + sequenceBytes.Length]; + ByteWriter.WriteByte(buffer, 0, ResponseVersion); + ByteWriter.WriteByte(buffer, 1, ResponseVersionHigh); + ByteWriter.WriteBytes(buffer, 8, timeBytes); + ByteWriter.WriteBytes(buffer, 16, ClientChallenge, 8); + ByteWriter.WriteBytes(buffer, 28, sequenceBytes); + return buffer; + } + + /// + /// [MS-NLMP] Page 60, Response key calculation algorithm: + /// To create 'temp', 4 zero bytes will be appended to NTLMv2_CLIENT_CHALLENGE + /// + public byte[] GetBytesPadded() + { + return ByteUtils.Concatenate(GetBytes(), new byte[4]); + } + } +} diff --git a/SMBLibrary/Authentication/AuthenticateMessage/NegotiateMessage.cs b/SMBLibrary/Authentication/AuthenticateMessage/NegotiateMessage.cs new file mode 100644 index 0000000..10f58ad --- /dev/null +++ b/SMBLibrary/Authentication/AuthenticateMessage/NegotiateMessage.cs @@ -0,0 +1,74 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.Authentication +{ + /// + /// [MS-NLMP] NEGOTIATE_MESSAGE (Type 1 Message) + /// + public class NegotiateMessage + { + public string Signature; // 8 bytes + public MessageTypeName MessageType; + public NegotiateFlags NegotiateFlags; + public string DomainName; + public string Workstation; + public Version Version; + + public NegotiateMessage() + { + Signature = AuthenticationMessageUtils.ValidSignature; + MessageType = MessageTypeName.Negotiate; + DomainName = String.Empty; + Workstation = String.Empty; + } + + public NegotiateMessage(byte[] buffer) + { + Signature = ByteReader.ReadAnsiString(buffer, 0, 8); + MessageType = (MessageTypeName)LittleEndianConverter.ToUInt32(buffer, 8); + NegotiateFlags = (NegotiateFlags)LittleEndianConverter.ToUInt32(buffer, 12); + DomainName = AuthenticationMessageUtils.ReadAnsiStringBufferPointer(buffer, 16); + Workstation = AuthenticationMessageUtils.ReadAnsiStringBufferPointer(buffer, 24); + if ((NegotiateFlags & NegotiateFlags.NegotiateVersion) > 0) + { + Version = new Version(buffer, 32); + } + } + + public byte[] GetBytes() + { + int fixedLength = 32; + if ((NegotiateFlags & NegotiateFlags.NegotiateVersion) > 0) + { + fixedLength += 8; + } + int payloadLength = DomainName.Length * 2 + Workstation.Length * 2; + byte[] buffer = new byte[fixedLength + payloadLength]; + ByteWriter.WriteAnsiString(buffer, 0, AuthenticationMessageUtils.ValidSignature, 8); + LittleEndianWriter.WriteUInt32(buffer, 8, (uint)MessageType); + LittleEndianWriter.WriteUInt32(buffer, 12, (uint)NegotiateFlags); + + if ((NegotiateFlags & NegotiateFlags.NegotiateVersion) > 0) + { + Version.WriteBytes(buffer, 32); + } + + int offset = fixedLength; + AuthenticationMessageUtils.WriteBufferPointer(buffer, 16, (ushort)(DomainName.Length * 2), (uint)offset); + ByteWriter.WriteUTF16String(buffer, ref offset, DomainName); + AuthenticationMessageUtils.WriteBufferPointer(buffer, 16, (ushort)(Workstation.Length * 2), (uint)offset); + ByteWriter.WriteUTF16String(buffer, ref offset, Workstation); + + return buffer; + } + } +} diff --git a/SMBLibrary/Authentication/AuthenticateMessage/Version.cs b/SMBLibrary/Authentication/AuthenticateMessage/Version.cs new file mode 100644 index 0000000..c4e765e --- /dev/null +++ b/SMBLibrary/Authentication/AuthenticateMessage/Version.cs @@ -0,0 +1,64 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.Authentication +{ + public class Version + { + public const byte NTLMSSP_REVISION_W2K3 = 0x0F; + + public byte ProductMajorVersion; + public byte ProductMinorVersion; + public ushort ProductBuild; + // Reserved - 3 bytes + public byte NTLMRevisionCurrent; + + public Version(byte majorVersion, byte minorVersion, ushort build, byte ntlmRevisionCurrent) + { + ProductMajorVersion = majorVersion; + ProductMinorVersion = minorVersion; + ProductBuild = build; + NTLMRevisionCurrent = ntlmRevisionCurrent; + } + + public Version(byte[] buffer, int offset) + { + ProductMajorVersion = ByteReader.ReadByte(buffer, offset + 0); + ProductMinorVersion = ByteReader.ReadByte(buffer, offset + 1); + ProductBuild = LittleEndianConverter.ToUInt16(buffer, offset + 2); + NTLMRevisionCurrent = ByteReader.ReadByte(buffer, offset + 7); + } + + public void WriteBytes(byte[] buffer, int offset) + { + ByteWriter.WriteByte(buffer, offset + 0, ProductMajorVersion); + ByteWriter.WriteByte(buffer, offset + 1, ProductMinorVersion); + LittleEndianWriter.WriteUInt16(buffer, offset + 2, ProductBuild); + ByteWriter.WriteByte(buffer, offset + 7, NTLMRevisionCurrent); + } + + public static Version WindowsXP + { + get + { + return new Version(5, 1, 2600, NTLMSSP_REVISION_W2K3); + } + } + + public static Version Server2003 + { + get + { + return new Version(5, 2, 3790, NTLMSSP_REVISION_W2K3); + } + } + } +} diff --git a/SMBLibrary/Authentication/MD4.cs b/SMBLibrary/Authentication/MD4.cs new file mode 100644 index 0000000..64d8ec8 --- /dev/null +++ b/SMBLibrary/Authentication/MD4.cs @@ -0,0 +1,418 @@ +/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD4 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD4 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + + -------------------------------------------------------------- + + Ported from Norbert Hranitzky's (norbert.hranitzky@mchp.siemens.de) + Java version by Oren Novotny (osn@po.cwru.edu) + + -------------------------------------------------------------- + Adapted to C# 2.0 By Tal Aloni + -------------------------------------------------------------- + + +*/ + +using System.Text; + +namespace System.Security.Cryptography +{ + using System; + + + /// + /// Implements the MD4 message digest algorithm in C# + /// + /// + ///

+ /// References: + ///

    + ///
  1. Ronald L. Rivest, + /// " + /// The MD4 Message-Digest Algorithm", + /// IETF RFC-1320 (informational). + ///
  2. + ///
+ ///

+ ///
+ internal class MD4 + { + // MD4 specific object variables + //----------------------------------------------------------------------- + + /// + /// The size in bytes of the input block to the transformation algorithm + /// + private const int BLOCK_LENGTH = 64; // = 512 / 8 + + /// + /// 512-bit work buffer = 16 x 32-bit words + /// + private readonly uint[] X = new uint[16]; + + /// + /// 4 32-bit words (interim result) + /// + private readonly uint[] context = new uint[4]; + + /// + /// 512-bit input buffer = 16 x 32-bit words holds until it reaches 512 bits + /// + private byte[] buffer = new byte[BLOCK_LENGTH]; + + /// + /// Number of bytes procesed so far mod. 2 power of 64. + /// + private long count; + + + // Constructors + //------------------------------------------------------------------------ + public MD4() + { + EngineReset(); + } + + /// + /// This constructor is here to implement the clonability of this class + /// + /// + private MD4(MD4 md) + : this() + { + //this(); + context = (uint[])md.context.Clone(); + buffer = (byte[])md.buffer.Clone(); + count = md.count; + } + + // Clonable method implementation + //------------------------------------------------------------------------- + public object Clone() + { + return new MD4(this); + } + + // JCE methods + //------------------------------------------------------------------------- + + /// + /// Resets this object disregarding any temporary data present at the + /// time of the invocation of this call. + /// + private void EngineReset() + { + // initial values of MD4 i.e. A, B, C, D + // as per rfc-1320; they are low-order byte first + context[0] = 0x67452301; + context[1] = 0xEFCDAB89; + context[2] = 0x98BADCFE; + context[3] = 0x10325476; + count = 0L; + for (int i = 0; i < BLOCK_LENGTH; i++) + buffer[i] = 0; + } + + + /// + /// Continues an MD4 message digest using the input byte + /// + /// byte to input + private void EngineUpdate(byte b) + { + // compute number of bytes still unhashed; ie. present in buffer + int i = (int)(count % BLOCK_LENGTH); + count++; // update number of bytes + buffer[i] = b; + if (i == BLOCK_LENGTH - 1) + Transform(ref buffer, 0); + } + + /// + /// MD4 block update operation + /// + /// + /// Continues an MD4 message digest operation by filling the buffer, + /// transform(ing) data in 512-bit message block(s), updating the variables + /// context and count, and leaving (buffering) the remaining bytes in buffer + /// for the next update or finish. + /// + /// input block + /// start of meaningful bytes in input + /// count of bytes in input blcok to consider + private void EngineUpdate(byte[] input, int offset, int len) + { + // make sure we don't exceed input's allocated size/length + if (offset < 0 || len < 0 || (long)offset + len > input.Length) + throw new ArgumentOutOfRangeException(); + + // compute number of bytes still unhashed; ie. present in buffer + int bufferNdx = (int)(count % BLOCK_LENGTH); + count += len; // update number of bytes + int partLen = BLOCK_LENGTH - bufferNdx; + int i = 0; + if (len >= partLen) + { + Array.Copy(input, offset + i, buffer, bufferNdx, partLen); + + Transform(ref buffer, 0); + + for (i = partLen; i + BLOCK_LENGTH - 1 < len; i += BLOCK_LENGTH) + Transform(ref input, offset + i); + bufferNdx = 0; + } + // buffer remaining input + if (i < len) + Array.Copy(input, offset + i, buffer, bufferNdx, len - i); + } + + /// + /// Completes the hash computation by performing final operations such + /// as padding. At the return of this engineDigest, the MD engine is + /// reset. + /// + /// the array of bytes for the resulting hash value. + private byte[] EngineDigest() + { + // pad output to 56 mod 64; as RFC1320 puts it: congruent to 448 mod 512 + int bufferNdx = (int)(count % BLOCK_LENGTH); + int padLen = (bufferNdx < 56) ? (56 - bufferNdx) : (120 - bufferNdx); + + // padding is always binary 1 followed by binary 0's + byte[] tail = new byte[padLen + 8]; + tail[0] = 0x80; + + // append length before final transform + // save number of bits, casting the long to an array of 8 bytes + // save low-order byte first. + for (int i = 0; i < 8; i++) + tail[padLen + i] = (byte)((count * 8) >> (8 * i)); + + EngineUpdate(tail, 0, tail.Length); + + byte[] result = new byte[16]; + // cast this MD4's context (array of 4 uints) into an array of 16 bytes. + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + result[i * 4 + j] = (byte)(context[i] >> (8 * j)); + + // reset the engine + EngineReset(); + return result; + } + + /// + /// Returns a byte hash from a string + /// + /// string to hash + /// byte-array that contains the hash + public byte[] GetByteHashFromString(string s) + { + byte[] b = Encoding.UTF8.GetBytes(s); + MD4 md4 = new MD4(); + + md4.EngineUpdate(b, 0, b.Length); + + return md4.EngineDigest(); + } + + /// + /// Returns a binary hash from an input byte array + /// + /// byte-array to hash + /// binary hash of input + public byte[] GetByteHashFromBytes(byte[] b) + { + MD4 md4 = new MD4(); + + md4.EngineUpdate(b, 0, b.Length); + + return md4.EngineDigest(); + } + + /// + /// Returns a string that contains the hexadecimal hash + /// + /// byte-array to input + /// String that contains the hex of the hash + public string GetHexHashFromBytes(byte[] b) + { + byte[] e = GetByteHashFromBytes(b); + return BytesToHex(e, e.Length); + } + + /// + /// Returns a byte hash from the input byte + /// + /// byte to hash + /// binary hash of the input byte + public byte[] GetByteHashFromByte(byte b) + { + MD4 md4 = new MD4(); + + md4.EngineUpdate(b); + + return md4.EngineDigest(); + } + + /// + /// Returns a string that contains the hexadecimal hash + /// + /// byte to hash + /// String that contains the hex of the hash + public string GetHexHashFromByte(byte b) + { + byte[] e = GetByteHashFromByte(b); + return BytesToHex(e, e.Length); + } + + /// + /// Returns a string that contains the hexadecimal hash + /// + /// string to hash + /// String that contains the hex of the hash + public string GetHexHashFromString(string s) + { + byte[] b = GetByteHashFromString(s); + return BytesToHex(b, b.Length); + } + + private static string BytesToHex(byte[] a, int len) + { + string temp = BitConverter.ToString(a); + + // We need to remove the dashes that come from the BitConverter + StringBuilder sb = new StringBuilder((len - 2) / 2); // This should be the final size + + for (int i = 0; i < temp.Length; i++) + if (temp[i] != '-') + sb.Append(temp[i]); + + return sb.ToString(); + } + + // own methods + //----------------------------------------------------------------------------------- + + /// + /// MD4 basic transformation + /// + /// + /// Transforms context based on 512 bits from input block starting + /// from the offset'th byte. + /// + /// input sub-array + /// starting position of sub-array + private void Transform(ref byte[] block, int offset) + { + // decodes 64 bytes from input block into an array of 16 32-bit + // entities. Use A as a temp var. + for (int i = 0; i < 16; i++) + X[i] = ((uint)block[offset++] & 0xFF) | + (((uint)block[offset++] & 0xFF) << 8) | + (((uint)block[offset++] & 0xFF) << 16) | + (((uint)block[offset++] & 0xFF) << 24); + + + uint A = context[0]; + uint B = context[1]; + uint C = context[2]; + uint D = context[3]; + + A = FF(A, B, C, D, X[0], 3); + D = FF(D, A, B, C, X[1], 7); + C = FF(C, D, A, B, X[2], 11); + B = FF(B, C, D, A, X[3], 19); + A = FF(A, B, C, D, X[4], 3); + D = FF(D, A, B, C, X[5], 7); + C = FF(C, D, A, B, X[6], 11); + B = FF(B, C, D, A, X[7], 19); + A = FF(A, B, C, D, X[8], 3); + D = FF(D, A, B, C, X[9], 7); + C = FF(C, D, A, B, X[10], 11); + B = FF(B, C, D, A, X[11], 19); + A = FF(A, B, C, D, X[12], 3); + D = FF(D, A, B, C, X[13], 7); + C = FF(C, D, A, B, X[14], 11); + B = FF(B, C, D, A, X[15], 19); + + A = GG(A, B, C, D, X[0], 3); + D = GG(D, A, B, C, X[4], 5); + C = GG(C, D, A, B, X[8], 9); + B = GG(B, C, D, A, X[12], 13); + A = GG(A, B, C, D, X[1], 3); + D = GG(D, A, B, C, X[5], 5); + C = GG(C, D, A, B, X[9], 9); + B = GG(B, C, D, A, X[13], 13); + A = GG(A, B, C, D, X[2], 3); + D = GG(D, A, B, C, X[6], 5); + C = GG(C, D, A, B, X[10], 9); + B = GG(B, C, D, A, X[14], 13); + A = GG(A, B, C, D, X[3], 3); + D = GG(D, A, B, C, X[7], 5); + C = GG(C, D, A, B, X[11], 9); + B = GG(B, C, D, A, X[15], 13); + + A = HH(A, B, C, D, X[0], 3); + D = HH(D, A, B, C, X[8], 9); + C = HH(C, D, A, B, X[4], 11); + B = HH(B, C, D, A, X[12], 15); + A = HH(A, B, C, D, X[2], 3); + D = HH(D, A, B, C, X[10], 9); + C = HH(C, D, A, B, X[6], 11); + B = HH(B, C, D, A, X[14], 15); + A = HH(A, B, C, D, X[1], 3); + D = HH(D, A, B, C, X[9], 9); + C = HH(C, D, A, B, X[5], 11); + B = HH(B, C, D, A, X[13], 15); + A = HH(A, B, C, D, X[3], 3); + D = HH(D, A, B, C, X[11], 9); + C = HH(C, D, A, B, X[7], 11); + B = HH(B, C, D, A, X[15], 15); + + context[0] += A; + context[1] += B; + context[2] += C; + context[3] += D; + } + + // The basic MD4 atomic functions. + + private uint FF(uint a, uint b, uint c, uint d, uint x, int s) + { + uint t = a + ((b & c) | (~b & d)) + x; + return t << s | t >> (32 - s); + } + + private uint GG(uint a, uint b, uint c, uint d, uint x, int s) + { + uint t = a + ((b & (c | d)) | (c & d)) + x + 0x5A827999; + return t << s | t >> (32 - s); + } + + private uint HH(uint a, uint b, uint c, uint d, uint x, int s) + { + uint t = a + (b ^ c ^ d) + x + 0x6ED9EBA1; + return t << s | t >> (32 - s); + } + } + + // class MD4 +} + +// namespace MD4Hash \ No newline at end of file diff --git a/SMBLibrary/Authentication/NTAuthentication.cs b/SMBLibrary/Authentication/NTAuthentication.cs new file mode 100644 index 0000000..50d5441 --- /dev/null +++ b/SMBLibrary/Authentication/NTAuthentication.cs @@ -0,0 +1,220 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using Utilities; + +namespace SMBLibrary.Authentication +{ + public class NTAuthentication + { + public static byte[] ComputeLMv1Response(byte[] challenge, string password) + { + byte[] hash = LMOWFv1(password); + return DesLongEncrypt(hash, challenge); + } + + public static byte[] ComputeNTLMv1Response(byte[] challenge, string password) + { + byte[] hash = NTOWFv1(password); + return DesLongEncrypt(hash, challenge); + } + + public static byte[] ComputeNTLMv1ExtendedSecurityResponse(byte[] serverChallenge, byte[] clientChallenge, string password) + { + byte[] passwordHash = NTOWFv1(password); + byte[] challengeHash = MD5.Create().ComputeHash(ByteUtils.Concatenate(serverChallenge, clientChallenge)); + byte[] challengeHashShort = new byte[8]; + Array.Copy(challengeHash, 0, challengeHashShort, 0, 8); + return DesLongEncrypt(passwordHash, challengeHashShort); + } + + public static byte[] ComputeLMv2Response(byte[] serverChallenge, byte[] clientChallenge, string password, string user, string domain) + { + byte[] key = LMOWFv2(password, user, domain); + byte[] bytes = ByteUtils.Concatenate(serverChallenge, clientChallenge); + HMACMD5 hmac = new HMACMD5(key); + byte[] hash = hmac.ComputeHash(bytes, 0, bytes.Length); + + return ByteUtils.Concatenate(hash, clientChallenge); + } + + /// + /// [MS-NLMP] page 60 + /// 'temp' is the same as ClientChallengeStructure with 4 zero bytes padding + /// + public static byte[] ComputeNTLMv2Response(byte[] serverChallenge, byte[] clientChallengeStructurePadded, string password, string user, string domain) + { + byte[] key = NTOWFv2(password, user, domain); + byte[] temp = clientChallengeStructurePadded; + + HMACMD5 hmac = new HMACMD5(key); + byte[] _NTProof = hmac.ComputeHash(ByteUtils.Concatenate(serverChallenge, temp), 0, serverChallenge.Length + temp.Length); + + return ByteUtils.Concatenate(_NTProof, temp); + } + + /// + /// NTLMv2_CLIENT_CHALLENGE + /// + [Obsolete] + public static byte[] GetNTLMv2ClientChallengeStructure(byte[] clientChallenge, byte responseVersion, byte responseVersionHigh, byte[] time, byte[] serverAVPair) + { + byte[] temp = new byte[28 + serverAVPair.Length]; + temp[0] = responseVersion; + temp[1] = responseVersionHigh; + Array.Copy(time, 0, temp, 8, 8); + Array.Copy(clientChallenge, 0, temp, 16, 8); + Array.Copy(serverAVPair, 0, temp, 28, serverAVPair.Length); + + return temp; + } + + public static byte[] DesEncrypt(byte[] key, byte[] plainText) + { + return DesEncrypt(key, plainText, 0, plainText.Length); + } + + public static byte[] DesEncrypt(byte[] key, byte[] plainText, int inputOffset, int inputCount) + { + ICryptoTransform encryptor = CreateWeakDesEncryptor(CipherMode.ECB, key, new byte[key.Length]); + byte[] result = new byte[inputCount]; + encryptor.TransformBlock(plainText, inputOffset, inputCount, result, 0); + return result; + } + + public static ICryptoTransform CreateWeakDesEncryptor(CipherMode mode, byte[] rgbKey, byte[] rgbIV) + { + DES des = DES.Create(); + des.Mode = mode; + DESCryptoServiceProvider sm = des as DESCryptoServiceProvider; + MethodInfo mi = sm.GetType().GetMethod("_NewEncryptor", BindingFlags.NonPublic | BindingFlags.Instance); + object[] Par = { rgbKey, mode, rgbIV, sm.FeedbackSize, 0 }; + ICryptoTransform trans = mi.Invoke(sm, Par) as ICryptoTransform; + return trans; + } + + /// + /// DESL() + /// + public static byte[] DesLongEncrypt(byte[] key, byte[] plainText) + { + if (key.Length != 16) + { + throw new ArgumentException("Invalid key length"); + } + + if (plainText.Length != 8) + { + throw new ArgumentException("Invalid plain-text length"); + } + byte[] padded = new byte[21]; + Array.Copy(key, padded, key.Length); + + byte[] k1 = new byte[7]; + byte[] k2 = new byte[7]; + byte[] k3 = new byte[7]; + Array.Copy(padded, 0, k1, 0, 7); + Array.Copy(padded, 7, k2, 0, 7); + Array.Copy(padded, 14, k3, 0, 7); + + byte[] r1 = DesEncrypt(ExtendDESKey(k1), plainText); + byte[] r2 = DesEncrypt(ExtendDESKey(k2), plainText); + byte[] r3 = DesEncrypt(ExtendDESKey(k3), plainText); + + byte[] result = new byte[24]; + Array.Copy(r1, 0, result, 0, 8); + Array.Copy(r2, 0, result, 8, 8); + Array.Copy(r3, 0, result, 16, 8); + + return result; + } + + public static Encoding GetOEMEncoding() + { + return Encoding.GetEncoding(CultureInfo.CurrentCulture.TextInfo.OEMCodePage); + } + + /// + /// LM Hash + /// + public static byte[] LMOWFv1(string password) + { + byte[] plainText = ASCIIEncoding.ASCII.GetBytes("KGS!@#$%"); + byte[] passwordBytes = GetOEMEncoding().GetBytes(password.ToUpper()); + byte[] key = new byte[14]; + Array.Copy(passwordBytes, key, Math.Min(passwordBytes.Length, 14)); + + byte[] k1 = new byte[7]; + byte[] k2 = new byte[7]; + Array.Copy(key, 0, k1, 0, 7); + Array.Copy(key, 7, k2, 0, 7); + + byte[] part1 = DesEncrypt(ExtendDESKey(k1), plainText); + byte[] part2 = DesEncrypt(ExtendDESKey(k2), plainText); + + return ByteUtils.Concatenate(part1, part2); + } + + /// + /// NTLM hash (NT hash) + /// + public static byte[] NTOWFv1(string password) + { + byte[] passwordBytes = UnicodeEncoding.Unicode.GetBytes(password); + return new MD4().GetByteHashFromBytes(passwordBytes); + } + + /// + /// LMOWFv2 is identical to NTOWFv2 + /// + public static byte[] LMOWFv2(string password, string user, string domain) + { + return NTOWFv2(password, user, domain); + } + + public static byte[] NTOWFv2(string password, string user, string domain) + { + byte[] passwordBytes = UnicodeEncoding.Unicode.GetBytes(password); + byte[] key = new MD4().GetByteHashFromBytes(passwordBytes); + string text = user.ToUpper() + domain; + byte[] bytes = UnicodeEncoding.Unicode.GetBytes(text); + HMACMD5 hmac = new HMACMD5(key); + return hmac.ComputeHash(bytes, 0, bytes.Length); + } + + /// + /// Extends a 7-byte key into an 8-byte key. + /// Note: The DES key ostensibly consists of 64 bits, however, only 56 of these are actually used by the algorithm. + /// Eight bits are used solely for checking parity, and are thereafter discarded + /// + private static byte[] ExtendDESKey(byte[] key) + { + byte[] result = new byte[8]; + int i; + + result[0] = (byte)((key[0] >> 1) & 0xff); + result[1] = (byte)((((key[0] & 0x01) << 6) | (((key[1] & 0xff) >> 2) & 0xff)) & 0xff); + result[2] = (byte)((((key[1] & 0x03) << 5) | (((key[2] & 0xff) >> 3) & 0xff)) & 0xff); + result[3] = (byte)((((key[2] & 0x07) << 4) | (((key[3] & 0xff) >> 4) & 0xff)) & 0xff); + result[4] = (byte)((((key[3] & 0x0F) << 3) | (((key[4] & 0xff) >> 5) & 0xff)) & 0xff); + result[5] = (byte)((((key[4] & 0x1F) << 2) | (((key[5] & 0xff) >> 6) & 0xff)) & 0xff); + result[6] = (byte)((((key[5] & 0x3F) << 1) | (((key[6] & 0xff) >> 7) & 0xff)) & 0xff); + result[7] = (byte)(key[6] & 0x7F); + for (i = 0; i < 8; i++) + { + result[i] = (byte)(result[i] << 1); + } + + return result; + } + } +} diff --git a/SMBLibrary/Client/SMBClient.cs b/SMBLibrary/Client/SMBClient.cs new file mode 100644 index 0000000..c1da15a --- /dev/null +++ b/SMBLibrary/Client/SMBClient.cs @@ -0,0 +1,69 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Text; +using SMBLibrary.NetBios; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Client +{ + public class SMBClient + { + public const int NetBiosOverTCPPort = 139; + public const int DirectTCPPort = 445; + public const string NTLanManagerDialect = "NT LM 0.12"; + + public SMBClient(IPAddress serverAddress, SMBTransportType transport) + { + NegotiateRequest request = new NegotiateRequest(); + request.Dialects.Add(NTLanManagerDialect); + + Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + if (transport == SMBTransportType.DirectTCPTransport) + { + serverSocket.Connect(serverAddress, DirectTCPPort); + } + else + { + serverSocket.Connect(serverAddress, NetBiosOverTCPPort); + } + TrySendMessage(serverSocket, request); + } + + public static void TrySendMessage(Socket serverSocket, SMBCommand request) + { + SMBMessage message = new SMBMessage(); + message.Commands.Add(request); + TrySendMessage(serverSocket, message); + } + + public static void TrySendMessage(Socket serverSocket, SMBMessage message) + { + SessionMessagePacket packet = new SessionMessagePacket(); + packet.Trailer = message.GetBytes(); + TrySendPacket(serverSocket, packet); + } + + public static void TrySendPacket(Socket serverSocket, SessionPacket response) + { + try + { + serverSocket.Send(response.GetBytes()); + } + catch (SocketException) + { + } + catch (ObjectDisposedException) + { + } + } + } +} diff --git a/SMBLibrary/EnumStructures/AccessMask.cs b/SMBLibrary/EnumStructures/AccessMask.cs new file mode 100644 index 0000000..8d2f310 --- /dev/null +++ b/SMBLibrary/EnumStructures/AccessMask.cs @@ -0,0 +1,101 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary +{ + /// + /// [MS-SMB] 2.2.1.4.1 - File_Pipe_Printer_Access_Mask + /// + [Flags] + public enum FileAccessMask : uint + { + FILE_READ_DATA = 0x00000001, + FILE_WRITE_DATA = 0x00000002, + FILE_APPEND_DATA = 0x00000004, + FILE_READ_EA = 0x00000008, + FILE_WRITE_EA = 0x00000010, + FILE_EXECUTE = 0x00000020, + FILE_READ_ATTRIBUTES = 0x00000080, + FILE_WRITE_ATTRIBUTES = 0x00000100, + DELETE = 0x00010000, + READ_CONTROL = 0x00020000, + WRITE_DAC = 0x00040000, + WRITE_OWNER = 0x00080000, + SYNCHRONIZE = 0x00100000, + ACCESS_SYSTEM_SECURITY = 0x01000000, + MAXIMUM_ALLOWED = 0x02000000, + GENERIC_ALL = 0x20000000, + GENERIC_EXECUTE = 0x20000000, + GENERIC_WRITE = 0x40000000, + GENERIC_READ = 0x80000000, + } + + /// + /// [MS-SMB] 2.2.1.4.2 - Directory_Access_Mask + /// + [Flags] + public enum DirectoryAccessMask : uint + { + FILE_LIST_DIRECTORY = 0x00000001, + FILE_ADD_FILE = 0x00000002, + FILE_ADD_SUBDIRECTORY = 0x00000004, + FILE_READ_EA = 0x00000008, + FILE_WRITE_EA = 0x00000010, + FILE_TRAVERSE = 0x00000020, + FILE_DELETE_CHILD = 0x00000040, + FILE_READ_ATTRIBUTES = 0x00000080, + FILE_WRITE_ATTRIBUTES = 0x00000100, + DELETE = 0x00010000, + READ_CONTROL = 0x00020000, + WRITE_DAC = 0x00040000, + WRITE_OWNER = 0x00080000, + SYNCHRONIZE = 0x00100000, + ACCESS_SYSTEM_SECURITY = 0x01000000, + MAXIMUM_ALLOWED = 0x02000000, + GENERIC_ALL = 0x20000000, + GENERIC_EXECUTE = 0x20000000, + GENERIC_WRITE = 0x40000000, + GENERIC_READ = 0x80000000, + } + + /// + /// [MS-DTYP] 2.4.3 - ACCESS_MASK + /// + public struct AccessMask // uint + { + public FileAccessMask File; + public FileAccessMask Directory; + + public AccessMask(byte[] buffer, ref int offset) : this(buffer, offset) + { + offset += 4; + } + + public AccessMask(byte[] buffer, int offset) + { + uint value = LittleEndianConverter.ToUInt32(buffer, offset); + File = (FileAccessMask)value; + Directory = (FileAccessMask)value; + } + + public void WriteBytes(byte[] buffer, int offset) + { + uint value = (uint)(this.File | this.Directory); + LittleEndianWriter.WriteUInt32(buffer, offset, value); + } + + public void WriteBytes(byte[] buffer, ref int offset) + { + WriteBytes(buffer, offset); + offset += 4; + } + } +} diff --git a/SMBLibrary/Enums/NTStatus.cs b/SMBLibrary/Enums/NTStatus.cs new file mode 100644 index 0000000..69d887e --- /dev/null +++ b/SMBLibrary/Enums/NTStatus.cs @@ -0,0 +1,36 @@ + +namespace SMBLibrary +{ + public enum NTStatus : uint + { + STATUS_SUCCESS = 0x00000000, + STATUS_NOT_IMPLEMENTED = 0xC0000002, + STATUS_INVALID_HANDLE = 0xC0000008, + STATUS_INVALID_PARAMETER = 0xC000000D, + STATUS_NO_SUCH_DEVICE = 0xC000000E, + STATUS_NO_SUCH_FILE = 0xC000000F, + STATUS_MORE_PROCESSING_REQUIRED = 0xC0000016, + STATUS_ACCESS_DENIED = 0xC0000022, // The user is not authorized to access the resource. + STATUS_OBJECT_NAME_INVALID = 0xC0000033, + STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034, + STATUS_OBJECT_NAME_COLLISION = 0xC0000035, // The file already exists + STATUS_OBJECT_PATH_INVALID = 0xC0000039, + STATUS_OBJECT_PATH_NOT_FOUND = 0xC000003A, // The share path does not reference a valid resource. + STATUS_OBJECT_PATH_SYNTAX_BAD = 0xC000003B, + STATUS_DATA_ERROR = 0xC000003E, // IO error + STATUS_SHARING_VIOLATION = 0xC0000043, + STATUS_FILE_LOCK_CONFLICT = 0xC0000054, + STATUS_LOGON_FAILURE = 0xC000006D, // Authentication failure. + STATUS_ACCOUNT_RESTRICTION = 0xC000006E, // The user has an empty password, which is not allowed + STATUS_DISK_FULL = 0xC000007F, + STATUS_MEDIA_WRITE_PROTECTED = 0xC00000A2, + STATUS_FILE_IS_A_DIRECTORY = 0xC00000BA, + STATUS_CANNOT_DELETE = 0xC0000121, + + STATUS_INVALID_SMB = 0x00010002, // CIFS/SMB1: A corrupt or invalid SMB request was received + STATUS_SMB_BAD_COMMAND = 0x00160002, // CIFS/SMB1: An unknown SMB command code was received by the server + STATUS_SMB_BAD_FID = 0x00060001, // CIFS/SMB1 + STATUS_SMB_BAD_TID = 0x00050002, // CIFS/SMB1 + STATUS_OS2_NO_MORE_SIDS = 0x00710001, // CIFS/SMB1 + } +} diff --git a/SMBLibrary/Enums/SMBTransportType.cs b/SMBLibrary/Enums/SMBTransportType.cs new file mode 100644 index 0000000..00f0210 --- /dev/null +++ b/SMBLibrary/Enums/SMBTransportType.cs @@ -0,0 +1,9 @@ + +namespace SMBLibrary +{ + public enum SMBTransportType + { + NetBiosOverTCP, // Port 139 + DirectTCPTransport, // Port 445 + } +} diff --git a/SMBLibrary/Enums/Win32Error.cs b/SMBLibrary/Enums/Win32Error.cs new file mode 100644 index 0000000..ee8587d --- /dev/null +++ b/SMBLibrary/Enums/Win32Error.cs @@ -0,0 +1,16 @@ + +namespace SMBLibrary +{ + // All Win32 error codes MUST be in the range 0x0000 to 0xFFFF + public enum Win32Error : ushort + { + ERROR_SUCCESS = 0x0000, + ERROR_ACCESS_DENIED = 0x0005, + ERROR_SHARING_VIOLATION = 0x0020, + ERROR_DISK_FULL = 0x0070, + ERROR_LOGON_FAILURE = 0x0000052E, + ERROR_ACCOUNT_RESTRICTION = 0x0000052F, + ERROR_LOGON_TYPE_NOT_GRANTED = 0x00000569, + NERR_NetNameNotFound = 0x00000906 + } +} diff --git a/SMBLibrary/IOCTL/FileFullEAInformation.cs b/SMBLibrary/IOCTL/FileFullEAInformation.cs new file mode 100644 index 0000000..73e2de9 --- /dev/null +++ b/SMBLibrary/IOCTL/FileFullEAInformation.cs @@ -0,0 +1,60 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary +{ + /// + /// [MS-FSCC] FILE_FULL_EA_INFORMATION data element + /// + public class FileFullEAInformation + { + public uint NextEntryOffset; + public byte Flags; + //byte EaNameLength; + //ushort EaValueLength; + public string EaName; // ASCII + public string EaValue; // ASCII + + public FileFullEAInformation() + { + } + + public FileFullEAInformation(byte[] buffer, int offset) + { + NextEntryOffset = LittleEndianReader.ReadUInt32(buffer, ref offset); + Flags = ByteReader.ReadByte(buffer, ref offset); + byte eaNameLength = ByteReader.ReadByte(buffer, ref offset); + ushort eaValueLength = LittleEndianReader.ReadUInt16(buffer, ref offset); + EaName = ByteReader.ReadAnsiString(buffer, ref offset, eaNameLength); + EaValue = ByteReader.ReadAnsiString(buffer, ref offset, eaValueLength); + } + + public void WriteBytes(byte[] buffer, int offset) + { + byte eaNameLength = (byte)EaName.Length; + ushort eaValueLength = (ushort)EaValue.Length; + LittleEndianWriter.WriteUInt32(buffer, ref offset, NextEntryOffset); + ByteWriter.WriteByte(buffer, ref offset, Flags); + ByteWriter.WriteByte(buffer, ref offset, eaNameLength); + LittleEndianWriter.WriteUInt16(buffer, ref offset, eaValueLength); + ByteWriter.WriteAnsiString(buffer, ref offset, EaName); + ByteWriter.WriteAnsiString(buffer, ref offset, EaValue); + } + + public int Length + { + get + { + return 8 + EaName.Length + EaValue.Length; + } + } + } +} diff --git a/SMBLibrary/IOCTL/FileFullEAInformationList.cs b/SMBLibrary/IOCTL/FileFullEAInformationList.cs new file mode 100644 index 0000000..b640e86 --- /dev/null +++ b/SMBLibrary/IOCTL/FileFullEAInformationList.cs @@ -0,0 +1,67 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary +{ + /// + /// [MS-FSCC] FILE_FULL_EA_INFORMATION buffer + /// + public class FileFullEAInformationList : List + { + public FileFullEAInformationList() + { + } + + public FileFullEAInformationList(byte[] buffer, int offset) + { + FileFullEAInformation entry = new FileFullEAInformation(buffer, offset); + this.Add(entry); + while (entry.NextEntryOffset != 0) + { + entry = new FileFullEAInformation(buffer, (int)entry.NextEntryOffset); + this.Add(entry); + } + } + + public void WriteBytes(byte[] buffer, int offset) + { + // When multiple FILE_FULL_EA_INFORMATION data elements are present in the buffer, each MUST be aligned on a 4-byte boundary + for (int index = 0; index < this.Count; index++) + { + this[index].WriteBytes(buffer, offset); + offset += this[index].Length; + if (index < this.Count - 1) + { + int padding = (4 - (offset % 4)) % 4; + offset += padding; + } + } + } + + public int Length + { + get + { + // When multiple FILE_FULL_EA_INFORMATION data elements are present in the buffer, each MUST be aligned on a 4-byte boundary + int length = 0; + for(int index = 0; index < this.Count; index++) + { + length += this[index].Length; + if (index < this.Count - 1) + { + int padding = (4 - (length % 4)) % 4; + length += padding; + } + } + return length; + } + } + } +} diff --git a/SMBLibrary/IOCTL/ObjectIDBufferType1.cs b/SMBLibrary/IOCTL/ObjectIDBufferType1.cs new file mode 100644 index 0000000..0f1b474 --- /dev/null +++ b/SMBLibrary/IOCTL/ObjectIDBufferType1.cs @@ -0,0 +1,48 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary +{ + /// + /// [MS-FSCC] FILE_OBJECTID_BUFFER Type 1 + /// + public class ObjectIDBufferType1 + { + public const int Length = 64; + + public Guid ObjectId; + public Guid BirthVolumeId; + public Guid BirthObjectId; + public Guid DomainId; + + public ObjectIDBufferType1() + { + } + + public ObjectIDBufferType1(byte[] buffer) + { + ObjectId = LittleEndianConverter.ToGuid(buffer, 0); + BirthVolumeId = LittleEndianConverter.ToGuid(buffer, 16); + BirthObjectId = LittleEndianConverter.ToGuid(buffer, 32); + DomainId = LittleEndianConverter.ToGuid(buffer, 48); + } + + public byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + LittleEndianWriter.WriteGuidBytes(buffer, 0, ObjectId); + LittleEndianWriter.WriteGuidBytes(buffer, 16, BirthVolumeId); + LittleEndianWriter.WriteGuidBytes(buffer, 32, BirthObjectId); + LittleEndianWriter.WriteGuidBytes(buffer, 48, DomainId); + return buffer; + } + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/EnumStructures/NameFlags.cs b/SMBLibrary/NetBios/NameServicePackets/EnumStructures/NameFlags.cs new file mode 100644 index 0000000..cc9e6fe --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/EnumStructures/NameFlags.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + public enum OwnerNodeType : byte + { + BNode = 0x00, + PNode = 0x01, + MNode = 0x10, + } + + public struct NameFlags // ushort + { + public const int Length = 2; + + public OwnerNodeType NodeType; + public bool WorkGroup; + + public ushort Value + { + get + { + ushort value = (ushort)(((byte)NodeType) << 13); + if (WorkGroup) + { + value |= 0x8000; + } + return value; + } + } + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/Enums/NameRecordType.cs b/SMBLibrary/NetBios/NameServicePackets/Enums/NameRecordType.cs new file mode 100644 index 0000000..fdfc1e3 --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/Enums/NameRecordType.cs @@ -0,0 +1,9 @@ + +namespace SMBLibrary.NetBios +{ + public enum NameRecordType : ushort + { + NB = 0x0020, + NBStat = 0x0021, + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/Enums/NameServiceOperation.cs b/SMBLibrary/NetBios/NameServicePackets/Enums/NameServiceOperation.cs new file mode 100644 index 0000000..88b82a4 --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/Enums/NameServiceOperation.cs @@ -0,0 +1,17 @@ + +namespace SMBLibrary.NetBios +{ + public enum NameServiceOperation : byte + { + QueryRequest = 0x00, + RegistrationRequest = 0x05, + ReleaseRequest = 0x06, + WackRequest = 0x07, + RefreshRequest = 0x08, + QueryResponse = 0x10, + RegistrationResponse = 0x15, + ReleaseResponse = 0x16, + WackResponse = 0x17, + RefreshResponse = 0x18, + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/Enums/NetBiosSuffix.cs b/SMBLibrary/NetBios/NameServicePackets/Enums/NetBiosSuffix.cs new file mode 100644 index 0000000..a7126fc --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/Enums/NetBiosSuffix.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.NetBios +{ + /// + /// 16th character suffix for netbios name. + /// see http://support.microsoft.com/kb/163409/en-us + /// + public enum NetBiosSuffix : byte + { + WorkstationService = 0x00, + MessengerService = 0x03, + DomainMasterBrowser = 0x1B, + MasterBrowser = 0x1D, + BrowserServiceElections = 0x1E, + FileServiceService = 0x20, + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/Enums/OperationFlags.cs b/SMBLibrary/NetBios/NameServicePackets/Enums/OperationFlags.cs new file mode 100644 index 0000000..090279b --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/Enums/OperationFlags.cs @@ -0,0 +1,17 @@ +using System; + +namespace SMBLibrary.NetBios +{ + /// + /// [RFC 1002] 4.2.1.1. HEADER + /// + [Flags] + public enum OperationFlags : byte + { + Broadcast = 0x01, + RecursionAvailable = 0x08, + RecursionDesired = 0x10, + Truncated = 0x20, + AuthoritativeAnswer = 0x40, + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/NameQueryRequest.cs b/SMBLibrary/NetBios/NameServicePackets/NameQueryRequest.cs new file mode 100644 index 0000000..36ab9ec --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/NameQueryRequest.cs @@ -0,0 +1,45 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + /// + /// [RFC 1002] 4.2.12. NAME QUERY REQUEST + /// + public class NameQueryRequest + { + public NameServicePacketHeader Header; + public QuestionSection Question; + + public NameQueryRequest() + { + Header = new NameServicePacketHeader(); + Header.OpCode = NameServiceOperation.QueryRequest; + Header.ARCount = 1; + Question = new QuestionSection(); + } + + public NameQueryRequest(byte[] buffer, int offset) + { + Header = new NameServicePacketHeader(buffer, ref offset); + Question = new QuestionSection(buffer, ref offset); + } + + public byte[] GetBytes() + { + MemoryStream stream = new MemoryStream(); + Header.WriteBytes(stream); + Question.WriteBytes(stream); + return stream.ToArray(); + } + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/NameRegistrationRequest.cs b/SMBLibrary/NetBios/NameServicePackets/NameRegistrationRequest.cs new file mode 100644 index 0000000..866453d --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/NameRegistrationRequest.cs @@ -0,0 +1,66 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + /// + /// [RFC 1002] 4.2.2. NAME REGISTRATION REQUEST + /// + public class NameRegistrationRequest + { + public const int DataLength = 6; + + public NameServicePacketHeader Header; + public QuestionSection Question; + public ResourceRecord Resource; + public NameFlags NameFlags; + public byte[] Address; // IPv4 address + + public NameRegistrationRequest() + { + Header = new NameServicePacketHeader(); + Header.OpCode = NameServiceOperation.RegistrationRequest; + Header.QDCount = 1; + Header.ARCount = 1; + Header.Flags = OperationFlags.Broadcast | OperationFlags.RecursionDesired; + Question = new QuestionSection(); + Resource = new ResourceRecord(); + Address = new byte[4]; + } + + public NameRegistrationRequest(string machineName, NetBiosSuffix suffix, IPAddress address) : this() + { + Question.Name = NetBiosUtils.GetMSNetBiosName(machineName, suffix); + Address = address.GetAddressBytes(); + } + + public byte[] GetBytes() + { + Resource.Data = GetData(); + + MemoryStream stream = new MemoryStream(); + Header.WriteBytes(stream); + Question.WriteBytes(stream); + Resource.WriteBytes(stream, NameServicePacketHeader.Length); + return stream.ToArray(); + } + + public byte[] GetData() + { + byte[] data = new byte[DataLength]; + BigEndianWriter.WriteUInt16(data, 0, NameFlags.Value); + ByteWriter.WriteBytes(data, 2, Address, 4); + return data; + } + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/NameServicePacketHeader.cs b/SMBLibrary/NetBios/NameServicePackets/NameServicePacketHeader.cs new file mode 100644 index 0000000..8127b18 --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/NameServicePacketHeader.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + /// + /// [RFC 1002] 4.2.1.1. HEADER + /// + public class NameServicePacketHeader + { + public const int Length = 12; + + public ushort TransactionID; + public NameServiceOperation OpCode; + public OperationFlags Flags; + public byte ResultCode; + public ushort QDCount; + public ushort ANCount; + public ushort NSCount; + public ushort ARCount; + + public NameServicePacketHeader() + { + } + + public NameServicePacketHeader(byte[] buffer, ref int offset) : this(buffer, offset) + { + offset += Length; + } + + public NameServicePacketHeader(byte[] buffer, int offset) + { + TransactionID = BigEndianConverter.ToUInt16(buffer, offset + 0); + ushort temp = BigEndianConverter.ToUInt16(buffer, offset + 2); + ResultCode = (byte)(temp & 0xF); + Flags = (OperationFlags)((temp >> 4) & 0x7F); + OpCode = (NameServiceOperation)((temp >> 11) & 0x1F); + QDCount = BigEndianConverter.ToUInt16(buffer, offset + 4); + ANCount = BigEndianConverter.ToUInt16(buffer, offset + 6); + NSCount = BigEndianConverter.ToUInt16(buffer, offset + 8); + ARCount = BigEndianConverter.ToUInt16(buffer, offset + 10); + } + + public void WriteBytes(Stream stream) + { + BigEndianWriter.WriteUInt16(stream, TransactionID); + ushort temp = (ushort)(ResultCode & (0xF)); + temp |= (ushort)((byte)Flags << 4); + temp |= (ushort)((byte)OpCode << 11); + BigEndianWriter.WriteUInt16(stream, temp); + BigEndianWriter.WriteUInt16(stream, QDCount); + BigEndianWriter.WriteUInt16(stream, ANCount); + BigEndianWriter.WriteUInt16(stream, NSCount); + BigEndianWriter.WriteUInt16(stream, ARCount); + + } + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/NodeStatusResponse.cs b/SMBLibrary/NetBios/NameServicePackets/NodeStatusResponse.cs new file mode 100644 index 0000000..ca86251 --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/NodeStatusResponse.cs @@ -0,0 +1,64 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + /// + /// [RFC 1002] 4.2.18. NODE STATUS RESPONSE + /// + public class NodeStatusResponse + { + public NameServicePacketHeader Header; + public ResourceRecord Resource; + // byte NumberOfNames; + public KeyValuePairList Names = new KeyValuePairList(); + public NodeStatistics Statistics; + + public NodeStatusResponse() + { + Header = new NameServicePacketHeader(); + Header.OpCode = NameServiceOperation.QueryResponse; + Header.Flags = OperationFlags.AuthoritativeAnswer | OperationFlags.RecursionAvailable; + Header.ANCount = 1; + Resource = new ResourceRecord(); + Resource.Type = NameRecordType.NBStat; + Statistics = new NodeStatistics(); + } + + public byte[] GetBytes() + { + Resource.Data = GetData(); + + MemoryStream stream = new MemoryStream(); + Header.WriteBytes(stream); + Resource.WriteBytes(stream); + return stream.ToArray(); + } + + public byte[] GetData() + { + MemoryStream stream = new MemoryStream(); + stream.WriteByte((byte)Names.Count); + foreach (KeyValuePair entry in Names) + { + ByteWriter.WriteAnsiString(stream, entry.Key); + //byte[] encodedName = NetBiosUtils.EncodeName(entry.Key, String.Empty); + //ByteWriter.WriteBytes(stream, encodedName); + BigEndianWriter.WriteUInt16(stream, entry.Value.Value); + } + + ByteWriter.WriteBytes(stream, Statistics.GetBytes()); + + return stream.ToArray(); + } + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/PositiveNameQueryResponse.cs b/SMBLibrary/NetBios/NameServicePackets/PositiveNameQueryResponse.cs new file mode 100644 index 0000000..70cb79c --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/PositiveNameQueryResponse.cs @@ -0,0 +1,57 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + /// + /// [RFC 1002] 4.2.13. POSITIVE NAME QUERY RESPONSE + /// + public class PositiveNameQueryResponse + { + public const int EntryLength = 6; + + public NameServicePacketHeader Header; + public ResourceRecord Resource; + public KeyValuePairList Addresses = new KeyValuePairList(); + + public PositiveNameQueryResponse() + { + Header = new NameServicePacketHeader(); + Header.Flags = OperationFlags.AuthoritativeAnswer | OperationFlags.RecursionDesired; + Header.OpCode = NameServiceOperation.QueryResponse; + Header.ANCount = 1; + Resource = new ResourceRecord(); + } + + public byte[] GetBytes() + { + Resource.Data = GetData(); + + MemoryStream stream = new MemoryStream(); + Header.WriteBytes(stream); + Resource.WriteBytes(stream); + return stream.ToArray(); + } + + public byte[] GetData() + { + byte[] data = new byte[EntryLength * Addresses.Count]; + int offset = 0; + foreach (KeyValuePair entry in Addresses) + { + BigEndianWriter.WriteUInt16(data, ref offset, entry.Value.Value); + ByteWriter.WriteBytes(data, ref offset, entry.Key, 4); + } + return data; + } + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/Structures/NodeStatistics.cs b/SMBLibrary/NetBios/NameServicePackets/Structures/NodeStatistics.cs new file mode 100644 index 0000000..94153eb --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/Structures/NodeStatistics.cs @@ -0,0 +1,75 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + public class NodeStatistics + { + public const int Length = 46; + + public byte[] UnitID; // MAC address, 6 bytes; + public byte Jumpers; + public byte TestResult; + public ushort VersionNumber; + public ushort PeriodOfStatistics; + public ushort NumberOfCRCs; + public ushort NumberOfAlignmentErrors; + public ushort NumberOfCollisions; + public ushort NumberOfSendAborts; + public uint NumberOfGoodSends; + public uint NumberOfGoodReceives; + public ushort NumberOfRetransmits; + public ushort NumberOfNoResourceConditions; + public ushort NumberOfFreeCommandBlocks; + public ushort TotalNumberOfCommandBlocks; + public ushort MaxTotalNumberOfCommandBlocks; + public ushort NumberOfPendingSessions; + public ushort MaxNumberOfPendingSessions; + public ushort MaxTotalsSessionsPossible; + public ushort SessionDataPacketSize; + + public NodeStatistics() + { + UnitID = new byte[6]; + } + + public void WriteBytes(byte[] buffer, int offset) + { + ByteWriter.WriteBytes(buffer, ref offset, UnitID, 6); + ByteWriter.WriteByte(buffer, ref offset, Jumpers); + ByteWriter.WriteByte(buffer, ref offset, TestResult); + BigEndianWriter.WriteUInt16(buffer, ref offset, VersionNumber); + BigEndianWriter.WriteUInt16(buffer, ref offset, PeriodOfStatistics); + BigEndianWriter.WriteUInt16(buffer, ref offset, NumberOfCRCs); + BigEndianWriter.WriteUInt16(buffer, ref offset, NumberOfAlignmentErrors); + BigEndianWriter.WriteUInt16(buffer, ref offset, NumberOfCollisions); + BigEndianWriter.WriteUInt16(buffer, ref offset, NumberOfSendAborts); + BigEndianWriter.WriteUInt32(buffer, ref offset, NumberOfGoodSends); + BigEndianWriter.WriteUInt32(buffer, ref offset, NumberOfGoodReceives); + BigEndianWriter.WriteUInt16(buffer, ref offset, NumberOfRetransmits); + BigEndianWriter.WriteUInt16(buffer, ref offset, NumberOfNoResourceConditions); + BigEndianWriter.WriteUInt16(buffer, ref offset, NumberOfFreeCommandBlocks); + BigEndianWriter.WriteUInt16(buffer, ref offset, TotalNumberOfCommandBlocks); + BigEndianWriter.WriteUInt16(buffer, ref offset, MaxTotalNumberOfCommandBlocks); + BigEndianWriter.WriteUInt16(buffer, ref offset, NumberOfPendingSessions); + BigEndianWriter.WriteUInt16(buffer, ref offset, MaxNumberOfPendingSessions); + BigEndianWriter.WriteUInt16(buffer, ref offset, MaxTotalsSessionsPossible); + BigEndianWriter.WriteUInt16(buffer, ref offset, SessionDataPacketSize); + } + + public byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + WriteBytes(buffer, 0); + return buffer; + } + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/Structures/QuestionSection.cs b/SMBLibrary/NetBios/NameServicePackets/Structures/QuestionSection.cs new file mode 100644 index 0000000..00449fa --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/Structures/QuestionSection.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + /// + /// [RFC 1002] 4.2.1.2. QUESTION SECTION + /// + public class QuestionSection + { + public string Name; + public NameRecordType Type = NameRecordType.NB; // NB + public ushort Class = 0x0001; // IN + + public QuestionSection() + { + } + + public QuestionSection(byte[] buffer, ref int offset) + { + Name = NetBiosUtils.DecodeName(buffer, ref offset); + Type = (NameRecordType)BigEndianReader.ReadUInt16(buffer, ref offset); + Class = BigEndianReader.ReadUInt16(buffer, ref offset); + } + + public void WriteBytes(Stream stream) + { + byte[] encodedName = NetBiosUtils.EncodeName(Name, String.Empty); + ByteWriter.WriteBytes(stream, encodedName); + BigEndianWriter.WriteUInt16(stream, (ushort)Type); + BigEndianWriter.WriteUInt16(stream, Class); + } + } +} diff --git a/SMBLibrary/NetBios/NameServicePackets/Structures/ResourceRecord.cs b/SMBLibrary/NetBios/NameServicePackets/Structures/ResourceRecord.cs new file mode 100644 index 0000000..7a8ae7d --- /dev/null +++ b/SMBLibrary/NetBios/NameServicePackets/Structures/ResourceRecord.cs @@ -0,0 +1,57 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + /// + /// [RFC 1002] 4.2.1.3. RESOURCE RECORD + /// + public class ResourceRecord + { + public string Name; + public NameRecordType Type = NameRecordType.NB; // NB + public ushort Class = 0x0001; // IN + public uint TTL; + // ushort DataLength + public byte[] Data; + + public ResourceRecord() + { + Name = String.Empty; + TTL = (uint)new TimeSpan(7, 0, 0, 0).TotalSeconds; + Data = new byte[0]; + } + + public void WriteBytes(Stream stream) + { + WriteBytes(stream, null); + } + + public void WriteBytes(Stream stream, int? nameOffset) + { + if (nameOffset.HasValue) + { + NetBiosUtils.WriteNamePointer(stream, nameOffset.Value); + } + else + { + byte[] encodedName = NetBiosUtils.EncodeName(Name, String.Empty); + ByteWriter.WriteBytes(stream, encodedName); + } + BigEndianWriter.WriteUInt16(stream, (ushort)Type); + BigEndianWriter.WriteUInt16(stream, Class); + BigEndianWriter.WriteUInt32(stream, TTL); + BigEndianWriter.WriteUInt16(stream, (ushort)Data.Length); + ByteWriter.WriteBytes(stream, Data); + } + } +} diff --git a/SMBLibrary/NetBios/NetBiosUtils.cs b/SMBLibrary/NetBios/NetBiosUtils.cs new file mode 100644 index 0000000..1553bf8 --- /dev/null +++ b/SMBLibrary/NetBios/NetBiosUtils.cs @@ -0,0 +1,207 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + public class NetBiosUtils + { + /// + /// The NetBIOS naming convention allows for 16 characters in a NetBIOS name. + /// Microsoft, however, limits NetBIOS names to 15 characters and uses the 16th character as a NetBIOS suffix + /// See http://support.microsoft.com/kb/163409/en-us + /// + public static string GetMSNetBiosName(string name, NetBiosSuffix suffix) + { + if (name.Length > 15) + { + name = name.Substring(0, 15); + } + else if (name.Length < 15) + { + name = name.PadRight(15); + } + + return name + (char)suffix; + } + + public static string GetNameFromMSNetBiosName(string netBiosName) + { + if (netBiosName.Length != 16) + { + throw new ArgumentException("Invalid MS NetBIOS name"); + } + + netBiosName = netBiosName.Substring(0, 15); + return netBiosName.TrimEnd(' '); + } + + public static byte[] EncodeName(string name, NetBiosSuffix suffix, string scopeID) + { + string netBiosName = GetMSNetBiosName(name, suffix); + return EncodeName(netBiosName, scopeID); + } + + /// NetBIOS name + /// dot-separated labels, formatted per DNS naming rules + public static byte[] EncodeName(string netBiosName, string scopeID) + { + string domainName = FirstLevelEncoding(netBiosName, scopeID); + return SecondLevelEncoding(domainName); + } + + // The conversion of a NetBIOS name to a format complying with DNS "best practices". + // NetBIOS names may contain characters which are not considered valid for use in DNS names, + // yet RFC 1001 and RFC 1002 attempted to map the NetBIOS name space into the DNS name space. + // To work around this conflict, NetBIOS names are encoded by splitting each byte of the name + // into two nibbles and then adding the value of 'A' (0x41). + // Thus, the '&' character (0x26) would be encoded as "CG". + // NetBIOS names are usually padded with spaces before being encoded. + /// NetBIOS name + /// dot-separated labels, formatted per DNS naming rules + public static string FirstLevelEncoding(string netBiosName, string scopeID) + { + // RFC 1001: NetBIOS names as seen across the client interface to NetBIOS are exactly 16 bytes long + if (netBiosName.Length != 16) + { + throw new ArgumentException("Invalid MS NetBIOS name"); + } + + StringBuilder builder = new StringBuilder(); + for (int index = 0; index < netBiosName.Length; index++) + { + byte c = (byte)netBiosName[index]; + byte high = (byte)(0x41 + (c >> 4)); + byte low = (byte)(0x41 + (c & 0x0F)); + builder.Append((char)high); + builder.Append((char)low); + } + + if (scopeID.Length > 0) + { + builder.Append("."); + builder.Append(scopeID); + } + + return builder.ToString(); + } + + // Domain names messages are expressed in terms of a sequence + // of labels. Each label is represented as a one octet length + // field followed by that number of octets. Since every domain + // name ends with the null label of the root, a compressed + // domain name is terminated by a length byte of zero + /// + /// The on-the-wire format of an NBT name. The encoding scheme replaces the familiar dot characters + /// used in DNS names with a byte containing the length of the next label. + /// + public static byte[] SecondLevelEncoding(string domainName) + { + string[] labels = domainName.Split('.'); + int length = 1; // null terminator + for (int index = 0; index < labels.Length; index++) + { + length += 1 + labels[index].Length; + if (labels[index].Length > 63) + { + throw new ArgumentException("Invalid NetBIOS label length"); + } + } + + byte[] result = new byte[length]; + int offset = 0; + foreach(string label in labels) + { + result[offset] = (byte)label.Length; + offset++; + ByteWriter.WriteAnsiString(result, offset, label, label.Length); + offset += label.Length; + } + + result[offset] = 0; // null termination + return result; + } + + public static string DecodeName(byte[] buffer, ref int offset) + { + string domainName = SecondLevelDecoding(buffer, ref offset); + string name = domainName.Split('.')[0]; + return FirstLevelDecoding(name); + } + + public static string SecondLevelDecoding(byte[] buffer, ref int offset) + { + StringBuilder builder = new StringBuilder(); + + byte labelLength = ByteReader.ReadByte(buffer, ref offset); + while (labelLength > 0) + { + if (builder.Length > 0) + { + builder.Append("."); + } + + // The high order two bits of the length field must be zero + if (labelLength > 63) + { + throw new ArgumentException("Invalid NetBIOS label length"); + } + + string label = ByteReader.ReadAnsiString(buffer, offset, labelLength); + builder.Append(label); + offset += labelLength; + + labelLength = ByteReader.ReadByte(buffer, ref offset); + } + + return builder.ToString(); + } + + public static string FirstLevelDecoding(string name) + { + StringBuilder builder = new StringBuilder(); + + for(int index = 0; index < name.Length; index += 2) + { + byte c0 = (byte)name[index]; + byte c1 = (byte)name[index + 1]; + byte high = (byte)(((c0 - 0x41) & 0xF) << 4); + byte low = (byte)((c1 - 0x41) & 0xF); + byte c = (byte)(high | low); + builder.Append((char)c); + } + + return builder.ToString(); + } + + public static void WriteNamePointer(byte[] buffer, ref int offset, int nameOffset) + { + WriteNamePointer(buffer, offset, nameOffset); + offset += 2; + } + + /// + /// Will write a 2 bytes pointer to a name + /// Note: NetBIOS implementations can only use label string pointers in Name Service packets + /// + public static void WriteNamePointer(byte[] buffer, int offset, int nameOffset) + { + ushort pointer = (ushort)(0xC000 | (nameOffset & 0x3FFF)); + BigEndianWriter.WriteUInt16(buffer, offset, pointer); + } + + public static void WriteNamePointer(Stream stream, int nameOffset) + { + ushort pointer = (ushort)(0xC000 | (nameOffset & 0x3FFF)); + BigEndianWriter.WriteUInt16(stream, pointer); + } + } +} diff --git a/SMBLibrary/NetBios/SessionPackets/Enums/SessionPacketTypeName.cs b/SMBLibrary/NetBios/SessionPackets/Enums/SessionPacketTypeName.cs new file mode 100644 index 0000000..ed6f90b --- /dev/null +++ b/SMBLibrary/NetBios/SessionPackets/Enums/SessionPacketTypeName.cs @@ -0,0 +1,21 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +namespace SMBLibrary.NetBios +{ + /// + /// [RFC 1002] 4.2.1.1. HEADER + /// + public enum SessionPacketTypeName : byte + { + SessionMessage = 0x00, + SessionRequest = 0x81, + PositiveSessionResponse = 0x82, + NegativeSessionResponse = 0x83, + RetargetSessionResponse = 0x84, + SessionKeepAlive = 0x85, + } +} diff --git a/SMBLibrary/NetBios/SessionPackets/NegativeSessionResponsePacket.cs b/SMBLibrary/NetBios/SessionPackets/NegativeSessionResponsePacket.cs new file mode 100644 index 0000000..dee829c --- /dev/null +++ b/SMBLibrary/NetBios/SessionPackets/NegativeSessionResponsePacket.cs @@ -0,0 +1,36 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + public class NegativeSessionResponsePacket : SessionPacket + { + public byte ErrorCode; + + public NegativeSessionResponsePacket() : base() + { + this.Type = SessionPacketTypeName.NegativeSessionResponse; + } + + public NegativeSessionResponsePacket(byte[] buffer) : base(buffer) + { + ErrorCode = ByteReader.ReadByte(this.Trailer, 0); + } + + public override byte[] GetBytes() + { + this.Trailer = new byte[1]; + this.Trailer[0] = ErrorCode; + + return base.GetBytes(); + } + } +} diff --git a/SMBLibrary/NetBios/SessionPackets/PositiveSessionResponsePacket.cs b/SMBLibrary/NetBios/SessionPackets/PositiveSessionResponsePacket.cs new file mode 100644 index 0000000..050893e --- /dev/null +++ b/SMBLibrary/NetBios/SessionPackets/PositiveSessionResponsePacket.cs @@ -0,0 +1,31 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + public class PositiveSessionResponsePacket : SessionPacket + { + public PositiveSessionResponsePacket() : base() + { + this.Type = SessionPacketTypeName.PositiveSessionResponse; + } + + public PositiveSessionResponsePacket(byte[] buffer) : base(buffer) + { + } + + public override byte[] GetBytes() + { + this.Trailer = new byte[0]; + return base.GetBytes(); + } + } +} diff --git a/SMBLibrary/NetBios/SessionPackets/SessionKeepAlivePacket.cs b/SMBLibrary/NetBios/SessionPackets/SessionKeepAlivePacket.cs new file mode 100644 index 0000000..e6bcb6c --- /dev/null +++ b/SMBLibrary/NetBios/SessionPackets/SessionKeepAlivePacket.cs @@ -0,0 +1,31 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + public class SessionKeepAlivePacket : SessionPacket + { + public SessionKeepAlivePacket() + { + this.Type = SessionPacketTypeName.SessionKeepAlive; + } + + public SessionKeepAlivePacket(byte[] buffer) : base(buffer) + { + } + + public override byte[] GetBytes() + { + this.Trailer = new byte[0]; + return base.GetBytes(); + } + } +} diff --git a/SMBLibrary/NetBios/SessionPackets/SessionMessagePacket.cs b/SMBLibrary/NetBios/SessionPackets/SessionMessagePacket.cs new file mode 100644 index 0000000..2385f83 --- /dev/null +++ b/SMBLibrary/NetBios/SessionPackets/SessionMessagePacket.cs @@ -0,0 +1,25 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + public class SessionMessagePacket : SessionPacket + { + public SessionMessagePacket() : base() + { + this.Type = SessionPacketTypeName.SessionMessage; + } + + public SessionMessagePacket(byte[] buffer) : base(buffer) + { + } + } +} diff --git a/SMBLibrary/NetBios/SessionPackets/SessionPacket.cs b/SMBLibrary/NetBios/SessionPackets/SessionPacket.cs new file mode 100644 index 0000000..71d20d2 --- /dev/null +++ b/SMBLibrary/NetBios/SessionPackets/SessionPacket.cs @@ -0,0 +1,75 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + public abstract class SessionPacket + { + public SessionPacketTypeName Type; + public byte Flags; + public int Length; // 2 bytes + length extension bit + public byte[] Trailer; + + public SessionPacket() + { + } + + public SessionPacket(byte[] buffer) + { + Type = (SessionPacketTypeName)ByteReader.ReadByte(buffer, 0); + Flags = ByteReader.ReadByte(buffer, 1); + Length = (Flags & 0x01) << 16 | BigEndianConverter.ToUInt16(buffer, 2); + + this.Trailer = ByteReader.ReadBytes(buffer, 4, Length); + } + + public virtual byte[] GetBytes() + { + Length = this.Trailer.Length; + if (Length > 0x1FFFF) + { + throw new ArgumentException("Invalid NBT packet length"); + } + + Flags = Convert.ToByte(Length > 0xFFFF); + + byte[] buffer = new byte[4 + Trailer.Length]; + ByteWriter.WriteByte(buffer, 0, (byte)this.Type); + ByteWriter.WriteByte(buffer, 1, Flags); + BigEndianWriter.WriteUInt16(buffer, 2, (ushort)(Length & 0xFFFF)); + ByteWriter.WriteBytes(buffer, 4, this.Trailer); + + return buffer; + } + + public static SessionPacket GetSessionPacket(byte[] buffer) + { + SessionPacketTypeName type = (SessionPacketTypeName)ByteReader.ReadByte(buffer, 0); + switch (type) + { + case SessionPacketTypeName.SessionMessage: + return new SessionMessagePacket(buffer); + case SessionPacketTypeName.SessionRequest: + return new SessionRequestPacket(buffer); + case SessionPacketTypeName.PositiveSessionResponse: + return new PositiveSessionResponsePacket(buffer); + case SessionPacketTypeName.NegativeSessionResponse: + return new NegativeSessionResponsePacket(buffer); + case SessionPacketTypeName.RetargetSessionResponse: + return new SessionRetargetResponsePacket(buffer); + case SessionPacketTypeName.SessionKeepAlive: + return new SessionKeepAlivePacket(buffer); + default: + throw new InvalidRequestException("Invalid NetBIOS Session Packet"); + } + } + } +} diff --git a/SMBLibrary/NetBios/SessionPackets/SessionRequestPacket.cs b/SMBLibrary/NetBios/SessionPackets/SessionRequestPacket.cs new file mode 100644 index 0000000..1aba6d5 --- /dev/null +++ b/SMBLibrary/NetBios/SessionPackets/SessionRequestPacket.cs @@ -0,0 +1,41 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + public class SessionRequestPacket : SessionPacket + { + public string CalledName; + public string CallingName; + + public SessionRequestPacket() + { + this.Type = SessionPacketTypeName.SessionRequest; + } + + public SessionRequestPacket(byte[] buffer) : base(buffer) + { + int offset = 0; + CalledName = NetBiosUtils.DecodeName(this.Trailer, ref offset); + CallingName = NetBiosUtils.DecodeName(this.Trailer, ref offset); + } + + public override byte[] GetBytes() + { + byte[] part1 = NetBiosUtils.EncodeName(CalledName, String.Empty); + byte[] part2 = NetBiosUtils.EncodeName(CallingName, String.Empty); + this.Trailer = new byte[part1.Length + part2.Length]; + ByteWriter.WriteBytes(this.Trailer, 0, part1); + ByteWriter.WriteBytes(this.Trailer, part1.Length, part2); + return base.GetBytes(); + } + } +} diff --git a/SMBLibrary/NetBios/SessionPackets/SessionRetargetResponsePacket.cs b/SMBLibrary/NetBios/SessionPackets/SessionRetargetResponsePacket.cs new file mode 100644 index 0000000..341510c --- /dev/null +++ b/SMBLibrary/NetBios/SessionPackets/SessionRetargetResponsePacket.cs @@ -0,0 +1,38 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.NetBios +{ + public class SessionRetargetResponsePacket : SessionPacket + { + uint IPAddress; + ushort Port; + + public SessionRetargetResponsePacket() : base() + { + this.Type = SessionPacketTypeName.RetargetSessionResponse; + } + + public SessionRetargetResponsePacket(byte[] buffer) : base(buffer) + { + IPAddress = BigEndianConverter.ToUInt32(this.Trailer, 0); + Port = BigEndianConverter.ToUInt16(this.Trailer, 4); + } + + public override byte[] GetBytes() + { + this.Trailer = new byte[6]; + BigEndianWriter.WriteUInt32(this.Trailer, 0, IPAddress); + BigEndianWriter.WriteUInt16(this.Trailer, 4, Port); + return base.GetBytes(); + } + } +} diff --git a/SMBLibrary/Properties/AssemblyInfo.cs b/SMBLibrary/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e133a0a --- /dev/null +++ b/SMBLibrary/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SMBLibrary")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Tal Aloni")] +[assembly: AssemblyProduct("SMBLibrary")] +[assembly: AssemblyCopyright("Copyright © Tal Aloni 2014-2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("301890e4-fc53-448e-8070-79d17086f922")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.5.0")] +[assembly: AssemblyFileVersion("1.0.5.0")] diff --git a/SMBLibrary/RPC/Enum/NegotiationResult.cs b/SMBLibrary/RPC/Enum/NegotiationResult.cs new file mode 100644 index 0000000..7dc9595 --- /dev/null +++ b/SMBLibrary/RPC/Enum/NegotiationResult.cs @@ -0,0 +1,16 @@ + +namespace SMBLibrary.RPC +{ + public enum NegotiationResult : ushort + { + Acceptance, + UserRejection, + ProviderRejection, + + /// + /// Microsoft extension: + /// [MS-RPCE] 2.2.2.4 - negotiate_ack + /// + NegotiateAck + } +} diff --git a/SMBLibrary/RPC/Enum/PacketFlags.cs b/SMBLibrary/RPC/Enum/PacketFlags.cs new file mode 100644 index 0000000..b536203 --- /dev/null +++ b/SMBLibrary/RPC/Enum/PacketFlags.cs @@ -0,0 +1,16 @@ +using System; + +namespace SMBLibrary.RPC +{ + [Flags] + public enum PacketFlags : byte + { + FirstFragment = 0x01, // PFC_FIRST_FRAG + LastFragment = 0x02, // PFC_LAST_FRAG + PendingCancel = 0x04, // PFC_PENDING_CANCEL + ConcurrntMultiplexing = 0x10, // PFC_CONC_MPX + DidNotExecute = 0x20, // PFC_DID_NOT_EXECUTE + Maybe = 0x40, // PFC_MAYBE + ObjectUUID = 0x80, // PFC_OBJECT_UUID + } +} diff --git a/SMBLibrary/RPC/Enum/PacketTypeName.cs b/SMBLibrary/RPC/Enum/PacketTypeName.cs new file mode 100644 index 0000000..b20b110 --- /dev/null +++ b/SMBLibrary/RPC/Enum/PacketTypeName.cs @@ -0,0 +1,27 @@ + +namespace SMBLibrary.RPC +{ + // Commented out packet types are connectionless-only + public enum PacketTypeName : byte + { + Request = 0x00, + // Ping = 0x01, + Response = 0x02, + Fault = 0x03, + //Working = 0x04, + //NoCall = 0x05, + //Reject = 0x06, + //Ack = 0x07, + //CLCancel = 0x08, // cl_cancel + //FAck = 0x09, + //CancelAck = 0x0A, // cancel_ack + Bind = 0x0B, + BindAck = 0x0C, + BindNak = 0x0D, // bind_nak + AlterContext = 0x0E, // alter_context + AlterContextResponse = 0x0F, // alter_context_resp + Shutdown = 0x11, + COCancel = 0x12, // co_cancel + Orphaned = 0x13, + } +} diff --git a/SMBLibrary/RPC/Enum/RejectionReason.cs b/SMBLibrary/RPC/Enum/RejectionReason.cs new file mode 100644 index 0000000..f7fe094 --- /dev/null +++ b/SMBLibrary/RPC/Enum/RejectionReason.cs @@ -0,0 +1,11 @@ + +namespace SMBLibrary.RPC +{ + public enum RejectionReason : ushort + { + NotSpecified, + AbstractSyntaxNotSupported, + ProposedTransferSyntaxesNotSupported, + LocalLimitExceeded, + } +} diff --git a/SMBLibrary/RPC/EnumStructures/DataRepresentationFormat.cs b/SMBLibrary/RPC/EnumStructures/DataRepresentationFormat.cs new file mode 100644 index 0000000..f3ce352 --- /dev/null +++ b/SMBLibrary/RPC/EnumStructures/DataRepresentationFormat.cs @@ -0,0 +1,52 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; + +namespace SMBLibrary.RPC +{ + // See DCE 1.1: Remote Procedure Call, Chapter 14.1 - Data Representation Format Label + public enum CharacterFormat : byte + { + ASCII = 0x00, + EBCDIC = 0x01, + } + + public enum ByteOrder : byte + { + BigEndian = 0x00, + LittleEndian = 0x01, + } + + public enum FloatingPointRepresentation : byte + { + IEEE = 0x00, + VAX = 0x01, + Cray = 0x02, + IBM = 0x03, + } + + public struct DataRepresentationFormat // uint + { + public CharacterFormat CharacterFormat; + public ByteOrder ByteOrder; + public FloatingPointRepresentation FloatingPointRepresentation; + + public DataRepresentationFormat(byte[] buffer, int offset) + { + CharacterFormat = (CharacterFormat)(buffer[offset + 0] & 0x0F); + ByteOrder = (ByteOrder)(buffer[offset + 0] >> 4); + FloatingPointRepresentation = (FloatingPointRepresentation)(buffer[offset + 1]); + } + + public void WriteBytes(byte[] buffer, int offset) + { + buffer[offset + 0] = (byte)CharacterFormat; + buffer[offset + 0] |= (byte)((byte)ByteOrder << 4); + buffer[offset + 1] = (byte)FloatingPointRepresentation; + } + } +} diff --git a/SMBLibrary/RPC/NDR/INDRStructure.cs b/SMBLibrary/RPC/NDR/INDRStructure.cs new file mode 100644 index 0000000..928ae27 --- /dev/null +++ b/SMBLibrary/RPC/NDR/INDRStructure.cs @@ -0,0 +1,21 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.RPC +{ + /// + /// Serializable Structure / Union / Array + /// + public interface INDRStructure + { + void Read(NDRParser parser); + void Write(NDRWriter writer); + } +} diff --git a/SMBLibrary/RPC/NDR/NDRConformantArray.cs b/SMBLibrary/RPC/NDR/NDRConformantArray.cs new file mode 100644 index 0000000..221507c --- /dev/null +++ b/SMBLibrary/RPC/NDR/NDRConformantArray.cs @@ -0,0 +1,45 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.RPC +{ + public class NDRConformantArray : List, INDRStructure where T : INDRStructure, new() + { + /// + /// See DCE 1.1: Remote Procedure Call - 14.3.3.2 - Uni-dimensional Conformant Arrays + /// + /// + public void Read(NDRParser parser) + { + parser.BeginStructure(); + uint maxCount = parser.ReadUInt32(); + for (int index = 0; index < maxCount; index++) + { + T entry = new T(); + entry.Read(parser); + this.Add(entry); + } + + parser.EndStructure(); + } + + public void Write(NDRWriter writer) + { + writer.BeginStructure(); + uint maxCount = (uint)this.Count; + writer.WriteUInt32(maxCount); + for (int index = 0; index < this.Count; index++) + { + this[index].Write(writer); + } + writer.EndStructure(); + } + } +} diff --git a/SMBLibrary/RPC/NDR/NDRParser.cs b/SMBLibrary/RPC/NDR/NDRParser.cs new file mode 100644 index 0000000..29d7684 --- /dev/null +++ b/SMBLibrary/RPC/NDR/NDRParser.cs @@ -0,0 +1,140 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// NDR - Native Data Representation + /// See DCE 1.1: Remote Procedure Call, Chapter 14 - Transfer Syntax NDR + /// + public class NDRParser + { + private byte[] m_buffer; + private int m_offset; + private int m_depth; + private List m_deferredStructures = new List(); + private Dictionary m_referentToInstance = new Dictionary(); + + public NDRParser(byte[] buffer) + { + m_buffer = buffer; + m_offset = 0; + m_depth = 0; + } + + public void BeginStructure() + { + m_depth++; + } + + /// + /// Add embedded pointer deferred structure (referent) parser + /// + private void AddDeferredStructure(INDRStructure structure) + { + m_deferredStructures.Add(structure); + } + + public void EndStructure() + { + m_depth--; + // 14.3.12.3 - Algorithm for Deferral of Referents + // Representations of (embedded) pointer referents are ordered according to a left-to-right, depth-first traversal of the embedding construction. + // referent representations for the embedded construction are further deferred to a position in the octet stream that + // follows the representation of the embedding construction. The set of referent representations for the embedded construction + // is inserted among the referent representations for any pointers in the embedding construction, according to the order of elements or + // members in the embedding construction + if (m_depth == 0) + { + // Make a copy of all the deferred structures, additional deferred structures will be inserted to m_deferredStructures + // as we process the existing list + List deferredStructures = new List(m_deferredStructures); + m_deferredStructures.Clear(); + // Read all deferred types: + foreach (INDRStructure deferredStructure in deferredStructures) + { + deferredStructure.Read(this); + } + } + } + + public string ReadUnicodeString() + { + NDRUnicodeString unicodeString = new NDRUnicodeString(this); + return unicodeString.Value; + } + + public void ReadStructure(INDRStructure structure) + { + structure.Read(this); + } + + // 14.3.11.1 - Top-level Full Pointers + public string ReadTopLevelUnicodeStringPointer() + { + uint referentID = ReadUInt32(); + if (referentID == 0) + { + return null; + } + + if (m_referentToInstance.ContainsKey(referentID)) + { + NDRUnicodeString unicodeString = (NDRUnicodeString)m_referentToInstance[referentID]; + return unicodeString.Value; + } + else + { + NDRUnicodeString unicodeString = new NDRUnicodeString(this); + m_referentToInstance.Add(referentID, unicodeString); + return unicodeString.Value; + } + } + + public void ReadEmbeddedStructureFullPointer(ref NDRUnicodeString structure) + { + ReadEmbeddedStructureFullPointer(ref structure); + } + + public void ReadEmbeddedStructureFullPointer(ref T structure) where T : INDRStructure, new () + { + uint referentID = ReadUInt32(); + if (referentID != 0) // not null + { + if (structure == null) + { + structure = new T(); + } + AddDeferredStructure(structure); + } + else + { + structure = default(T); + } + } + + // 14.2.2 - Alignment of Primitive Types + public uint ReadUInt16() + { + m_offset += (2 - (m_offset % 2)) % 2; + return LittleEndianReader.ReadUInt16(m_buffer, ref m_offset); + } + + // 14.2.2 - Alignment of Primitive Types + public uint ReadUInt32() + { + m_offset += (4 - (m_offset % 4)) % 4; + return LittleEndianReader.ReadUInt32(m_buffer, ref m_offset); + } + } +} diff --git a/SMBLibrary/RPC/NDR/NDRTypeName.cs b/SMBLibrary/RPC/NDR/NDRTypeName.cs new file mode 100644 index 0000000..218fa79 --- /dev/null +++ b/SMBLibrary/RPC/NDR/NDRTypeName.cs @@ -0,0 +1,16 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +namespace SMBLibrary.RPC +{ + /// + /// Primitive and construced types + /// + public enum NDRTypeName + { + UnicodeString, + } +} diff --git a/SMBLibrary/RPC/NDR/NDRUnicodeString.cs b/SMBLibrary/RPC/NDR/NDRUnicodeString.cs new file mode 100644 index 0000000..bab3f62 --- /dev/null +++ b/SMBLibrary/RPC/NDR/NDRUnicodeString.cs @@ -0,0 +1,74 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + public class NDRUnicodeString : INDRStructure + { + public string Value; + + public NDRUnicodeString() + { + Value = String.Empty; + } + + public NDRUnicodeString(string value) + { + Value = value; + } + + public NDRUnicodeString(NDRParser parser) + { + Read(parser); + } + + // 14.3.4.2 - Conformant and Varying Strings + public void Read(NDRParser parser) + { + uint maxCount = parser.ReadUInt32(); + // the offset from the first index of the string to the first index of the actual subset being passed + uint index = parser.ReadUInt32(); + // actualCount includes the null terminator + uint actualCount = parser.ReadUInt32(); + StringBuilder builder = new StringBuilder(); + for (int position = 0; position < actualCount - 1; position++) + { + builder.Append((char)parser.ReadUInt16()); + } + this.Value = builder.ToString(); + parser.ReadUInt16(); // null terminator + } + + public void Write(NDRWriter writer) + { + int length = 0; + if (Value != null) + { + length = Value.Length; + } + + // maxCount includes the null terminator + uint maxCount = (uint)(length + 1); + writer.WriteUInt32(maxCount); + // the offset from the first index of the string to the first index of the actual subset being passed + uint index = 0; + writer.WriteUInt32(index); + // actualCount includes the null terminator + uint actualCount = (uint)(length + 1); + writer.WriteUInt32(actualCount); + for (int position = 0; position < length; position++) + { + writer.WriteUInt16((ushort)Value[position]); + } + writer.WriteUInt16(0); // null terminator + } + } +} diff --git a/SMBLibrary/RPC/NDR/NDRWriter.cs b/SMBLibrary/RPC/NDR/NDRWriter.cs new file mode 100644 index 0000000..78b7f3c --- /dev/null +++ b/SMBLibrary/RPC/NDR/NDRWriter.cs @@ -0,0 +1,140 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// NDR - Native Data Representation + /// See DCE 1.1: Remote Procedure Call, Chapter 14 - Transfer Syntax NDR + /// + public class NDRWriter + { + private MemoryStream m_stream = new MemoryStream(); + private int m_depth; + private List m_deferredStructures = new List(); + private Dictionary m_referentToInstance = new Dictionary(); + private uint m_nextReferentID = 0x00020000; + + public void BeginStructure() + { + m_depth++; + } + + /// + /// Add embedded pointer deferred structure (referent) writer + /// + private void AddDeferredStructure(INDRStructure structure) + { + m_deferredStructures.Add(structure); + } + + public void EndStructure() + { + m_depth--; + // 14.3.12.3 - Algorithm for Deferral of Referents + // Representations of (embedded) pointer referents are ordered according to a left-to-right, depth-first traversal of the embedding construction. + // referent representations for the embedded construction are further deferred to a position in the octet stream that + // follows the representation of the embedding construction. The set of referent representations for the embedded construction + // is inserted among the referent representations for any pointers in the embedding construction, according to the order of elements or + // members in the embedding construction + if (m_depth == 0) + { + // Make a copy of all the deferred structures, additional deferred structures will be inserted to m_deferredStructures + // as we process the existing list + List deferredStructures = new List(m_deferredStructures); + m_deferredStructures.Clear(); + // Write all deferred types: + foreach (INDRStructure deferredStructure in deferredStructures) + { + deferredStructure.Write(this); + } + } + } + + public void WriteUnicodeString(string value) + { + NDRUnicodeString unicodeString = new NDRUnicodeString(value); + unicodeString.Write(this); + } + + public void WriteStructure(INDRStructure structure) + { + structure.Write(this); + } + + public void WriteTopLevelUnicodeStringPointer(string value) + { + if (value == null) + { + WriteUInt32(0); + return; + } + + // Note: We do not bother searching for existing values + uint referentID = GetNextReferentID(); + WriteUInt32(referentID); + NDRUnicodeString unicodeString = new NDRUnicodeString(value); + unicodeString.Write(this); + m_referentToInstance.Add(referentID, unicodeString); + } + + // 14.3.12.1 Embedded Full Pointers + public void WriteEmbeddedStructureFullPointer(INDRStructure structure) + { + if (structure == null) + { + WriteUInt32(0); // null + return; + } + else + { + // Note: We do not bother searching for existing values + uint referentID = GetNextReferentID(); + WriteUInt32(referentID); + AddDeferredStructure(structure); + m_referentToInstance.Add(referentID, structure); + } + } + + // 14.2.2 - Alignment of Primitive Types + public void WriteUInt16(ushort value) + { + uint padding = (uint)(2 - (m_stream.Position % 2)) % 2; + m_stream.Position += padding; + LittleEndianWriter.WriteUInt16(m_stream, value); + } + + // 14.2.2 - Alignment of Primitive Types + public void WriteUInt32(uint value) + { + uint padding = (uint)(4 - (m_stream.Position % 4)) % 4; + m_stream.Position += padding; + LittleEndianWriter.WriteUInt32(m_stream, value); + } + + public byte[] GetBytes() + { + byte[] buffer = new byte[m_stream.Length]; + m_stream.Seek(0, SeekOrigin.Begin); + m_stream.Read(buffer, 0, buffer.Length); + return buffer; + } + + private uint GetNextReferentID() + { + uint result = m_nextReferentID; + m_nextReferentID++; + return result; + } + } +} diff --git a/SMBLibrary/RPC/PDU/BindAckPDU.cs b/SMBLibrary/RPC/PDU/BindAckPDU.cs new file mode 100644 index 0000000..262ff11 --- /dev/null +++ b/SMBLibrary/RPC/PDU/BindAckPDU.cs @@ -0,0 +1,68 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// rpcconn_bind_ack_hdr_t + /// + public class BindAckPDU : RPCPDU + { + public ushort MaxTransmitFragmentSize; // max_xmit_frag + public ushort MaxReceiveFragmentSize; // max_recv_frag + public uint AssociationGroupID; // assoc_group_id + public string SecondaryAddress; // sec_addr (port_any_t) + // Padding (alignment to 4 byte boundary) + public ResultList ResultList; // p_result_list + public byte[] AuthVerifier; + + public BindAckPDU() : base() + { + PacketType = PacketTypeName.BindAck; + SecondaryAddress = String.Empty; + ResultList = new ResultList(); + AuthVerifier = new byte[0]; + } + + public BindAckPDU(byte[] buffer) : base(buffer) + { + int offset = RPCPDU.CommonFieldsLength; + MaxTransmitFragmentSize = LittleEndianReader.ReadUInt16(buffer, ref offset); + MaxReceiveFragmentSize = LittleEndianReader.ReadUInt16(buffer, ref offset); + AssociationGroupID = LittleEndianReader.ReadUInt32(buffer, ref offset); + SecondaryAddress = RPCHelper.ReadPortAddress(buffer, ref offset); + int padding = (4 - (offset % 4)) % 4; + offset += padding; + ResultList = new ResultList(buffer, offset); + offset += ResultList.Length; + AuthVerifier = ByteReader.ReadBytes(buffer, offset, AuthLength); + } + + public override byte[] GetBytes() + { + AuthLength = (ushort)AuthVerifier.Length; + int padding = (4 - ((SecondaryAddress.Length + 3) % 4)) % 4; + FragmentLength = (ushort)(RPCPDU.CommonFieldsLength + 8 + SecondaryAddress.Length + 3 + padding + ResultList.Length + AuthLength); + byte[] buffer = new byte[FragmentLength]; + WriteCommonFieldsBytes(buffer); + int offset = RPCPDU.CommonFieldsLength; + LittleEndianWriter.WriteUInt16(buffer, ref offset, MaxTransmitFragmentSize); + LittleEndianWriter.WriteUInt16(buffer, ref offset, MaxReceiveFragmentSize); + LittleEndianWriter.WriteUInt32(buffer, ref offset, AssociationGroupID); + RPCHelper.WritePortAddress(buffer, ref offset, SecondaryAddress); + offset += padding; + ResultList.WriteBytes(buffer, ref offset); + ByteWriter.WriteBytes(buffer, offset, AuthVerifier); + + return buffer; + } + } +} diff --git a/SMBLibrary/RPC/PDU/BindPDU.cs b/SMBLibrary/RPC/PDU/BindPDU.cs new file mode 100644 index 0000000..efad960 --- /dev/null +++ b/SMBLibrary/RPC/PDU/BindPDU.cs @@ -0,0 +1,60 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// rpcconn_bind_hdr_t + /// + public class BindPDU : RPCPDU + { + public ushort MaxTransmitFragmentSize; // max_xmit_frag + public ushort MaxReceiveFragmentSize; // max_recv_frag + public uint AssociationGroupID; // assoc_group_id + public ContextList ContextList; + public byte[] AuthVerifier; + + public BindPDU() : base() + { + PacketType = PacketTypeName.Bind; + ContextList = new ContextList(); + AuthVerifier = new byte[0]; + } + + public BindPDU(byte[] buffer) : base(buffer) + { + int offset = RPCPDU.CommonFieldsLength; + MaxTransmitFragmentSize = LittleEndianReader.ReadUInt16(buffer, ref offset); + MaxReceiveFragmentSize = LittleEndianReader.ReadUInt16(buffer, ref offset); + AssociationGroupID = LittleEndianReader.ReadUInt32(buffer, ref offset); + ContextList = new ContextList(buffer, offset); + offset += ContextList.Length; + AuthVerifier = ByteReader.ReadBytes(buffer, offset, AuthLength); + } + + public override byte[] GetBytes() + { + AuthLength =(ushort)AuthVerifier.Length; + FragmentLength = (ushort)(RPCPDU.CommonFieldsLength + 8 + ContextList.Length + AuthLength); + byte[] buffer = new byte[FragmentLength]; + WriteCommonFieldsBytes(buffer); + int offset = RPCPDU.CommonFieldsLength; + LittleEndianWriter.WriteUInt16(buffer, ref offset, MaxTransmitFragmentSize); + LittleEndianWriter.WriteUInt16(buffer, ref offset, MaxReceiveFragmentSize); + LittleEndianWriter.WriteUInt32(buffer, ref offset, AssociationGroupID); + ContextList.WriteBytes(buffer, ref offset); + ByteWriter.WriteBytes(buffer, offset, AuthVerifier); + + return buffer; + } + + } +} diff --git a/SMBLibrary/RPC/PDU/FaultPDU.cs b/SMBLibrary/RPC/PDU/FaultPDU.cs new file mode 100644 index 0000000..07df1d1 --- /dev/null +++ b/SMBLibrary/RPC/PDU/FaultPDU.cs @@ -0,0 +1,66 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// rpcconn_fault_hdr_t + /// + public class FaultPDU : RPCPDU + { + public uint AllocationHint; + public ushort ContextID; + public byte CancelCount; + public byte Reserved; + public uint Status; + public uint Reserved2; + public byte[] Data; + public byte[] AuthVerifier; + + public FaultPDU() : base() + { + PacketType = PacketTypeName.Fault; + AuthVerifier = new byte[0]; + } + + public FaultPDU(byte[] buffer) : base(buffer) + { + int offset = RPCPDU.CommonFieldsLength; + AllocationHint = LittleEndianReader.ReadUInt32(buffer, ref offset); + ContextID = LittleEndianReader.ReadUInt16(buffer, ref offset); + CancelCount = ByteReader.ReadByte(buffer, ref offset); + Reserved = ByteReader.ReadByte(buffer, ref offset); + Status = LittleEndianReader.ReadUInt32(buffer, ref offset); + Reserved2 = LittleEndianReader.ReadUInt32(buffer, ref offset); + int dataLength = FragmentLength - AuthLength - offset; + Data = ByteReader.ReadBytes(buffer, ref offset, dataLength); + AuthVerifier = ByteReader.ReadBytes(buffer, offset, AuthLength); + } + + public override byte[] GetBytes() + { + AuthLength = (ushort)AuthVerifier.Length; + FragmentLength = (ushort)(RPCPDU.CommonFieldsLength + 16 + Data.Length + AuthVerifier.Length); + byte[] buffer = new byte[FragmentLength]; + WriteCommonFieldsBytes(buffer); + int offset = RPCPDU.CommonFieldsLength; + LittleEndianWriter.WriteUInt32(buffer, ref offset, AllocationHint); + LittleEndianWriter.WriteUInt16(buffer, ref offset, ContextID); + ByteWriter.WriteByte(buffer, ref offset, CancelCount); + ByteWriter.WriteByte(buffer, ref offset, Reserved); + LittleEndianWriter.WriteUInt32(buffer, ref offset, Status); + LittleEndianWriter.WriteUInt32(buffer, ref offset, Reserved2); + ByteWriter.WriteBytes(buffer, ref offset, Data); + ByteWriter.WriteBytes(buffer, ref offset, AuthVerifier); + return buffer; + } + } +} diff --git a/SMBLibrary/RPC/PDU/RPCPDU.cs b/SMBLibrary/RPC/PDU/RPCPDU.cs new file mode 100644 index 0000000..50cafb5 --- /dev/null +++ b/SMBLibrary/RPC/PDU/RPCPDU.cs @@ -0,0 +1,83 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// See DCE 1.1: Remote Procedure Call, Chapter 12.6 - Connection-oriented RPC PDUs + /// + public abstract class RPCPDU + { + public const int CommonFieldsLength = 16; + + // The common header fields, which appear in all (connection oriented) PDU types: + public byte VersionMajor; // rpc_vers + public byte VersionMinor; // rpc_vers_minor + public PacketTypeName PacketType; + public PacketFlags Flags; + public DataRepresentationFormat DataRepresentation; + public ushort FragmentLength; + public ushort AuthLength; + public uint CallID; + + public RPCPDU() + { + VersionMajor = 5; + VersionMinor = 0; + } + + public RPCPDU(byte[] buffer) + { + VersionMajor = ByteReader.ReadByte(buffer, 0); + VersionMinor = ByteReader.ReadByte(buffer, 1); + PacketType = (PacketTypeName)ByteReader.ReadByte(buffer, 2); + Flags = (PacketFlags)ByteReader.ReadByte(buffer, 3); + DataRepresentation = new DataRepresentationFormat(buffer, 4); + FragmentLength = LittleEndianConverter.ToUInt16(buffer, 8); + AuthLength = LittleEndianConverter.ToUInt16(buffer, 10); + CallID = LittleEndianConverter.ToUInt32(buffer, 12); + } + + public abstract byte[] GetBytes(); + + public void WriteCommonFieldsBytes(byte[] buffer) + { + ByteWriter.WriteByte(buffer, 0, VersionMajor); + ByteWriter.WriteByte(buffer, 1, VersionMinor); + ByteWriter.WriteByte(buffer, 2, (byte)PacketType); + ByteWriter.WriteByte(buffer, 3, (byte)Flags); + DataRepresentation.WriteBytes(buffer, 4); + LittleEndianWriter.WriteUInt16(buffer, 8, FragmentLength); + LittleEndianWriter.WriteUInt16(buffer, 10, AuthLength); + LittleEndianWriter.WriteUInt32(buffer, 12, CallID); + } + + public static RPCPDU GetPDU(byte[] buffer) + { + PacketTypeName packetType = (PacketTypeName)ByteReader.ReadByte(buffer, 2); + switch (packetType) + { + case PacketTypeName.Request: + return new RequestPDU(buffer); + case PacketTypeName.Response: + return new ResponsePDU(buffer); + case PacketTypeName.Fault: + return new FaultPDU(buffer); + case PacketTypeName.Bind: + return new BindPDU(buffer); + case PacketTypeName.BindAck: + return new BindAckPDU(buffer); + default: + throw new NotImplementedException(); + } + } + } +} diff --git a/SMBLibrary/RPC/PDU/RequestPDU.cs b/SMBLibrary/RPC/PDU/RequestPDU.cs new file mode 100644 index 0000000..58d3f06 --- /dev/null +++ b/SMBLibrary/RPC/PDU/RequestPDU.cs @@ -0,0 +1,72 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// rpcconn_request_hdr_t + /// + public class RequestPDU : RPCPDU + { + public uint AllocationHint; // alloc_hint + public ushort ContextID; + public ushort OpNum; + public Guid ObjectGuid; // Optional field + public byte[] Data; + public byte[] AuthVerifier; + + public RequestPDU() : base() + { + PacketType = PacketTypeName.Request; + AuthVerifier = new byte[0]; + } + + public RequestPDU(byte[] buffer) : base(buffer) + { + int offset = RPCPDU.CommonFieldsLength; + AllocationHint = LittleEndianReader.ReadUInt32(buffer, ref offset); + ContextID = LittleEndianReader.ReadUInt16(buffer, ref offset); + OpNum = LittleEndianReader.ReadUInt16(buffer, ref offset); + if ((Flags & PacketFlags.ObjectUUID) > 0) + { + ObjectGuid = LittleEndianReader.ReadGuid(buffer, ref offset); + } + int dataLength = FragmentLength - AuthLength - offset; + Data = ByteReader.ReadBytes(buffer, ref offset, dataLength); + AuthVerifier = ByteReader.ReadBytes(buffer, offset, AuthLength); + } + + public override byte[] GetBytes() + { + AuthLength = (ushort)AuthVerifier.Length; + FragmentLength = (ushort)(RPCPDU.CommonFieldsLength + 8 + Data.Length + AuthVerifier.Length); + if ((Flags & PacketFlags.ObjectUUID) > 0) + { + FragmentLength += 16; + } + byte[] buffer = new byte[FragmentLength]; + WriteCommonFieldsBytes(buffer); + int offset = RPCPDU.CommonFieldsLength; + LittleEndianWriter.WriteUInt32(buffer, ref offset, AllocationHint); + LittleEndianWriter.WriteUInt16(buffer, ref offset, ContextID); + LittleEndianWriter.WriteUInt16(buffer, ref offset, OpNum); + if ((Flags & PacketFlags.ObjectUUID) > 0) + { + LittleEndianWriter.WriteGuidBytes(buffer, ref offset, ObjectGuid); + } + ByteWriter.WriteBytes(buffer, ref offset, Data); + ByteWriter.WriteBytes(buffer, ref offset, AuthVerifier); + return buffer; + } + + + } +} diff --git a/SMBLibrary/RPC/PDU/ResponsePDU.cs b/SMBLibrary/RPC/PDU/ResponsePDU.cs new file mode 100644 index 0000000..9c94308 --- /dev/null +++ b/SMBLibrary/RPC/PDU/ResponsePDU.cs @@ -0,0 +1,62 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// rpcconn_response_hdr_t + /// + public class ResponsePDU : RPCPDU + { + private uint AllocationHint; + public ushort ContextID; + public byte CancelCount; + public byte Reserved; + public byte[] Data; + public byte[] AuthVerifier; + + public ResponsePDU() : base() + { + PacketType = PacketTypeName.Response; + AuthVerifier = new byte[0]; + } + + public ResponsePDU(byte[] buffer) : base(buffer) + { + int offset = RPCPDU.CommonFieldsLength; + AllocationHint = LittleEndianReader.ReadUInt32(buffer, ref offset); + ContextID = LittleEndianReader.ReadUInt16(buffer, ref offset); + CancelCount = ByteReader.ReadByte(buffer, ref offset); + Reserved = ByteReader.ReadByte(buffer, ref offset); + int dataLength = FragmentLength - AuthLength - offset; + Data = ByteReader.ReadBytes(buffer, ref offset, dataLength); + AuthVerifier = ByteReader.ReadBytes(buffer, offset, AuthLength); + } + + public override byte[] GetBytes() + { + AuthLength = (ushort)AuthVerifier.Length; + FragmentLength = (ushort)(RPCPDU.CommonFieldsLength + 8 + Data.Length + AuthVerifier.Length); + AllocationHint = (ushort)Data.Length; + + byte[] buffer = new byte[FragmentLength]; + WriteCommonFieldsBytes(buffer); + int offset = RPCPDU.CommonFieldsLength; + LittleEndianWriter.WriteUInt32(buffer, ref offset, AllocationHint); + LittleEndianWriter.WriteUInt16(buffer, ref offset, ContextID); + ByteWriter.WriteByte(buffer, ref offset, CancelCount); + ByteWriter.WriteByte(buffer, ref offset, Reserved); + ByteWriter.WriteBytes(buffer, ref offset, Data); + ByteWriter.WriteBytes(buffer, ref offset, AuthVerifier); + return buffer; + } + } +} diff --git a/SMBLibrary/RPC/RPCHelper.cs b/SMBLibrary/RPC/RPCHelper.cs new file mode 100644 index 0000000..ce2078b --- /dev/null +++ b/SMBLibrary/RPC/RPCHelper.cs @@ -0,0 +1,46 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + public class RPCHelper + { + /// + /// Read port_any_t string structure + /// + public static string ReadPortAddress(byte[] buffer, int offset) + { + ushort length = LittleEndianConverter.ToUInt16(buffer, offset + 0); + // The length includes the C NULL string termination + return ByteReader.ReadAnsiString(buffer, offset + 2, length - 1); + } + + public static string ReadPortAddress(byte[] buffer, ref int offset) + { + string result = ReadPortAddress(buffer, offset); + offset += result.Length + 3; + return result; + } + + public static void WritePortAddress(byte[] buffer, int offset, string value) + { + ushort length = (ushort)(value.Length + 1); + LittleEndianWriter.WriteUInt16(buffer, offset + 0, length); + ByteWriter.WriteNullTerminatedAnsiString(buffer, offset + 2, value); + } + + public static void WritePortAddress(byte[] buffer, ref int offset, string value) + { + WritePortAddress(buffer, offset, value); + offset += value.Length + 3; + } + } +} diff --git a/SMBLibrary/RPC/Structures/ContextElement.cs b/SMBLibrary/RPC/Structures/ContextElement.cs new file mode 100644 index 0000000..d76588a --- /dev/null +++ b/SMBLibrary/RPC/Structures/ContextElement.cs @@ -0,0 +1,65 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// p_cont_elem_t + /// + public class ContextElement // Presentation Context Element + { + public ushort ContextID; + //byte NumberOfTransferSyntaxItems; + public byte Reserved; + public SyntaxID AbstractSyntax; + public List TransferSyntaxList = new List(); + + public ContextElement(byte[] buffer, int offset) + { + ContextID = LittleEndianConverter.ToUInt16(buffer, offset + 0); + byte numberOfTransferSyntaxItems = ByteReader.ReadByte(buffer, offset + 2); + Reserved = ByteReader.ReadByte(buffer, offset + 3); + AbstractSyntax = new SyntaxID(buffer, offset + 4); + offset += 4 + SyntaxID.Length; + for (int index = 0; index < numberOfTransferSyntaxItems; index++) + { + SyntaxID syntax = new SyntaxID(buffer, offset); + TransferSyntaxList.Add(syntax); + offset += SyntaxID.Length; + } + } + + public void WriteBytes(byte[] buffer, int offset) + { + byte numberOfTransferSyntaxItems = (byte)TransferSyntaxList.Count; + + LittleEndianWriter.WriteUInt16(buffer, offset + 0, ContextID); + ByteWriter.WriteByte(buffer, offset + 2, numberOfTransferSyntaxItems); + ByteWriter.WriteByte(buffer, offset + 3, Reserved); + AbstractSyntax.WriteBytes(buffer, offset + 4); + offset += 4 + SyntaxID.Length; + + for (int index = 0; index < numberOfTransferSyntaxItems; index++) + { + TransferSyntaxList[index].WriteBytes(buffer, offset); + offset += SyntaxID.Length; + } + } + + public int Length + { + get + { + return 4 + SyntaxID.Length * (TransferSyntaxList.Count + 1); + } + } + } +} diff --git a/SMBLibrary/RPC/Structures/ContextList.cs b/SMBLibrary/RPC/Structures/ContextList.cs new file mode 100644 index 0000000..492f4d3 --- /dev/null +++ b/SMBLibrary/RPC/Structures/ContextList.cs @@ -0,0 +1,76 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// p_cont_list_t + /// Presentation Context List + /// + public class ContextList : List + { + //byte NumberOfContextElements; + public byte Reserved1; + public ushort Reserved2; + + public ContextList() : base() + { + } + + public ContextList(byte[] buffer, int offset) : base() + { + byte numberOfContextElements = ByteReader.ReadByte(buffer, offset + 0); + Reserved1 = ByteReader.ReadByte(buffer, offset + 1); + Reserved2 = LittleEndianConverter.ToUInt16(buffer, offset + 2); + offset += 4; + for (int index = 0; index < numberOfContextElements; index++) + { + ContextElement element = new ContextElement(buffer, offset); + this.Add(element); + offset += element.Length; + } + } + + public void WriteBytes(byte[] buffer, int offset) + { + byte numberOfContextElements = (byte)this.Count; + + ByteWriter.WriteByte(buffer, offset + 0, numberOfContextElements); + ByteWriter.WriteByte(buffer, offset + 1, Reserved1); + LittleEndianWriter.WriteUInt16(buffer, offset + 2, Reserved2); + offset += 4; + for (int index = 0; index < numberOfContextElements; index++) + { + this[index].WriteBytes(buffer, offset); + offset += this[index].Length; + } + } + + public void WriteBytes(byte[] buffer, ref int offset) + { + WriteBytes(buffer, offset); + offset += this.Length; + } + + public int Length + { + get + { + int length = 4; + for (int index = 0; index < this.Count; index++) + { + length += this[index].Length; + } + return length; + } + } + } +} diff --git a/SMBLibrary/RPC/Structures/ResultElement.cs b/SMBLibrary/RPC/Structures/ResultElement.cs new file mode 100644 index 0000000..1f74311 --- /dev/null +++ b/SMBLibrary/RPC/Structures/ResultElement.cs @@ -0,0 +1,39 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// p_result_t + /// + public struct ResultElement + { + public const int Length = 24; + + public NegotiationResult Result; + public RejectionReason Reason; + public SyntaxID TransferSyntax; + + public ResultElement(byte[] buffer, int offset) + { + Result = (NegotiationResult)LittleEndianConverter.ToUInt16(buffer, offset + 0); + Reason = (RejectionReason)LittleEndianConverter.ToUInt16(buffer, offset + 2); + TransferSyntax = new SyntaxID(buffer, offset + 4); + } + + public void WriteBytes(byte[] buffer, int offset) + { + LittleEndianWriter.WriteUInt16(buffer, offset + 0, (ushort)Result); + LittleEndianWriter.WriteUInt16(buffer, offset + 2, (ushort)Reason); + TransferSyntax.WriteBytes(buffer, offset + 4); + } + } +} diff --git a/SMBLibrary/RPC/Structures/ResultList.cs b/SMBLibrary/RPC/Structures/ResultList.cs new file mode 100644 index 0000000..2315f19 --- /dev/null +++ b/SMBLibrary/RPC/Structures/ResultList.cs @@ -0,0 +1,69 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// p_result_list_t + /// + public class ResultList : List + { + //byte NumberOfResults; + public byte Reserved; + public ushort Reserved2; + + public ResultList() : base() + {} + + public ResultList(byte[] buffer, int offset) : base() + { + byte numberOfResults = ByteReader.ReadByte(buffer, offset + 0); + Reserved = ByteReader.ReadByte(buffer, offset + 1); + Reserved2 = LittleEndianConverter.ToUInt16(buffer, offset + 2); + offset += 4; + for (int index = 0; index < numberOfResults; index++) + { + ResultElement element = new ResultElement(buffer, offset); + this.Add(element); + offset += ResultElement.Length; + } + } + + public void WriteBytes(byte[] buffer, int offset) + { + byte numberOfResults = (byte)this.Count; + + ByteWriter.WriteByte(buffer, offset + 0, numberOfResults); + ByteWriter.WriteByte(buffer, offset + 1, Reserved); + LittleEndianWriter.WriteUInt16(buffer, offset + 2, Reserved2); + offset += 4; + for (int index = 0; index < numberOfResults; index++) + { + this[index].WriteBytes(buffer, offset); + offset += ResultElement.Length; + } + } + + public void WriteBytes(byte[] buffer, ref int offset) + { + WriteBytes(buffer, offset); + offset += this.Length; + } + + public int Length + { + get + { + return 4 + ResultElement.Length * this.Count; + } + } + } +} diff --git a/SMBLibrary/RPC/Structures/SyntaxID.cs b/SMBLibrary/RPC/Structures/SyntaxID.cs new file mode 100644 index 0000000..ccb5a1c --- /dev/null +++ b/SMBLibrary/RPC/Structures/SyntaxID.cs @@ -0,0 +1,56 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.RPC +{ + /// + /// p_syntax_id_t + /// + public struct SyntaxID + { + public const int Length = 20; + + public Guid InterfaceUUID; // if_uuid + public uint InterfaceVersion; // if_version + + public SyntaxID(Guid interfaceUUID, uint interfaceVersion) + { + InterfaceUUID = interfaceUUID; + InterfaceVersion = interfaceVersion; + } + + public SyntaxID(byte[] buffer, int offset) + { + InterfaceUUID = LittleEndianConverter.ToGuid(buffer, offset + 0); + InterfaceVersion = LittleEndianConverter.ToUInt32(buffer, offset + 16); + } + + public void WriteBytes(byte[] buffer, int offset) + { + LittleEndianWriter.WriteGuidBytes(buffer, offset + 0, InterfaceUUID); + LittleEndianWriter.WriteUInt32(buffer, offset + 16, InterfaceVersion); + } + + public override bool Equals(object obj) + { + if (obj is SyntaxID) + { + return this.InterfaceUUID.Equals(((SyntaxID)obj).InterfaceUUID) && this.InterfaceVersion.Equals(((SyntaxID)obj).InterfaceVersion); + } + return false; + } + + public override int GetHashCode() + { + return InterfaceUUID.GetHashCode() * InterfaceVersion.GetHashCode(); + } + } +} diff --git a/SMBLibrary/Readme.txt b/SMBLibrary/Readme.txt new file mode 100644 index 0000000..3eb3fd5 --- /dev/null +++ b/SMBLibrary/Readme.txt @@ -0,0 +1,43 @@ +About SMBLibrary: +================= +SMBLibrary is an open-source C# SMB 1.0/CIFS 1.0 server implementation. +SMBLibrary gives .NET developers an easy way to share a directory / file system / virtual file system, with any operating system that supports the SMB protocol. +SMBLibrary shares can be accessed from any Windows version since Windows NT 4.0. + +Supported SMB / CIFS transport methods: +======================================= +NetBIOS over TCP (port 139) +Direct TCP hosting (port 445) + +Notes: +------ +1. Windows bind port 139 on a per-adapter basis, while port 445 is bound globally. +This means that you can't use direct TCP hosting without disabling Windows File and Printer Sharing server completely. +However, NetBIOS over TCP is almost identical, and for this reason, it's recommended to use port 139. + +2. To free port 139 for a given adapter, go to 'Internet Protocol (TCP/IP) Properties' > Advanced > WINS, +and select 'Disable NetBIOS over TCP/IP'. in addition you need to uncheck 'File and Printer Sharing for Microsoft Networks'. + +3. It's important to note that disabling NetBIOS over TCP/IP will also disable NetBIOS name service for that adapter (a.k.a. WINS), +This service uses UDP port 137. SMBLibrary offers a name service of its own. + +4. You can install a virtual network adapter driver for Windows to be used solely with SMBLibrary: +- You can install the 'Microsoft Loopback adapter' and use it for server-only communication with SMBLibrary. +- A limited alternative is 'OpenVPN TAP-Windows Adapter' that can be used for client communication with SMBLibrary, +However, you will have to configure this adapter to use a separate network segment. +The driver installation can be downloaded from: https://openvpn.net/index.php/open-source/downloads.html +To get started, go to Adapter properties > 'Advanced' and set 'Media Status' to 'Always Connected'. + +5. The differences between 'Direct TCP hosting' and 'NetBIOS over TCP' are: +- A 'session request' packet is initiating the NBT connection. +- A 'keep alive' packet is sent from time to time over NBT connections. + +Using SMBLibrary: +================= +Any directory / filesystem / object you wish to share must implement the IFileSystem interface. +You can share anything from actual directories to custom objects, as long as they expose a directory structure. + +Contact: +======== +If you have any question, feel free to contact me. +Tal Aloni \ No newline at end of file diff --git a/SMBLibrary/RevisionHistory.txt b/SMBLibrary/RevisionHistory.txt new file mode 100644 index 0000000..2d3ef06 --- /dev/null +++ b/SMBLibrary/RevisionHistory.txt @@ -0,0 +1,14 @@ +Revision History: +----------------- +1.0.0 - Initial release. + +1.0.1 - Better handling of invalid read / write operations. + +1.0.2 - Improved documentation and updated SMB_COM_TRANSACTION2 request (Technical Specifications Errata was found). + +1.0.3 - Search handles (SID) are now properly closed. + +1.0.4 - Added support for IPv6. + +1.0.5 - bugfix: SMB_COM_OPEN_ANDX was not returning the requested response format. + File buffering logic logic was moved to a separate class. diff --git a/SMBLibrary/SMB1/EnumStructures/NamedPipeStatus.cs b/SMBLibrary/SMB1/EnumStructures/NamedPipeStatus.cs new file mode 100644 index 0000000..c3eadec --- /dev/null +++ b/SMBLibrary/SMB1/EnumStructures/NamedPipeStatus.cs @@ -0,0 +1,95 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; + +namespace SMBLibrary.SMB1 +{ + public enum ReadMode : byte + { + ByteMode = 0x00, + MessageMode = 0x01, + } + + public enum NamedPipeType : byte + { + ByteNamedPipe = 0x00, + MessageNodePipe = 0x01, + } + + public enum Endpoint : byte + { + ClientSideEnd = 0x00, + ServerSideEnd = 0x01, + } + + public enum NonBlocking : byte + { + Block = 0x00, + DoNotBlock = 0x01, + } + + /// + /// SMB_NMPIPE_STATUS + /// + public struct NamedPipeStatus // ushort + { + public byte ICount; + public ReadMode ReadMode; + public NamedPipeType NamedPipeType; + public Endpoint Endpoint; + public NonBlocking NonBlocking; + + public NamedPipeStatus(byte[] buffer, int offset) + { + ICount = buffer[offset]; + ReadMode = (ReadMode)(buffer[offset + 1] & 0x03); + NamedPipeType = (NamedPipeType)((buffer[offset + 1] & 0x0C) >> 2); + Endpoint = (Endpoint)((buffer[offset + 1] & 0x40) >> 6); + NonBlocking = (NonBlocking)((buffer[offset + 1] & 0x80) >> 7); + } + + public NamedPipeStatus(ushort value) + { + ICount = (byte)(value & 0xFF); + ReadMode = (ReadMode)((value & 0x0300) >> 8); + NamedPipeType = (NamedPipeType)((value & 0x0C00) >> 10); + Endpoint = (Endpoint)((value & 0x4000) >> 14); + NonBlocking = (NonBlocking)((value & 0x80) >> 15); + } + + public void WriteBytes(byte[] buffer, int offset) + { + buffer[offset + 0] = ICount; + buffer[offset + 1] = (byte)((byte)ReadMode & 0x03); + buffer[offset + 1] |= (byte)(((byte)NamedPipeType << 2) & 0x0C); + buffer[offset + 1] |= (byte)(((byte)Endpoint << 6) & 0x40); + buffer[offset + 1] |= (byte)(((byte)NonBlocking << 7) & 0x80); + } + + public void WriteBytes(byte[] buffer, ref int offset) + { + WriteBytes(buffer, offset); + offset += 2; + } + + public ushort ToUInt16() + { + ushort result = ICount; + result |= (ushort)(((byte)ReadMode << 8) & 0x0300); + result |= (ushort)(((byte)NamedPipeType << 10) & 0x0C00); + result |= (ushort)(((byte)Endpoint << 14) & 0x4000); + result |= (ushort)(((byte)NonBlocking << 15) & 0x8000); + return result; + } + + public static NamedPipeStatus Read(byte[] buffer, ref int offset) + { + offset += 2; + return new NamedPipeStatus(buffer, offset - 2); + } + } +} diff --git a/SMBLibrary/SMB1/EnumStructures/OpenResults.cs b/SMBLibrary/SMB1/EnumStructures/OpenResults.cs new file mode 100644 index 0000000..12f0e07 --- /dev/null +++ b/SMBLibrary/SMB1/EnumStructures/OpenResults.cs @@ -0,0 +1,50 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public struct OpenResults // 2 bytes + { + public OpenResult OpenResult; + public bool OpLockGranted; + + public OpenResults(byte[] buffer, int offset) + { + OpenResult = (OpenResult)(buffer[offset] & 0x3); + OpLockGranted = (buffer[offset + 1] & 0x80) > 0; + } + + public void WriteBytes(byte[] buffer, int offset) + { + buffer[0] = (byte)OpenResult; + if (OpLockGranted) + { + buffer[1] = 0x80; + } + else + { + buffer[1] = 0x00; + } + } + + public void WriteBytes(byte[] buffer, ref int offset) + { + WriteBytes(buffer, offset); + offset += 2; + } + + public static OpenResults Read(byte[] buffer, ref int offset) + { + offset += 2; + return new OpenResults(buffer, offset - 2); + } + } +} diff --git a/SMBLibrary/SMB1/Enums/CommandName.cs b/SMBLibrary/SMB1/Enums/CommandName.cs new file mode 100644 index 0000000..a5775d9 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/CommandName.cs @@ -0,0 +1,40 @@ + +namespace SMBLibrary.SMB1 +{ + public enum CommandName : byte + { + SMB_COM_CREATE_DIRECTORY = 0x00, + SMB_COM_DELETE_DIRECTORY = 0x01, + 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_CHECK_DIRECTORY = 0x10, + SMB_COM_WRITE_RAW = 0x1D, + SMB_COM_WRITE_COMPLETE = 0x20, // Write RAW final response + SMB_COM_SET_INFORMATION2 = 0x22, + SMB_COM_LOCKING_ANDX = 0x24, + SMB_COM_TRANSACTION = 0x25, + SMB_COM_TRANSACTION_SECONDARY = 0x26, + SMB_COM_ECHO = 0x2B, + SMB_COM_OPEN_ANDX = 0x2D, + SMB_COM_READ_ANDX = 0x2E, + SMB_COM_WRITE_ANDX = 0x2F, + SMB_COM_TRANSACTION2 = 0x32, + SMB_COM_TRANSACTION2_SECONDARY = 0x33, + SMB_COM_FIND_CLOSE2 = 0x34, + 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_NT_TRANSACT = 0xA0, + SMB_COM_NT_TRANSACT_SECONDARY = 0xA1, + SMB_COM_NT_CREATE_ANDX = 0xA2, + SMB_COM_NO_ANDX_COMMAND = 0xFF, + } +} diff --git a/SMBLibrary/SMB1/Enums/ExtendedFileAttributes.cs b/SMBLibrary/SMB1/Enums/ExtendedFileAttributes.cs new file mode 100644 index 0000000..b6a5e07 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/ExtendedFileAttributes.cs @@ -0,0 +1,36 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_EXT_FILE_ATTR + /// + [Flags] + public enum ExtendedFileAttributes : uint + { + Readonly = 0x001, // ATTR_READONLY + Hidden = 0x0002, // ATTR_HIDDEN + System = 0x0004, // ATTR_SYSTEM + Directory = 0x0010, // ATTR_DIRECTORY + Archive = 0x0020, // ATTR_ARCHIVE + + /// + /// The file has no other attributes set. This attribute is valid only if used alone. + /// + Normal = 0x0080, // ATTR_NORMAL + Temporary = 0x0100, // ATTR_TEMPORARY + Sparse = 0x0200, // ATTR_SPARSE, SMB 1.0 Addition + ReparsePoint = 0x0400, // ATTR_REPARSE_POINT, SMB 1.0 Addition + Compressed = 0x0800, // ATTR_COMPRESSED + Offline = 0x1000, // ATTR_OFFLINE, SMB 1.0 Addition + NotIndexed = 0x2000, // ATTR_NOT_CONTENT_INDEXED, SMB 1.0 Addition + Encrypted = 0x4000, // ATTR_ENCRYPTED, SMB 1.0 Addition + PosixSemantics = 0x01000000, // POSIX_SEMANTICS + BackupSemantics = 0x02000000, // BACKUP_SEMANTICS + DeleteOnClose = 0x04000000, // DELETE_ON_CLOSE + SequentialScan = 0x08000000, // SEQUENTIAL_SCAN + RandomAccess = 0x10000000, // RANDOM_ACCESS + NoBuffering = 0x10000000, // NO_BUFFERING + WriteThrough = 0x80000000, // WRITE_THROUGH + } +} diff --git a/SMBLibrary/SMB1/Enums/FileAttributes.cs b/SMBLibrary/SMB1/Enums/FileAttributes.cs new file mode 100644 index 0000000..ddc3a49 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/FileAttributes.cs @@ -0,0 +1,24 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_FILE_ATTRIBUTES + /// + [Flags] + public enum FileAttributes : ushort + { + Normal = 0x0000, // SMB_FILE_ATTRIBUTE_NORMAL + ReadOnly = 0x0001, // SMB_FILE_ATTRIBUTE_READONLY + Hidden = 0x0002, // SMB_FILE_ATTRIBUTE_HIDDEN + System = 0x0004, // SMB_FILE_ATTRIBUTE_SYSTEM + Volume = 0x0008, // SMB_FILE_ATTRIBUTE_VOLUME + Directory = 0x0010, // SMB_FILE_ATTRIBUTE_DIRECTORY + Archive = 0x0020, // SMB_FILE_ATTRIBUTE_ARCHIVE + SearchReadOnly = 0x0100, // SMB_SEARCH_ATTRIBUTE_READONLY + SearchHidden = 0x0200, // SMB_SEARCH_ATTRIBUTE_HIDDEN + SearchSystem = 0x0400, // SMB_SEARCH_ATTRIBUTE_SYSTEM + SearchDirectory = 0x1000, // SMB_SEARCH_ATTRIBUTE_DIRECTORY + SearchArchive = 0x2000, // SMB_SEARCH_ATTRIBUTE_ARCHIVE + } +} diff --git a/SMBLibrary/SMB1/Enums/HeaderFlags.cs b/SMBLibrary/SMB1/Enums/HeaderFlags.cs new file mode 100644 index 0000000..d1af0f8 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/HeaderFlags.cs @@ -0,0 +1,14 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum HeaderFlags : byte + { + LockAndRead = 0x01, // SMB_FLAGS_LOCK_AND_READ_OK + CaseInsensitive = 0x08, // SMB_FLAGS_CASE_INSENSITIVE + CanonicalizedPaths = 0x10, // SMB_FLAGS_CANONICALIZED_PATHS + Oplock = 0x20, // SMB_FLAGS_OPLOCK + Reply = 0x80, // SMB_FLAGS_REPLY + } +} diff --git a/SMBLibrary/SMB1/Enums/HeaderFlags2.cs b/SMBLibrary/SMB1/Enums/HeaderFlags2.cs new file mode 100644 index 0000000..daf54d8 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/HeaderFlags2.cs @@ -0,0 +1,25 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum HeaderFlags2 : ushort + { + LongNamesAllowed = 0x0001, // SMB_FLAGS2_LONG_NAMES + ExtendedAttributes = 0x0002, // SMB_FLAGS2_EAS + SecuritySignature = 0x0004, // SMB_FLAGS2_SMB_SECURITY_SIGNATURE + CompressedData = 0x0008, // SMB_FLAGS2_COMPRESSED + SecuritySignatureRequired = 0x0010, // SMB_FLAGS2_SMB_SECURITY_SIGNATURE_REQUIRED + LongNameUsed = 0x0040, // SMB_FLAGS2_IS_LONG_NAME + ReparsePath = 0x400, // SMB_FLAGS2_REPARSE_PATH + + /// + /// Indicates that the client or server supports extended security + /// + ExtendedSecurity = 0x0800, // SMB_FLAGS2_EXTENDED_SECURITY + DFS = 0x1000, // SMB_FLAGS2_DFS + ReadIfExecute = 0x2000, // SMB_FLAGS2_PAGING_IO + NTStatusCode = 0x4000, // SMB_FLAGS2_NT_STATUS + Unicode = 0x8000, // SMB_FLAGS2_UNICODE + } +} diff --git a/SMBLibrary/SMB1/Enums/Locking/LockType.cs b/SMBLibrary/SMB1/Enums/Locking/LockType.cs new file mode 100644 index 0000000..5a4a32a --- /dev/null +++ b/SMBLibrary/SMB1/Enums/Locking/LockType.cs @@ -0,0 +1,24 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum LockType : byte + { + READ_WRITE_LOCK = 0x00, + SHARED_LOCK = 0x01, + OPLOCK_RELEASE = 0x02, + CHANGE_LOCKTYPE = 0x04, + + /// + /// Request to cancel all outstanding lock requests for the specified FID and PID. + /// + CANCEL_LOCK = 0x08, + + /// + /// Indicates that the LOCKING_ANDX_RANGE format is the 64-bit file offset version. + /// If this flag is not set, then the LOCKING_ANDX_RANGE format is the 32-bit file offset version + /// + LARGE_FILES = 0x10, + } +} diff --git a/SMBLibrary/SMB1/Enums/NTCreate/CreateDisposition.cs b/SMBLibrary/SMB1/Enums/NTCreate/CreateDisposition.cs new file mode 100644 index 0000000..1800d0c --- /dev/null +++ b/SMBLibrary/SMB1/Enums/NTCreate/CreateDisposition.cs @@ -0,0 +1,45 @@ + +namespace SMBLibrary.SMB1 +{ + public enum CreateDisposition : uint + { + /// + /// If the file already exists, it SHOULD be superseded (overwritten). + /// If it does not already exist, then it SHOULD be created. + /// + FILE_SUPERSEDE = 0x0000, + + /// + /// If the file already exists, it SHOULD be opened rather than created. + /// If the file does not already exist, the operation MUST fail. + /// + FILE_OPEN = 0x0001, + + /// + /// If the file already exists, the operation MUST fail. + /// If the file does not already exist, it SHOULD be created. + /// + FILE_CREATE = 0x0002, + + /// + /// If the file already exists, it SHOULD be opened. + /// If the file does not already exist, then it SHOULD be created. + /// This value is equivalent to (FILE_OPEN | FILE_CREATE). + /// + FILE_OPEN_IF = 0x0003, + + /// + /// If the file already exists, it SHOULD be opened and truncated. + /// If the file does not already exist, the operation MUST fail. + /// The client MUST open the file with at least GENERIC_WRITE access for the command to succeed. + /// + FILE_OVERWRITE = 0x0004, + + /// + /// If the file already exists, it SHOULD be opened and truncated. + /// If the file does not already exist, it SHOULD be created. + /// The client MUST open the file with at least GENERIC_WRITE access. + /// + FILE_OVERWRITE_IF = 0x0005, + } +} diff --git a/SMBLibrary/SMB1/Enums/NTCreate/CreateOptions.cs b/SMBLibrary/SMB1/Enums/NTCreate/CreateOptions.cs new file mode 100644 index 0000000..e53333c --- /dev/null +++ b/SMBLibrary/SMB1/Enums/NTCreate/CreateOptions.cs @@ -0,0 +1,101 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum CreateOptions : uint + { + /// + /// The file being created or opened is a directory file. + /// With this option, the CreateDisposition field MUST be set to FILE_CREATE, FILE_OPEN, or FILE_OPEN_IF. + /// + FILE_DIRECTORY_FILE = 0x0001, + + /// + /// Applications that write data to the file MUST actually transfer the data into the file before any write request is considered complete. + /// If FILE_NO_INTERMEDIATE_BUFFERING is set, the server MUST perform as if FILE_WRITE_THROUGH is set in the create request. + /// + FILE_WRITE_THROUGH = 0x0002, + + /// + /// This option indicates that access to the file can be sequential. + /// The server can use this information to influence its caching and read-ahead strategy for this file. + /// The file MAY in fact be accessed randomly, but the server can optimize its caching and read-ahead policy for sequential access. + /// + FILE_SEQUENTIAL_ONLY = 0x0004, + + /// + /// The file SHOULD NOT be cached or buffered in an internal buffer by the server. + /// This option is incompatible when the FILE_APPEND_DATA bit field is set in the DesiredAccess field. + /// + FILE_NO_INTERMEDIATE_BUFFERING = 0x0008, + + FILE_SYNCHRONOUS_IO_ALERT = 0x0010, + + FILE_SYNCHRONOUS_IO_NONALERT = 0x0020, + + /// + /// If the file being opened is a directory, the server MUST fail the request with STATUS_FILE_IS_A_DIRECTORY + /// + FILE_NON_DIRECTORY_FILE = 0x0040, + + FILE_CREATE_TREE_CONNECTION = 0x0080, + + FILE_COMPLETE_IF_OPLOCKED = 0x0100, + + /// + /// The application that initiated the client's request does not support extended attributes (EAs). + /// If the EAs on an existing file being opened indicate that the caller SHOULD support EAs to correctly interpret the file, the server SHOULD fail this request with STATUS_ACCESS_DENIED. + /// + FILE_NO_EA_KNOWLEDGE = 0x0200, + + FILE_OPEN_FOR_RECOVERY = 0x0400, + + /// + /// Indicates that access to the file can be random. + /// The server MAY use this information to influence its caching and read-ahead strategy for this file. + /// This is a hint to the server that sequential read-ahead operations might not be appropriate on the file. + /// + FILE_RANDOM_ACCESS = 0x0800, + + /// + /// The file SHOULD be automatically deleted when the last open request on this file is closed. + /// When this option is set, the DesiredAccess field MUST include the DELETE flag. + /// This option is often used for temporary files. + /// + FILE_DELETE_ON_CLOSE = 0x1000, + + /// + /// Opens a file based on the FileId. + /// If this option is set, the server MUST fail the request with STATUS_NOT_SUPPORTED in the Status field of the SMB Header in the server response. + /// + FILE_OPEN_BY_FILE_ID = 0x2000, + + /// + /// The file is being opened or created for the purposes of either a backup or a restore operation. + /// Thus, the server can make appropriate checks to ensure that the caller is capable of overriding + /// whatever security checks have been placed on the file to allow a backup or restore operation to occur. + /// The server can check for certain access rights to the file before checking the DesiredAccess field. + /// + FILE_OPEN_FOR_BACKUP_INTENT = 0x4000, + + /// + /// When a new file is created, the file MUST NOT be compressed, even if it is on a compressed volume. + /// The flag MUST be ignored when opening an existing file. + /// + FILE_NO_COMPRESSION = 0x8000, + + FILE_RESERVE_OPFILTER = 0x00100000, + + FILE_OPEN_REPARSE_POINT = 0x00200000, + + /// + /// In a hierarchical storage management environment, this option requests that the file SHOULD NOT be recalled from tertiary storage such as tape. + /// A file recall can take up to several minutes in a hierarchical storage management environment. + /// The clients can specify this option to avoid such delays. + /// + FILE_OPEN_NO_RECALL = 0x00400000, + + FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000, + } +} diff --git a/SMBLibrary/SMB1/Enums/NTCreate/DesiredAccess.cs b/SMBLibrary/SMB1/Enums/NTCreate/DesiredAccess.cs new file mode 100644 index 0000000..2307f92 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/NTCreate/DesiredAccess.cs @@ -0,0 +1,27 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum DesiredAccess : uint + { + FILE_READ_DATA = 0x0001, + FILE_WRITE_DATA = 0x0002, + FILE_APPEND_DATA = 0x0004, + FILE_READ_EA = 0x0008, + FILE_WRITE_EA = 0x0010, + FILE_EXECUTE = 0x0020, + FILE_READ_ATTRIBUTES = 0x0080, + FILE_WRITE_ATTRIBUTES = 0x0100, + DELETE = 0x00010000, + READ_CONTROL = 0x00020000, + WRITE_DAC = 0x00040000, + WRITE_OWNER = 0x00080000, + SYNCHRONIZE = 0x00100000, + ACCESS_SYSTEM_SECURITY = 0x01000000, + MAXIMUM_ALLOWED = 0x02000000, + GENERIC_ALL = 0x10000000, + GENERIC_EXECUTE = 0x20000000, + GENERIC_WRITE = 0x40000000, + } +} diff --git a/SMBLibrary/SMB1/Enums/NTCreate/FileStatus.cs b/SMBLibrary/SMB1/Enums/NTCreate/FileStatus.cs new file mode 100644 index 0000000..2a1edf5 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/NTCreate/FileStatus.cs @@ -0,0 +1,12 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum FileStatus : ushort + { + NO_EAS = 0x01, + NO_SUBSTREAMS = 0x02, + NO_REPARSETAG = 0x04, + } +} diff --git a/SMBLibrary/SMB1/Enums/NTCreate/ImpersonationLevel.cs b/SMBLibrary/SMB1/Enums/NTCreate/ImpersonationLevel.cs new file mode 100644 index 0000000..63b0ddc --- /dev/null +++ b/SMBLibrary/SMB1/Enums/NTCreate/ImpersonationLevel.cs @@ -0,0 +1,11 @@ + +namespace SMBLibrary.SMB1 +{ + public enum ImpersonationLevel : uint + { + SEC_ANONYMOUS = 0x00, + SEC_IDENTIFY = 0x01, + SEC_IMPERSONATE = 0x02, + SECURITY_DELEGATION = 0x04, // SMB 1.0 addition + } +} diff --git a/SMBLibrary/SMB1/Enums/NTCreate/NTCreateFlags.cs b/SMBLibrary/SMB1/Enums/NTCreate/NTCreateFlags.cs new file mode 100644 index 0000000..1a63808 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/NTCreate/NTCreateFlags.cs @@ -0,0 +1,20 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum NTCreateFlags : uint + { + /// + /// If set, the client requests an exclusive OpLock. + /// + NT_CREATE_REQUEST_OPLOCK = 0x0002, + + /// + /// If set, the client requests an exclusive batch OpLock. + /// + NT_CREATE_REQUEST_OPBATCH = 0x0004, + NT_CREATE_OPEN_TARGET_DIR = 0x0008, + NT_CREATE_REQUEST_EXTENDED_RESPONSE = 0x0010, // SMB 1.0 addition + } +} diff --git a/SMBLibrary/SMB1/Enums/NTCreate/OpLockLevel.cs b/SMBLibrary/SMB1/Enums/NTCreate/OpLockLevel.cs new file mode 100644 index 0000000..017731b --- /dev/null +++ b/SMBLibrary/SMB1/Enums/NTCreate/OpLockLevel.cs @@ -0,0 +1,11 @@ + +namespace SMBLibrary.SMB1 +{ + public enum OpLockLevel : byte + { + NoOpLockGranted = 0x00, + ExclusiveOpLockGranted = 0x01, + BatchOpLockGranted = 0x02, + Level2OpLockGranted = 0x03, + } +} diff --git a/SMBLibrary/SMB1/Enums/NTCreate/SecurityFlags.cs b/SMBLibrary/SMB1/Enums/NTCreate/SecurityFlags.cs new file mode 100644 index 0000000..74d6633 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/NTCreate/SecurityFlags.cs @@ -0,0 +1,10 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + public enum SecurityFlags : byte + { + SMB_SECURITY_CONTEXT_TRACKING = 0x01, + SMB_SECURITY_EFFECTIVE_ONLY = 0x02, + } +} diff --git a/SMBLibrary/SMB1/Enums/NTCreate/ShareAccess.cs b/SMBLibrary/SMB1/Enums/NTCreate/ShareAccess.cs new file mode 100644 index 0000000..05e1eed --- /dev/null +++ b/SMBLibrary/SMB1/Enums/NTCreate/ShareAccess.cs @@ -0,0 +1,15 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + /// + /// No bits set = Prevents the file from being shared + /// + [Flags] + public enum ShareAccess : uint + { + FILE_SHARE_READ = 0x0001, + FILE_SHARE_WRITE = 0x0002, + FILE_SHARE_DELETE = 0x0004, + } +} diff --git a/SMBLibrary/SMB1/Enums/Negotiate/SecurityMode.cs b/SMBLibrary/SMB1/Enums/Negotiate/SecurityMode.cs new file mode 100644 index 0000000..e23bc49 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/Negotiate/SecurityMode.cs @@ -0,0 +1,23 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum SecurityMode : byte + { + /// + /// If clear, the server supports only Share Level access control. + /// If set, the server supports only User Level access control. + /// + UserSecurityMode = 0x01, // NEGOTIATE_USER_SECURITY + + /// + /// If clear, the server supports only plaintext password authentication. + /// If set, the server supports challenge/response authentication. + /// Note: Windows 2000 and above do not support plain-text passwords by default. + /// + EncryptPasswords = 0x02, // NEGOTIATE_ENCRYPT_PASSWORDS + SecuritySignaturesEnabled = 0x04, // NEGOTIATE_SECURITY_SIGNATURES_ENABLED + SecuritySignaturesRequired = 0x08, // NEGOTIATE_SECURITY_SIGNATURES_REQUIRED + } +} diff --git a/SMBLibrary/SMB1/Enums/Negotiate/ServerCapabilities.cs b/SMBLibrary/SMB1/Enums/Negotiate/ServerCapabilities.cs new file mode 100644 index 0000000..88c5526 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/Negotiate/ServerCapabilities.cs @@ -0,0 +1,31 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum ServerCapabilities : uint + { + RawMode = 0x00000001, // CAP_RAW_MODE + MPXMode = 0x00000002, // SMB_COM_READ_MPX + Unicode = 0x00000004, // CAP_UNICODE + LargeFiles = 0x00000008, // CAP_LARGE_FILES + NTSMB = 0x00000010, // CAP_NT_SMBS + RpcRemoteApi = 0x00000020, // CAP_RPC_REMOTE_APIS + NTStatusCode = 0x00000040, // CAP_STATUS32 + Level2Oplocks = 0x00000080, // CAP_LEVEL_II_OPLOCKS + LockAndRead = 0x00000100, // CAP_LOCK_AND_READ + NTFind = 0x00000200, // CAP_NT_FIND + DFS = 0x00001000, // CAP_DFS + InfoLevelPassthrough = 0x00002000, // CAP_INFOLEVEL_PASSTHRU + LargeRead = 0x00004000, // CAP_LARGE_READX + LargeWrite = 0x00008000, // CAP_LARGE_WRITEX + LightWeightIO = 0x00010000, // CAP_LWIO + Unix = 0x00800000, // CAP_UNIX + DynamicReauthentication = 0x20000000, // CAP_DYNAMIC_REAUTH + + /// + /// The server supports extended security for authentication + /// + ExtendedSecurity = 0x80000000, // CAP_EXTENDED_SECURITY + } +} diff --git a/SMBLibrary/SMB1/Enums/Open/AccessRights.cs b/SMBLibrary/SMB1/Enums/Open/AccessRights.cs new file mode 100644 index 0000000..255d080 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/Open/AccessRights.cs @@ -0,0 +1,10 @@ + +namespace SMBLibrary.SMB1 +{ + public enum AccessRights : ushort + { + SMB_DA_ACCESS_READ = 0x00, + SMB_DA_ACCESS_WRITE = 0x01, + SMB_DA_ACCESS_READ_WRITE = 0x02, + } +} diff --git a/SMBLibrary/SMB1/Enums/Open/OpenFlags.cs b/SMBLibrary/SMB1/Enums/Open/OpenFlags.cs new file mode 100644 index 0000000..883837c --- /dev/null +++ b/SMBLibrary/SMB1/Enums/Open/OpenFlags.cs @@ -0,0 +1,31 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum OpenFlags : ushort + { + /// + /// If this bit is set, the client requests that the file attribute data in the response be populated. + /// All fields after the FID in the response are also populated. If this bit is not set, + /// all fields after the FID in the response are zero. + /// + REQ_ATTRIB = 0x0001, + + /// + /// Client requests an exclusive OpLock on the file. + /// + REQ_OPLOCK = 0x0002, + + /// + /// Client requests a Batch OpLock on the file. + /// + REQ_OPLOCK_BATCH = 0x0004, + + /// + /// SMB 1.0 Addition. + /// If set, the client is requesting the extended format of the response. + /// + SMB_OPEN_EXTENDED_RESPONSE = 0x0010, + } +} diff --git a/SMBLibrary/SMB1/Enums/Open/OpenResult.cs b/SMBLibrary/SMB1/Enums/Open/OpenResult.cs new file mode 100644 index 0000000..e0cc7cb --- /dev/null +++ b/SMBLibrary/SMB1/Enums/Open/OpenResult.cs @@ -0,0 +1,11 @@ + +namespace SMBLibrary.SMB1 +{ + public enum OpenResult : byte + { + Reserved = 0x00, + FileExistedAndWasOpened = 0x01, + NotExistedAndWasCreated = 0x02, + FileExistedAndWasTruncated = 0x03, + } +} diff --git a/SMBLibrary/SMB1/Enums/ResourceType.cs b/SMBLibrary/SMB1/Enums/ResourceType.cs new file mode 100644 index 0000000..b4edf2e --- /dev/null +++ b/SMBLibrary/SMB1/Enums/ResourceType.cs @@ -0,0 +1,28 @@ + +namespace SMBLibrary.SMB1 +{ + public enum ResourceType : ushort + { + FileTypeDisk = 0x0000, + FileTypeByteModePipe = 0x0001, + FileTypeMessageModePipe = 0x0002, + FileTypePrinter = 0x0003, + + /// + /// OpenAndX Response: Valid. + /// OpenAndX Extended Response: Invalid (SMB 1.0). + /// NTCreateAndX Response: Valid. + /// NTCreateAndX Extended Response: Invalid (SMB 1.0). + /// Transact2Open2: Was never valid + /// + FileTypeCommDevice = 0x0004, + + /// + /// OpenAndX Response: Valid + /// NTCreateAndX Response: Invalid + /// Transact2Open2 Response: Valid + /// TransactCreate Response: Valid + /// + FileTypeUnknown = 0xFFFF, + } +} diff --git a/SMBLibrary/SMB1/Enums/SessionSetup/SessionSetupAction.cs b/SMBLibrary/SMB1/Enums/SessionSetup/SessionSetupAction.cs new file mode 100644 index 0000000..1563674 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/SessionSetup/SessionSetupAction.cs @@ -0,0 +1,11 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum SessionSetupAction : ushort + { + SetupGuest = 0x01, // SMB_SETUP_GUEST + UseLanmanKey = 0x02, // SMB_SETUP_USE_LANMAN_KEY + } +} diff --git a/SMBLibrary/SMB1/Enums/Transaction/TransactionFlags.cs b/SMBLibrary/SMB1/Enums/Transaction/TransactionFlags.cs new file mode 100644 index 0000000..9b0eed1 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/Transaction/TransactionFlags.cs @@ -0,0 +1,11 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum TransactionFlags : ushort + { + DISCONNECT_TID = 0x0001, + NO_RESPONSE = 0x0002, + } +} diff --git a/SMBLibrary/SMB1/Enums/TreeConnect/OptionalSupportFlags.cs b/SMBLibrary/SMB1/Enums/TreeConnect/OptionalSupportFlags.cs new file mode 100644 index 0000000..c54f216 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/TreeConnect/OptionalSupportFlags.cs @@ -0,0 +1,13 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum OptionalSupportFlags : ushort + { + /// + /// The server supports the use of SMB_FILE_ATTRIBUTES exclusive search attributes in client requests. + /// + SMB_SUPPORT_SEARCH_BITS = 0x01, + } +} diff --git a/SMBLibrary/SMB1/Enums/TreeConnect/ServiceName.cs b/SMBLibrary/SMB1/Enums/TreeConnect/ServiceName.cs new file mode 100644 index 0000000..6632468 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/TreeConnect/ServiceName.cs @@ -0,0 +1,16 @@ + +namespace SMBLibrary.SMB1 +{ + public enum ServiceName + { + DiskShare, + PrinterShare, + NamedPipe, + SerialCommunicationsDevice, + + /// + /// Valid only for request + /// + AnyType, + } +} diff --git a/SMBLibrary/SMB1/Enums/TreeConnect/TreeConnectFlags.cs b/SMBLibrary/SMB1/Enums/TreeConnect/TreeConnectFlags.cs new file mode 100644 index 0000000..e78e166 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/TreeConnect/TreeConnectFlags.cs @@ -0,0 +1,27 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum TreeConnectFlags : ushort + { + /// + /// If set and SMB_Header.TID is valid, the tree connect specified by the TID in the SMB + /// header of the request SHOULD be disconnected when the server sends the response. If this tree disconnect fails, then the error SHOULD be ignored + /// If set and TID is invalid, the server MUST ignore this bit. + /// + DisconnectTID = 0x0001, // TREE_CONNECT_ANDX_DISCONNECT_TID + + /// + /// SMB 1.0 addition. + /// If set, then the client is requesting signing key protection. + /// + ExtendedSignatures = 0x0004, // TREE_CONNECT_ANDX_EXTENDED_SIGNATURES + + /// + /// SMB 1.0 addition. + /// If set, then the client is requesting extended information in the SMB_COM_TREE_CONNECT_ANDX response. + /// + ExtendedResponse = 0x0008, // TREE_CONNECT_ANDX_EXTENDED_RESPONSE + } +} diff --git a/SMBLibrary/SMB1/Enums/Write/WriteMode.cs b/SMBLibrary/SMB1/Enums/Write/WriteMode.cs new file mode 100644 index 0000000..e569394 --- /dev/null +++ b/SMBLibrary/SMB1/Enums/Write/WriteMode.cs @@ -0,0 +1,13 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum WriteMode : ushort + { + WritethroughMode = 0x0001, + ReadBytesAvailable = 0x0002, + RAW_MODE = 0x0004, + MSG_START = 0x0008, + } +} diff --git a/SMBLibrary/SMB1/NTTransactSubcommands/Enums/CompletionFilter.cs b/SMBLibrary/SMB1/NTTransactSubcommands/Enums/CompletionFilter.cs new file mode 100644 index 0000000..e3d6c65 --- /dev/null +++ b/SMBLibrary/SMB1/NTTransactSubcommands/Enums/CompletionFilter.cs @@ -0,0 +1,20 @@ + +namespace SMBLibrary.SMB1 +{ + public enum CompletionFilter : uint + { + FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001, + FILE_NOTIFY_CHANGE_DIR_NAME = 0x00000002, + FILE_NOTIFY_CHANGE_NAME = 0x00000003, + FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x00000004, + FILE_NOTIFY_CHANGE_SIZE = 0x00000008, + FILE_NOTIFY_CHANGE_LAST_WRITE = 0x00000010, + FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x00000020, + FILE_NOTIFY_CHANGE_CREATION = 0x00000040, + FILE_NOTIFY_CHANGE_EA = 0x00000080, + FILE_NOTIFY_CHANGE_SECURITY = 0x00000100, + FILE_NOTIFY_CHANGE_STREAM_NAME = 0x00000200, + FILE_NOTIFY_CHANGE_STREAM_SIZE = 0x00000400, + FILE_NOTIFY_CHANGE_STREAM_WRITE = 0x00000800, + } +} diff --git a/SMBLibrary/SMB1/NTTransactSubcommands/Enums/NTTransactSubcommandName.cs b/SMBLibrary/SMB1/NTTransactSubcommands/Enums/NTTransactSubcommandName.cs new file mode 100644 index 0000000..c9b05a7 --- /dev/null +++ b/SMBLibrary/SMB1/NTTransactSubcommands/Enums/NTTransactSubcommandName.cs @@ -0,0 +1,16 @@ + +namespace SMBLibrary.SMB1 +{ + /// + /// This is the Function field in NTTransactRequest + /// + public enum NTTransactSubcommandName : ushort + { + NT_TRANSACT_CREATE = 0x0001, + NT_TRANSACT_IOCTL = 0x0002, + NT_TRANSACT_SET_SECURITY_DESC = 0x0003, + NT_TRANSACT_NOTIFY_CHANGE = 0x0004, + // NT_TRANSACT_RENAME = 0x0005, + NT_TRANSACT_QUERY_SECURITY_DESC = 0x0006, + } +} diff --git a/SMBLibrary/SMB1/NTTransactSubcommands/Enums/SecurityInfoFields.cs b/SMBLibrary/SMB1/NTTransactSubcommands/Enums/SecurityInfoFields.cs new file mode 100644 index 0000000..d741d7e --- /dev/null +++ b/SMBLibrary/SMB1/NTTransactSubcommands/Enums/SecurityInfoFields.cs @@ -0,0 +1,13 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum SecurityInfoFields : uint + { + OWNER_SECURITY_INFORMATION = 0x00000001, + GROUP_SECURITY_INFORMATION = 0x00000002, + DACL_SECURITY_INFORMATION = 0x00000004, + SACL_SECURITY_INFORMATION = 0x00000008, + } +} diff --git a/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactCreateRequest.cs b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactCreateRequest.cs new file mode 100644 index 0000000..0db6d0c --- /dev/null +++ b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactCreateRequest.cs @@ -0,0 +1,90 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// NT_TRANSACT_CREATE Request + /// + public class NTTransactCreateRequest : NTTransactSubcommand + { + public const int ParametersFixedLength = 53; + // Parameters: + public NTCreateFlags Flags; + public uint RootDirectoryFID; + public DesiredAccess DesiredAccess; + public ulong AllocationSize; + public ExtendedFileAttributes ExtFileAttributes; + public ShareAccess ShareAccess; + public CreateDisposition CreateDisposition; + public CreateOptions CreateOptions; + //uint SecurityDescriptiorLength; + //uint EALength; + //uint NameLength; + public ImpersonationLevel ImpersonationLevel; + public SecurityFlags SecurityFlags; + public string Name; // OEM / Unicode. NOT null terminated. (MUST be aligned to start on a 2-byte boundary from the start of the NT_Trans_Parameters) + // Data: + public SecurityDescriptor SecurityDescriptor; + public FileFullEAInformationList ExtendedAttributes; + + public NTTransactCreateRequest() + { + } + + public NTTransactCreateRequest(byte[] parameters, byte[] data, bool isUnicode) + { + int parametersOffset = 0; + Flags = (NTCreateFlags)LittleEndianReader.ReadUInt32(parameters, ref parametersOffset); + RootDirectoryFID = LittleEndianReader.ReadUInt32(parameters, ref parametersOffset); + DesiredAccess = (DesiredAccess)LittleEndianReader.ReadUInt32(parameters, ref parametersOffset); + AllocationSize = LittleEndianReader.ReadUInt64(parameters, ref parametersOffset); + ExtFileAttributes = (ExtendedFileAttributes)LittleEndianReader.ReadUInt32(parameters, ref parametersOffset); + ShareAccess = (ShareAccess)LittleEndianReader.ReadUInt32(parameters, ref parametersOffset); + CreateDisposition = (CreateDisposition)LittleEndianReader.ReadUInt32(parameters, ref parametersOffset); + CreateOptions = (CreateOptions)LittleEndianReader.ReadUInt32(parameters, ref parametersOffset); + uint securityDescriptiorLength = LittleEndianReader.ReadUInt32(parameters, ref parametersOffset); + uint eaLength = LittleEndianReader.ReadUInt32(parameters, ref parametersOffset); + uint nameLength = LittleEndianReader.ReadUInt32(parameters, ref parametersOffset); + ImpersonationLevel = (ImpersonationLevel)LittleEndianReader.ReadUInt32(parameters, ref parametersOffset); + SecurityFlags = (SecurityFlags)ByteReader.ReadByte(parameters, ref parametersOffset); + + if (isUnicode) + { + parametersOffset++; + } + Name = SMBHelper.ReadFixedLengthString(parameters, ref parametersOffset, isUnicode, (int)nameLength); + if (securityDescriptiorLength > 0) + { + SecurityDescriptor = new SecurityDescriptor(data, 0); + } + ExtendedAttributes = new FileFullEAInformationList(data, (int)securityDescriptiorLength); + } + + public override byte[] GetParameters(bool isUnicode) + { + throw new NotImplementedException(); + } + + public override byte[] GetData() + { + throw new NotImplementedException(); + } + + public override NTTransactSubcommandName SubcommandName + { + get + { + return NTTransactSubcommandName.NT_TRANSACT_CREATE; + } + } + } +} diff --git a/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactIOCTLRequest.cs b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactIOCTLRequest.cs new file mode 100644 index 0000000..f3055dc --- /dev/null +++ b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactIOCTLRequest.cs @@ -0,0 +1,66 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// NT_TRANSACT_IOCTL Request + /// + public class NTTransactIOCTLRequest : NTTransactSubcommand + { + public const int SetupLength = 8; + // Setup: + public uint FunctionCode; + public ushort FID; + public bool IsFsctl; + public bool IsFlags; + // Data: + public byte[] Data; + + public NTTransactIOCTLRequest() : base() + { + Data = new byte[0]; + } + + public NTTransactIOCTLRequest(byte[] setup, byte[] data) : base() + { + FunctionCode = LittleEndianConverter.ToUInt32(setup, 0); + FID = LittleEndianConverter.ToUInt16(setup, 4); + IsFsctl = (ByteReader.ReadByte(setup, 6) != 0); + IsFlags = (ByteReader.ReadByte(setup, 7) != 0); + + Data = data; + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[SetupLength]; + LittleEndianWriter.WriteUInt32(setup, 0, FunctionCode); + LittleEndianWriter.WriteUInt32(setup, 4, FID); + ByteWriter.WriteByte(setup, 6, Convert.ToByte(IsFsctl)); + ByteWriter.WriteByte(setup, 7, Convert.ToByte(IsFlags)); + return setup; + } + + public override byte[] GetData() + { + return Data; + } + + public override NTTransactSubcommandName SubcommandName + { + get + { + return NTTransactSubcommandName.NT_TRANSACT_IOCTL; + } + } + } +} diff --git a/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactIOCTLResponse.cs b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactIOCTLResponse.cs new file mode 100644 index 0000000..9428503 --- /dev/null +++ b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactIOCTLResponse.cs @@ -0,0 +1,56 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// NT_TRANSACT_IOCTL Response + /// + public class NTTransactIOCTLResponse : NTTransactSubcommand + { + public const int SetupLength = 2; + // Setup: + public ushort TransactionDataSize; // in bytes + // Data: + public byte[] Data; + + public NTTransactIOCTLResponse() : base() + { + } + + public NTTransactIOCTLResponse(byte[] setup, byte[] data) : base() + { + TransactionDataSize = LittleEndianConverter.ToUInt16(setup, 0); + + Data = data; + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[SetupLength]; + LittleEndianWriter.WriteUInt16(setup, 0, TransactionDataSize); + return setup; + } + + public override byte[] GetData() + { + return Data; + } + + public override NTTransactSubcommandName SubcommandName + { + get + { + return NTTransactSubcommandName.NT_TRANSACT_IOCTL; + } + } + } +} diff --git a/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactNotifyChangeRequest.cs b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactNotifyChangeRequest.cs new file mode 100644 index 0000000..b3d6304 --- /dev/null +++ b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactNotifyChangeRequest.cs @@ -0,0 +1,56 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// NT_TRANSACT_NOTIFY_CHANGE Request + /// + public class NTTransactNotifyChangeRequest : NTTransactSubcommand + { + public const int SetupLength = 8; + // Setup: + public CompletionFilter CompletionFilter; + public ushort FID; + public bool WatchTree; + public byte Reserved; + + public NTTransactNotifyChangeRequest() : base() + { + } + + public NTTransactNotifyChangeRequest(byte[] setup) : base() + { + CompletionFilter = (CompletionFilter)LittleEndianConverter.ToUInt32(setup, 0); + FID = LittleEndianConverter.ToUInt16(setup, 4); + WatchTree = (ByteReader.ReadByte(setup, 6) != 0); + Reserved = ByteReader.ReadByte(setup, 7); + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[SetupLength]; + LittleEndianWriter.WriteUInt32(setup, 0, (uint)CompletionFilter); + LittleEndianWriter.WriteUInt32(setup, 4, FID); + ByteWriter.WriteByte(setup, 6, Convert.ToByte(WatchTree)); + ByteWriter.WriteByte(setup, 7, Reserved); + return setup; + } + + public override NTTransactSubcommandName SubcommandName + { + get + { + return NTTransactSubcommandName.NT_TRANSACT_NOTIFY_CHANGE; + } + } + } +} diff --git a/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactQuerySecurityDescriptorRequest.cs b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactQuerySecurityDescriptorRequest.cs new file mode 100644 index 0000000..9bb9271 --- /dev/null +++ b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactQuerySecurityDescriptorRequest.cs @@ -0,0 +1,53 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// NT_TRANSACT_QUERY_SECURITY_DESC Request + /// + public class NTTransactQuerySecurityDescriptorRequest :NTTransactSubcommand + { + public const int ParametersLength = 8; + // Parameters: + public ushort FID; + public ushort Reserved; + public SecurityInfoFields SecurityInfoFields; + + public NTTransactQuerySecurityDescriptorRequest() + { + } + + public NTTransactQuerySecurityDescriptorRequest(byte[] parameters) + { + FID = LittleEndianConverter.ToUInt16(parameters, 0); + Reserved = LittleEndianConverter.ToUInt16(parameters, 2); + SecurityInfoFields = (SecurityInfoFields)LittleEndianConverter.ToUInt32(parameters, 4); + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(parameters, 0, FID); + LittleEndianWriter.WriteUInt16(parameters, 2, Reserved); + LittleEndianWriter.WriteUInt32(parameters, 4, (uint)SecurityInfoFields); + return parameters; + } + + public override NTTransactSubcommandName SubcommandName + { + get + { + return NTTransactSubcommandName.NT_TRANSACT_QUERY_SECURITY_DESC; + } + } + } +} diff --git a/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactQuerySecurityDescriptorResponse.cs b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactQuerySecurityDescriptorResponse.cs new file mode 100644 index 0000000..f84184b --- /dev/null +++ b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactQuerySecurityDescriptorResponse.cs @@ -0,0 +1,47 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// NTTransactQuerySecurityDescription Response + /// + public class NTTransactQuerySecurityDescriptorResponse : NTTransactSubcommand + { + public const uint ParametersLength = 4; + // Parameters: + public uint LengthNeeded; // We might return STATUS_BUFFER_OVERFLOW without the SecurityDescriptor field + // Data + public SecurityDescriptor SecurityDescriptor; + + public NTTransactQuerySecurityDescriptorResponse() + { + } + + public NTTransactQuerySecurityDescriptorResponse(byte[] parameters, byte[] data) + { + LengthNeeded = LittleEndianConverter.ToUInt32(parameters, 0); + + if (data.Length == LengthNeeded) + { + SecurityDescriptor = new SecurityDescriptor(data, 0); + } + } + + public override NTTransactSubcommandName SubcommandName + { + get + { + return NTTransactSubcommandName.NT_TRANSACT_QUERY_SECURITY_DESC; + } + } + } +} diff --git a/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactSetSecurityDescriptor.cs b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactSetSecurityDescriptor.cs new file mode 100644 index 0000000..9905475 --- /dev/null +++ b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactSetSecurityDescriptor.cs @@ -0,0 +1,62 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// NT_TRANSACT_SET_SECURITY_DESC Request + /// + public class NTTransactSetSecurityDescriptor : NTTransactSubcommand + { + public const int ParametersLength = 8; + // Parameters: + public ushort FID; + public ushort Reserved; + public SecurityInfoFields SecurityInfoFields; + // Data: + public SecurityDescriptor SecurityDescriptor; + + public NTTransactSetSecurityDescriptor() + { + } + + public NTTransactSetSecurityDescriptor(byte[] parameters, byte[] data) + { + FID = LittleEndianConverter.ToUInt16(parameters, 0); + Reserved = LittleEndianConverter.ToUInt16(parameters, 2); + SecurityInfoFields = (SecurityInfoFields)LittleEndianConverter.ToUInt32(parameters, 4); + + SecurityDescriptor = new SecurityDescriptor(data, 0); + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(parameters, 0, FID); + LittleEndianWriter.WriteUInt16(parameters, 2, Reserved); + LittleEndianWriter.WriteUInt32(parameters, 4, (uint)SecurityInfoFields); + return parameters; + } + + public override byte[] GetData() + { + return SecurityDescriptor.GetBytes(); + } + + public override NTTransactSubcommandName SubcommandName + { + get + { + return NTTransactSubcommandName.NT_TRANSACT_SET_SECURITY_DESC; + } + } + } +} diff --git a/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactSubcommand.cs b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactSubcommand.cs new file mode 100644 index 0000000..f8aacfa --- /dev/null +++ b/SMBLibrary/SMB1/NTTransactSubcommands/NTTransactSubcommand.cs @@ -0,0 +1,58 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public abstract class NTTransactSubcommand + { + public NTTransactSubcommand() + { + } + + public virtual byte[] GetSetup() + { + return new byte[0]; + } + + public virtual byte[] GetParameters(bool isUnicode) + { + return new byte[0]; + } + + public virtual byte[] GetData() + { + return new byte[0]; + } + + public abstract NTTransactSubcommandName SubcommandName + { + get; + } + + public static NTTransactSubcommand GetSubcommandRequest(NTTransactSubcommandName subcommandName, byte[] setup, byte[] parameters, byte[] data, bool isUnicode) + { + switch (subcommandName) + { + case NTTransactSubcommandName.NT_TRANSACT_CREATE: + return new NTTransactCreateRequest(parameters, data, isUnicode); + case NTTransactSubcommandName.NT_TRANSACT_IOCTL: + return new NTTransactIOCTLRequest(setup, data); + case NTTransactSubcommandName.NT_TRANSACT_SET_SECURITY_DESC: + return new NTTransactSetSecurityDescriptor(parameters, data); + case NTTransactSubcommandName.NT_TRANSACT_NOTIFY_CHANGE: + return new NTTransactNotifyChangeRequest(setup); + case NTTransactSubcommandName.NT_TRANSACT_QUERY_SECURITY_DESC: + return new NTTransactQuerySecurityDescriptorRequest(parameters); + } + throw new InvalidRequestException(); + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/CheckDirectoryRequest.cs b/SMBLibrary/SMB1/SMBCommands/CheckDirectoryRequest.cs new file mode 100644 index 0000000..a70bf9a --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/CheckDirectoryRequest.cs @@ -0,0 +1,67 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_CHECK_DIRECTORY Request + /// + public class CheckDirectoryRequest : SMBCommand + { + public const byte SupportedBufferFormat = 0x04; + // Data: + public byte BufferFormat; + public string DirectoryName; // SMB_STRING + + public CheckDirectoryRequest() : base() + { + BufferFormat = SupportedBufferFormat; + DirectoryName = String.Empty; + } + + public CheckDirectoryRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + BufferFormat = ByteReader.ReadByte(this.SMBData, 0); + if (BufferFormat != SupportedBufferFormat) + { + throw new InvalidRequestException("Unsupported Buffer Format"); + } + DirectoryName = SMBHelper.ReadSMBString(this.SMBData, 1, isUnicode); + } + + public override byte[] GetBytes(bool isUnicode) + { + int length = 1; + if (isUnicode) + { + length += DirectoryName.Length * 2 + 2; + } + else + { + length += DirectoryName.Length + 1; + } + this.SMBData = new byte[1 + length]; + ByteWriter.WriteByte(this.SMBData, 0, BufferFormat); + SMBHelper.WriteSMBString(this.SMBData, 1, isUnicode, DirectoryName); + + return base.GetBytes(isUnicode); + } + + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_CHECK_DIRECTORY; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/CheckDirectoryResponse.cs b/SMBLibrary/SMB1/SMBCommands/CheckDirectoryResponse.cs new file mode 100644 index 0000000..fb27384 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/CheckDirectoryResponse.cs @@ -0,0 +1,40 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_CHECK_DIRECTORY Response + /// + public class CheckDirectoryResponse : SMBCommand + { + public CheckDirectoryResponse() : base() + { + } + + public CheckDirectoryResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override byte[] GetBytes(bool isUnicode) + { + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_CHECK_DIRECTORY; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/CloseRequest.cs b/SMBLibrary/SMB1/SMBCommands/CloseRequest.cs new file mode 100644 index 0000000..c4c050b --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/CloseRequest.cs @@ -0,0 +1,54 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_CLOSE Request + /// + public class CloseRequest : SMBCommand + { + public const int ParametersLength = 6; + // Parameters: + public ushort FID; + /// + /// A value of 0x00000000 or 0xFFFFFFFF results in the server not updating the last modification time + /// + public DateTime LastTimeModified; + + public CloseRequest() : base() + { + LastTimeModified = SMBHelper.UTimeNotSpecified; + } + + public CloseRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + FID = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + LastTimeModified = SMBHelper.ReadUTime(this.SMBParameters, 2); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, FID); + SMBHelper.WriteUTime(this.SMBParameters, 2, LastTimeModified); + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_CLOSE; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/CloseResponse.cs b/SMBLibrary/SMB1/SMBCommands/CloseResponse.cs new file mode 100644 index 0000000..4a10fd2 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/CloseResponse.cs @@ -0,0 +1,33 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_CLOSE Response + /// + public class CloseResponse : SMBCommand + { + public CloseResponse() : base() + {} + + public CloseResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_CLOSE; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/CreateDirectoryRequest.cs b/SMBLibrary/SMB1/SMBCommands/CreateDirectoryRequest.cs new file mode 100644 index 0000000..c448217 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/CreateDirectoryRequest.cs @@ -0,0 +1,68 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_CREATE_DIRECTORY Request. + /// This command is obsolete. + /// This command is used by Windows NT4 SP6. + /// + public class CreateDirectoryRequest : SMBCommand + { + public const byte SupportedBufferFormat = 0x04; + // Data: + public byte BufferFormat; + public string DirectoryName; // SMB_STRING + + public CreateDirectoryRequest() : base() + { + BufferFormat = SupportedBufferFormat; + DirectoryName = String.Empty; + } + + public CreateDirectoryRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + BufferFormat = ByteReader.ReadByte(this.SMBData, 0); + if (BufferFormat != SupportedBufferFormat) + { + throw new InvalidRequestException("Unsupported Buffer Format"); + } + DirectoryName = SMBHelper.ReadSMBString(this.SMBData, 1, isUnicode); + } + + public override byte[] GetBytes(bool isUnicode) + { + int length = 1; + if (isUnicode) + { + length += DirectoryName.Length * 2 + 2; + } + else + { + length += DirectoryName.Length + 1; + } + this.SMBData = new byte[1 + length]; + ByteWriter.WriteByte(this.SMBData, 0, BufferFormat); + SMBHelper.WriteSMBString(this.SMBData, 1, isUnicode, DirectoryName); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_CREATE_DIRECTORY; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/CreateDirectoryResponse.cs b/SMBLibrary/SMB1/SMBCommands/CreateDirectoryResponse.cs new file mode 100644 index 0000000..d9b7bd5 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/CreateDirectoryResponse.cs @@ -0,0 +1,37 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_CREATE_DIRECTORY Response. + /// This command is obsolete. + /// This command is used by Windows NT4 SP6. + /// + public class CreateDirectoryResponse : SMBCommand + { + public CreateDirectoryResponse() : base() + { + } + + public CreateDirectoryResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_CREATE_DIRECTORY; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/DeleteDirectoryRequest.cs b/SMBLibrary/SMB1/SMBCommands/DeleteDirectoryRequest.cs new file mode 100644 index 0000000..3bf2eb6 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/DeleteDirectoryRequest.cs @@ -0,0 +1,64 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_DELETE_DIRECTORY Request + /// + public class DeleteDirectoryRequest : SMBCommand + { + public const int SupportedBufferFormat = 0x04; + // Data: + public byte BufferFormat; + public string DirectoryName; // SMB_STRING + + public DeleteDirectoryRequest() : base() + { + BufferFormat = SupportedBufferFormat; + } + + public DeleteDirectoryRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + BufferFormat = ByteReader.ReadByte(this.SMBData, 0); + if (BufferFormat != SupportedBufferFormat) + { + throw new InvalidRequestException("Unsupported Buffer Format"); + } + DirectoryName = SMBHelper.ReadSMBString(this.SMBData, 1, isUnicode); + } + + public override byte[] GetBytes(bool isUnicode) + { + int length = 1; + if (isUnicode) + { + length += DirectoryName.Length * 2 + 2; + } + else + { + length += DirectoryName.Length + 1; + } + this.SMBData = new byte[length]; + ByteWriter.WriteByte(this.SMBData, 0, BufferFormat); + SMBHelper.WriteSMBString(this.SMBData, 1, isUnicode, DirectoryName); + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_DELETE_DIRECTORY; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/DeleteDirectoryResponse.cs b/SMBLibrary/SMB1/SMBCommands/DeleteDirectoryResponse.cs new file mode 100644 index 0000000..b41f7c1 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/DeleteDirectoryResponse.cs @@ -0,0 +1,34 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_DELETE_DIRECTORY Response + /// + public class DeleteDirectoryResponse : SMBCommand + { + public DeleteDirectoryResponse() : base() + { + } + + public DeleteDirectoryResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_DELETE_DIRECTORY; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/DeleteRequest.cs b/SMBLibrary/SMB1/SMBCommands/DeleteRequest.cs new file mode 100644 index 0000000..4639f58 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/DeleteRequest.cs @@ -0,0 +1,57 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_DELETE Request + /// + public class DeleteRequest : SMBCommand + { + public const int SupportedBufferFormat = 0x04; + public const int ParametersLength = 2; + // Parameters; + public FileAttributes SearchAttributes; + // Data: + public byte BufferFormat; + public string FileName; // SMB_STRING + + public DeleteRequest() : base() + { + BufferFormat = SupportedBufferFormat; + } + + public DeleteRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + SearchAttributes = (FileAttributes)LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + + BufferFormat = ByteReader.ReadByte(this.SMBData, 0); + if (BufferFormat != SupportedBufferFormat) + { + throw new InvalidRequestException("Unsupported Buffer Format"); + } + FileName = SMBHelper.ReadSMBString(this.SMBData, 1, isUnicode); + } + + public override byte[] GetBytes(bool isUnicode) + { + throw new NotImplementedException(); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_DELETE; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/DeleteResponse.cs b/SMBLibrary/SMB1/SMBCommands/DeleteResponse.cs new file mode 100644 index 0000000..729b709 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/DeleteResponse.cs @@ -0,0 +1,39 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_DELETE Response + /// + public class DeleteResponse : SMBCommand + { + public DeleteResponse() : base() + { + } + + public DeleteResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override byte[] GetBytes(bool isUnicode) + { + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_DELETE; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/EchoRequest.cs b/SMBLibrary/SMB1/SMBCommands/EchoRequest.cs new file mode 100644 index 0000000..9fe3b74 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/EchoRequest.cs @@ -0,0 +1,49 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_ECHO + /// + public class EchoRequest : SMBCommand + { + public const int ParametersLength = 1; + // Parameters + public byte EchoCount; + + public EchoRequest() : base() + { + + } + + public EchoRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + EchoCount = ByteReader.ReadByte(this.SMBParameters, 0); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + ByteWriter.WriteByte(this.SMBParameters, 0, EchoCount); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_ECHO; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/EchoResponse.cs b/SMBLibrary/SMB1/SMBCommands/EchoResponse.cs new file mode 100644 index 0000000..34896fc --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/EchoResponse.cs @@ -0,0 +1,48 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_ECHO + /// + public class EchoResponse : SMBCommand + { + public const int ParametersLength = 2; + // Parameters + public ushort SequenceNumber; + + public EchoResponse() : base() + { + } + + public EchoResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + SequenceNumber = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, SequenceNumber); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_ECHO; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/ErrorResponse.cs b/SMBLibrary/SMB1/SMBCommands/ErrorResponse.cs new file mode 100644 index 0000000..bca6194 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/ErrorResponse.cs @@ -0,0 +1,35 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// The Command trailer of an error response message. + /// See [MS-CIFS]3.3.4.1.2 - Sending Any Error Response Message. + /// + public class ErrorResponse : SMBCommand + { + private CommandName m_commandName; + + public ErrorResponse(CommandName commandName) : base() + { + m_commandName = commandName; + } + + public override CommandName CommandName + { + get + { + return m_commandName; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/FindClose2Request.cs b/SMBLibrary/SMB1/SMBCommands/FindClose2Request.cs new file mode 100644 index 0000000..ecd4bcd --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/FindClose2Request.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_FIND_CLOSE2 Request + /// + public class FindClose2Request : SMBCommand + { + public const int ParameterCount = 2; + // Parameters: + public ushort SearchHandle; + + public FindClose2Request() : base() + { + } + + public FindClose2Request(byte[] buffer, int offset) : base(buffer, offset, false) + { + SearchHandle = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_FIND_CLOSE2; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/FindClose2Response.cs b/SMBLibrary/SMB1/SMBCommands/FindClose2Response.cs new file mode 100644 index 0000000..921a8bb --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/FindClose2Response.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_FIND_CLOSE2 Response + /// + public class FindClose2Response : SMBCommand + { + public FindClose2Response() : base() + { + } + + public FindClose2Response(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_FIND_CLOSE2; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/FlushRequest.cs b/SMBLibrary/SMB1/SMBCommands/FlushRequest.cs new file mode 100644 index 0000000..2ab11dc --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/FlushRequest.cs @@ -0,0 +1,48 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_FLUSH Request + /// + public class FlushRequest : SMBCommand + { + public const int ParametersLength = 2; + // Parameters: + public ushort FID; + + public FlushRequest() : base() + { + } + + public FlushRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + FID = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, FID); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_FLUSH; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/FlushResponse.cs b/SMBLibrary/SMB1/SMBCommands/FlushResponse.cs new file mode 100644 index 0000000..86acbf0 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/FlushResponse.cs @@ -0,0 +1,35 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_FLUSH Response + /// + public class FlushResponse : SMBCommand + { + public FlushResponse() : base() + { + } + + public FlushResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_FLUSH; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/LockingAndXRequest.cs b/SMBLibrary/SMB1/SMBCommands/LockingAndXRequest.cs new file mode 100644 index 0000000..14778c3 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/LockingAndXRequest.cs @@ -0,0 +1,180 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// LOCKING_ANDX_RANGE32 (10-byte) + /// or + /// LOCKING_ANDX_RANGE64 (24-byte ) + /// + public class LockingRange + { + public const int Length32 = 10; + public const int Length64 = 20; + + public ushort PID; + public ulong ByteOffset; + public ulong LengthInBytes; + + public void Write32(byte[] buffer, ref int offset) + { + LittleEndianWriter.WriteUInt16(buffer, ref offset, this.PID); + LittleEndianWriter.WriteUInt32(buffer, ref offset, (uint)this.ByteOffset); + LittleEndianWriter.WriteUInt32(buffer, ref offset, (uint)this.LengthInBytes); + } + + public void Write64(byte[] buffer, ref int offset) + { + LittleEndianWriter.WriteUInt16(buffer, ref offset, this.PID); + offset += 2; // padding + LittleEndianWriter.WriteUInt64(buffer, ref offset, this.ByteOffset); + LittleEndianWriter.WriteUInt64(buffer, ref offset, this.LengthInBytes); + } + + public static LockingRange Read32(byte[] buffer, ref int offset) + { + LockingRange entry = new LockingRange(); + entry.PID = LittleEndianReader.ReadUInt16(buffer, ref offset); + entry.ByteOffset = LittleEndianReader.ReadUInt32(buffer, ref offset); + entry.LengthInBytes = LittleEndianReader.ReadUInt32(buffer, ref offset); + return entry; + } + + public static LockingRange Read64(byte[] buffer, ref int offset) + { + LockingRange entry = new LockingRange(); + entry.PID = LittleEndianReader.ReadUInt16(buffer, ref offset); + offset += 2; // padding + entry.ByteOffset = LittleEndianReader.ReadUInt64(buffer, ref offset); + entry.LengthInBytes = LittleEndianReader.ReadUInt64(buffer, ref offset); + return entry; + } + } + + /// + /// SMB_COM_LOCKING_ANDX Request + /// + public class LockingAndXRequest : SMBAndXCommand + { + public const int ParametersLength = 12; + // Parameters: + public ushort FID; + public LockType TypeOfLock; + public byte NewOpLockLevel; + public uint Timeout; + //ushort NumberOfRequestedUnlocks; + //ushort NumberOfRequestedLocks; + // Data: + public List Unlocks = new List(); + public List Locks = new List(); + + public LockingAndXRequest() : base() + { + } + + public LockingAndXRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + FID = LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + TypeOfLock = (LockType)ByteReader.ReadByte(this.SMBParameters, 6); + NewOpLockLevel = ByteReader.ReadByte(this.SMBParameters, 7); + Timeout = LittleEndianConverter.ToUInt32(this.SMBParameters, 8); + ushort numberOfRequestedUnlocks = LittleEndianConverter.ToUInt16(this.SMBParameters, 12); + ushort numberOfRequestedLocks = LittleEndianConverter.ToUInt16(this.SMBParameters, 14); + + int dataOffset = 0; + if ((TypeOfLock & LockType.LARGE_FILES) > 0) + { + for (int index = 0; index < numberOfRequestedUnlocks; index++) + { + LockingRange entry = LockingRange.Read64(this.SMBData, ref dataOffset); + Unlocks.Add(entry); + } + + for (int index = 0; index < numberOfRequestedLocks; index++) + { + LockingRange entry = LockingRange.Read64(this.SMBData, ref dataOffset); + Locks.Add(entry); + } + } + else + { + for (int index = 0; index < numberOfRequestedUnlocks; index++) + { + LockingRange entry = LockingRange.Read32(this.SMBData, ref dataOffset); + Unlocks.Add(entry); + } + + for (int index = 0; index < numberOfRequestedLocks; index++) + { + LockingRange entry = LockingRange.Read32(this.SMBData, ref dataOffset); + Locks.Add(entry); + } + } + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, FID); + ByteWriter.WriteByte(this.SMBParameters, 6, (byte)TypeOfLock); + ByteWriter.WriteByte(this.SMBParameters, 7, NewOpLockLevel); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 8, Timeout); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 12, (ushort)Unlocks.Count); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 14, (ushort)Locks.Count); + + int dataLength; + bool isLargeFile = (TypeOfLock & LockType.LARGE_FILES) > 0; + if (isLargeFile) + { + dataLength = (Unlocks.Count + Locks.Count) * LockingRange.Length64; + } + else + { + dataLength = (Unlocks.Count + Locks.Count) * LockingRange.Length32; + } + int dataOffset = 0; + this.SMBData = new byte[dataLength]; + for (int index = 0; index < Unlocks.Count; index++) + { + if (isLargeFile) + { + Unlocks[index].Write64(this.SMBData, ref dataOffset); + } + else + { + Unlocks[index].Write32(this.SMBData, ref dataOffset); + } + } + + for (int index = 0; index < Locks.Count; index++) + { + if (isLargeFile) + { + Locks[index].Write64(this.SMBData, ref dataOffset); + } + else + { + Locks[index].Write32(this.SMBData, ref dataOffset); + } + } + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_LOCKING_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/LockingAndXResponse.cs b/SMBLibrary/SMB1/SMBCommands/LockingAndXResponse.cs new file mode 100644 index 0000000..5060031 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/LockingAndXResponse.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_LOCKING_ANDX Response + /// + public class LockingAndXResponse : SMBAndXCommand + { + public const uint ParametersLength = 4; + + public LockingAndXResponse() : base() + { + } + + public LockingAndXResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_LOCKING_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/LogoffAndXRequest.cs b/SMBLibrary/SMB1/SMBCommands/LogoffAndXRequest.cs new file mode 100644 index 0000000..c2a89a2 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/LogoffAndXRequest.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_LOGOFF_ANDX Request + /// + public class LogoffAndXRequest : SMBAndXCommand + { + public const uint ParametersLength = 4; + + public LogoffAndXRequest() : base() + { + } + + public LogoffAndXRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_LOGOFF_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/LogoffAndXResponse.cs b/SMBLibrary/SMB1/SMBCommands/LogoffAndXResponse.cs new file mode 100644 index 0000000..0939414 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/LogoffAndXResponse.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_LOGOFF_ANDX Response + /// + public class LogoffAndXResponse : SMBAndXCommand + { + public const uint ParametersLength = 4; + + public LogoffAndXResponse() : base() + { + } + + public LogoffAndXResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_LOGOFF_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/NTCreateAndXRequest.cs b/SMBLibrary/SMB1/SMBCommands/NTCreateAndXRequest.cs new file mode 100644 index 0000000..9b8f0de --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/NTCreateAndXRequest.cs @@ -0,0 +1,110 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_NT_CREATE_ANDX Request + /// + public class NTCreateAndXRequest : SMBAndXCommand + { + public const int ParametersLength = 48; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public byte Reserved; + //ushort NameLength; // in bytes + public NTCreateFlags Flags; + public uint RootDirectoryFID; + public DesiredAccess DesiredAccess; + public ulong AllocationSize; + public ExtendedFileAttributes ExtFileAttributes; + public ShareAccess ShareAccess; + public CreateDisposition CreateDisposition; + public CreateOptions CreateOptions; + public ImpersonationLevel ImpersonationLevel; + public SecurityFlags SecurityFlags; + // Data: + public string FileName; // SMB_STRING (If Unicode, this field MUST be aligned to start on a 2-byte boundary from the start of the SMB header) + + public NTCreateAndXRequest() : base() + { + } + + public NTCreateAndXRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + Reserved = ByteReader.ReadByte(this.SMBParameters, 4); + ushort nameLength = LittleEndianConverter.ToUInt16(this.SMBParameters, 5); + Flags = (NTCreateFlags)LittleEndianConverter.ToUInt32(this.SMBParameters, 7); + RootDirectoryFID = LittleEndianConverter.ToUInt32(this.SMBParameters, 11); + DesiredAccess = (DesiredAccess)LittleEndianConverter.ToUInt32(this.SMBParameters, 15); + AllocationSize = LittleEndianConverter.ToUInt64(this.SMBParameters, 19); + ExtFileAttributes = (ExtendedFileAttributes)LittleEndianConverter.ToUInt32(this.SMBParameters, 27); + ShareAccess = (ShareAccess)LittleEndianConverter.ToUInt32(this.SMBParameters, 31); + CreateDisposition = (CreateDisposition)LittleEndianConverter.ToUInt32(this.SMBParameters, 35); + CreateOptions = (CreateOptions)LittleEndianConverter.ToUInt32(this.SMBParameters, 39); + ImpersonationLevel = (ImpersonationLevel)LittleEndianConverter.ToUInt32(this.SMBParameters, 43); + SecurityFlags = (SecurityFlags)ByteReader.ReadByte(this.SMBParameters, 47); + + int dataOffset = 0; + if (isUnicode) + { + dataOffset = 1; // 1 byte padding for 2 byte alignment + } + FileName = SMBHelper.ReadSMBString(this.SMBData, dataOffset, isUnicode); + } + + public override byte[] GetBytes(bool isUnicode) + { + ushort nameLength = (ushort)FileName.Length; + this.SMBParameters = new byte[ParametersLength]; + ByteWriter.WriteByte(this.SMBParameters, 0, (byte)AndXCommand); + ByteWriter.WriteByte(this.SMBParameters, 1, AndXReserved); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 2, AndXOffset); + ByteWriter.WriteByte(this.SMBParameters, 4, Reserved); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 5, nameLength); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 7, (uint)Flags); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 11, (uint)RootDirectoryFID); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 15, (uint)DesiredAccess); + LittleEndianWriter.WriteUInt64(this.SMBParameters, 19, (ulong)AllocationSize); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 27, (uint)ExtFileAttributes); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 31, (uint)ShareAccess); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 35, (uint)CreateDisposition); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 39, (uint)CreateOptions); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 43, (uint)ImpersonationLevel); + ByteWriter.WriteByte(this.SMBParameters, 47, (byte)SecurityFlags); + + if (isUnicode) + { + int padding = 1; + this.SMBData = new byte[padding + FileName.Length * 2 + 2]; + int offset = padding; + ByteWriter.WriteNullTerminatedUTF16String(this.SMBData, offset, FileName); + } + else + { + this.SMBData = new byte[FileName.Length + 1]; + ByteWriter.WriteNullTerminatedUTF16String(this.SMBData, 0, FileName); + } + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_NT_CREATE_ANDX; + } + } + } +} \ No newline at end of file diff --git a/SMBLibrary/SMB1/SMBCommands/NTCreateAndXResponse.cs b/SMBLibrary/SMB1/SMBCommands/NTCreateAndXResponse.cs new file mode 100644 index 0000000..61ece40 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/NTCreateAndXResponse.cs @@ -0,0 +1,92 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_NT_CREATE_ANDX Response + /// + public class NTCreateAndXResponse : SMBAndXCommand + { + public const int ParametersLength = 68; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public OpLockLevel OpLockLevel; + public ushort FID; + public CreateDisposition CreateDisposition; + public DateTime CreateTime; + public DateTime LastAccessTime; + public DateTime LastWriteTime; + public DateTime LastChangeTime; + public ExtendedFileAttributes ExtFileAttributes; + public ulong AllocationSize; + public ulong EndOfFile; + public ResourceType ResourceType; + public NamedPipeStatus NMPipeStatus; + public bool Directory; + + public NTCreateAndXResponse() : base() + { + CreateTime = SMBHelper.FileTimeNotSpecified; + LastAccessTime = SMBHelper.FileTimeNotSpecified; + LastWriteTime = SMBHelper.FileTimeNotSpecified; + LastChangeTime = SMBHelper.FileTimeNotSpecified; + } + + public NTCreateAndXResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + int parametersOffset = 4; + OpLockLevel = (OpLockLevel)ByteReader.ReadByte(this.SMBParameters, ref parametersOffset); + FID = LittleEndianReader.ReadUInt16(this.SMBParameters, ref parametersOffset); + CreateDisposition = (CreateDisposition)LittleEndianReader.ReadUInt32(this.SMBParameters, ref parametersOffset); + CreateTime = SMBHelper.ReadFileTime(buffer, ref parametersOffset); + LastAccessTime = SMBHelper.ReadFileTime(buffer, ref parametersOffset); + LastWriteTime = SMBHelper.ReadFileTime(buffer, ref parametersOffset); + LastChangeTime = SMBHelper.ReadFileTime(buffer, ref parametersOffset); + ExtFileAttributes = (ExtendedFileAttributes)LittleEndianReader.ReadUInt32(this.SMBParameters, ref parametersOffset); + AllocationSize = LittleEndianReader.ReadUInt64(buffer, ref parametersOffset); + EndOfFile = LittleEndianReader.ReadUInt64(buffer, ref parametersOffset); + ResourceType = (ResourceType)LittleEndianReader.ReadUInt16(this.SMBParameters, ref parametersOffset); + NMPipeStatus = NamedPipeStatus.Read(this.SMBParameters, ref parametersOffset); + Directory = (ByteReader.ReadByte(this.SMBParameters, ref parametersOffset) > 0); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + int parametersOffset = 4; + ByteWriter.WriteByte(this.SMBParameters, ref parametersOffset, (byte)OpLockLevel); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, FID); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref parametersOffset, (uint)CreateDisposition); + SMBHelper.WriteFileTime(this.SMBParameters, ref parametersOffset, CreateTime); + SMBHelper.WriteFileTime(this.SMBParameters, ref parametersOffset, LastAccessTime); + SMBHelper.WriteFileTime(this.SMBParameters, ref parametersOffset, LastWriteTime); + SMBHelper.WriteFileTime(this.SMBParameters, ref parametersOffset, LastChangeTime); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref parametersOffset, (uint)ExtFileAttributes); + LittleEndianWriter.WriteUInt64(this.SMBParameters, ref parametersOffset, AllocationSize); + LittleEndianWriter.WriteUInt64(this.SMBParameters, ref parametersOffset, EndOfFile); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, (ushort)ResourceType); + NMPipeStatus.WriteBytes(this.SMBParameters, ref parametersOffset); + ByteWriter.WriteByte(this.SMBParameters, ref parametersOffset, Convert.ToByte(Directory)); + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_NT_CREATE_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/NTCreateAndXResponseExtended.cs b/SMBLibrary/SMB1/SMBCommands/NTCreateAndXResponseExtended.cs new file mode 100644 index 0000000..9bcfdf1 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/NTCreateAndXResponseExtended.cs @@ -0,0 +1,128 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_NT_CREATE_ANDX Extended Response + /// + public class NTCreateAndXResponseExtended : SMBAndXCommand + { + public const int ParametersLength = 100; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public OpLockLevel OpLockLevel; + public ushort FID; + public CreateDisposition CreateDisposition; + public DateTime CreateTime; + public DateTime LastAccessTime; + public DateTime LastWriteTime; + public DateTime LastChangeTime; + public ExtendedFileAttributes ExtFileAttributes; + public ulong AllocationSize; + public ulong EndOfFile; + public ResourceType ResourceType; + public ushort NMPipeStatus_or_FileStatusFlags; + public bool Directory; + public Guid VolumeGuid; + public ulong FileID; + public AccessMask MaximalAccessRights; + public AccessMask GuestMaximalAccessRights; + + public NTCreateAndXResponseExtended() : base() + { + CreateTime = SMBHelper.FileTimeNotSpecified; + LastAccessTime = SMBHelper.FileTimeNotSpecified; + LastWriteTime = SMBHelper.FileTimeNotSpecified; + LastChangeTime = SMBHelper.FileTimeNotSpecified; + } + + public NTCreateAndXResponseExtended(byte[] buffer, int offset) : base(buffer, offset, false) + { + int parametersOffset = 4; + OpLockLevel = (OpLockLevel)ByteReader.ReadByte(this.SMBParameters, ref parametersOffset); + FID = LittleEndianReader.ReadUInt16(this.SMBParameters, ref parametersOffset); + CreateDisposition = (CreateDisposition)LittleEndianReader.ReadUInt32(this.SMBParameters, ref parametersOffset); + CreateTime = SMBHelper.ReadFileTime(buffer, ref parametersOffset); + LastAccessTime = SMBHelper.ReadFileTime(buffer, ref parametersOffset); + LastWriteTime = SMBHelper.ReadFileTime(buffer, ref parametersOffset); + LastChangeTime = SMBHelper.ReadFileTime(buffer, ref parametersOffset); + ExtFileAttributes = (ExtendedFileAttributes)LittleEndianReader.ReadUInt32(this.SMBParameters, ref parametersOffset); + AllocationSize = LittleEndianReader.ReadUInt64(buffer, ref parametersOffset); + EndOfFile = LittleEndianReader.ReadUInt64(buffer, ref parametersOffset); + ResourceType = (ResourceType)LittleEndianReader.ReadUInt16(this.SMBParameters, ref parametersOffset); + NMPipeStatus_or_FileStatusFlags = LittleEndianReader.ReadUInt16(this.SMBParameters, ref parametersOffset); + Directory = (ByteReader.ReadByte(this.SMBParameters, ref parametersOffset) > 0); + VolumeGuid = LittleEndianReader.ReadGuid(this.SMBParameters, ref parametersOffset); + FileID = LittleEndianReader.ReadUInt64(this.SMBParameters, ref parametersOffset); + MaximalAccessRights = new AccessMask(this.SMBParameters, ref parametersOffset); + GuestMaximalAccessRights = new AccessMask(this.SMBParameters, ref parametersOffset); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + int parametersOffset = 4; + ByteWriter.WriteByte(this.SMBParameters, ref parametersOffset, (byte)OpLockLevel); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, FID); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref parametersOffset, (uint)CreateDisposition); + SMBHelper.WriteFileTime(this.SMBParameters, ref parametersOffset, CreateTime); + SMBHelper.WriteFileTime(this.SMBParameters, ref parametersOffset, LastAccessTime); + SMBHelper.WriteFileTime(this.SMBParameters, ref parametersOffset, LastWriteTime); + SMBHelper.WriteFileTime(this.SMBParameters, ref parametersOffset, LastChangeTime); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref parametersOffset, (uint)ExtFileAttributes); + LittleEndianWriter.WriteUInt64(this.SMBParameters, ref parametersOffset, AllocationSize); + LittleEndianWriter.WriteUInt64(this.SMBParameters, ref parametersOffset, EndOfFile); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, (ushort)ResourceType); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, NMPipeStatus_or_FileStatusFlags); + ByteWriter.WriteByte(this.SMBParameters, ref parametersOffset, Convert.ToByte(Directory)); + LittleEndianWriter.WriteGuidBytes(this.SMBParameters, ref parametersOffset, VolumeGuid); + LittleEndianWriter.WriteUInt64(this.SMBParameters, ref parametersOffset, FileID); + MaximalAccessRights.WriteBytes(this.SMBParameters, ref parametersOffset); + GuestMaximalAccessRights.WriteBytes(this.SMBParameters, ref parametersOffset); + return base.GetBytes(isUnicode); + } + + public NamedPipeStatus NMPipeStatus + { + get + { + return new NamedPipeStatus(NMPipeStatus_or_FileStatusFlags); + } + set + { + NMPipeStatus_or_FileStatusFlags = value.ToUInt16(); + } + } + + public FileStatus FileStatus + { + get + { + return (FileStatus)NMPipeStatus_or_FileStatusFlags; + } + set + { + NMPipeStatus_or_FileStatusFlags = (ushort)value; + } + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_NT_CREATE_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/NTTransactInterimResponse.cs b/SMBLibrary/SMB1/SMBCommands/NTTransactInterimResponse.cs new file mode 100644 index 0000000..d06e4df --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/NTTransactInterimResponse.cs @@ -0,0 +1,37 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_NT_TRANSACT Interim Response + /// + public class NTTransactInterimResponse : SMBCommand + { + public const int ParametersLength = 0; + + public NTTransactInterimResponse() : base() + { + } + + public NTTransactInterimResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_NT_TRANSACT; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/NTTransactRequest.cs b/SMBLibrary/SMB1/SMBCommands/NTTransactRequest.cs new file mode 100644 index 0000000..0f62374 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/NTTransactRequest.cs @@ -0,0 +1,110 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_NT_TRANSACT Request + /// + public class NTTransactRequest : SMBCommand + { + public const int FixedSMBParametersLength = 38; + // Parameters: + public byte MaxSetupCount; + public ushort Reserved1; + public uint TotalParameterCount; + public uint TotalDataCount; + public uint MaxParameterCount; + public uint MaxDataCount; + //uint ParameterCount; + //uint ParameterOffset; + //uint DataCount; + //uint DataOffset; + //byte SetupCount; // In 2-byte words + public NTTransactSubcommandName Function; + public byte[] Setup; + // Data: + // Padding (alignment to 4 byte boundary) + public byte[] TransParameters; // Trans_Parameters + // Padding (alignment to 4 byte boundary) + public byte[] TransData; // Trans_Data + + public NTTransactRequest() : base() + { + } + + public NTTransactRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + int readOffset = 0; + MaxSetupCount = ByteReader.ReadByte(this.SMBParameters, ref readOffset); + Reserved1 = LittleEndianReader.ReadUInt16(this.SMBParameters, ref readOffset); + TotalParameterCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + TotalDataCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + MaxParameterCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + MaxDataCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint parameterCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint parameterOffset = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint dataCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint dataOffset = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + byte setupCount = ByteReader.ReadByte(this.SMBParameters, ref readOffset); + Function = (NTTransactSubcommandName)LittleEndianReader.ReadUInt16(this.SMBParameters, ref readOffset); + Setup = ByteReader.ReadBytes(this.SMBParameters, ref readOffset, setupCount * 2); + + TransParameters = ByteReader.ReadBytes(buffer, (int)parameterOffset, (int)parameterCount); + TransData = ByteReader.ReadBytes(buffer, (int)dataOffset, (int)dataCount); + } + + public override byte[] GetBytes(bool isUnicode) + { + byte setupCount = (byte)(Setup.Length / 2); + uint parameterCount = (ushort)TransParameters.Length; + uint dataCount = (ushort)TransData.Length; + + // WordCount + ByteCount are additional 3 bytes + uint parameterOffset = (ushort)(SMBHeader.Length + 3 + (FixedSMBParametersLength + Setup.Length)); + int padding1 = (int)(4 - (parameterOffset % 4)) % 4; + parameterOffset += (ushort)padding1; + uint dataOffset = (ushort)(parameterOffset + parameterCount); + int padding2 = (int)(4 - (dataOffset % 4)) % 4; + dataOffset += (ushort)padding2; + + this.SMBParameters = new byte[FixedSMBParametersLength + Setup.Length]; + int writeOffset = 0; + ByteWriter.WriteByte(this.SMBParameters, ref writeOffset, MaxSetupCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref writeOffset, Reserved1); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, TotalParameterCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, TotalDataCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, MaxParameterCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, MaxDataCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, parameterCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, parameterOffset); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, dataCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, dataOffset); + ByteWriter.WriteByte(this.SMBParameters, ref writeOffset, setupCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref writeOffset, (ushort)Function); + ByteWriter.WriteBytes(this.SMBParameters, ref writeOffset, Setup); + + this.SMBData = new byte[parameterCount + dataCount + padding1 + padding2]; + ByteWriter.WriteBytes(this.SMBData, padding1, TransParameters); + ByteWriter.WriteBytes(this.SMBData, (int)(padding1 + parameterCount + padding2), TransData); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_NT_TRANSACT; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/NTTransactResponse.cs b/SMBLibrary/SMB1/SMBCommands/NTTransactResponse.cs new file mode 100644 index 0000000..d237e2e --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/NTTransactResponse.cs @@ -0,0 +1,121 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_NT_TRANSACT Response + /// + public class NTTransactResponse : SMBCommand + { + public const int FixedSMBParametersLength = 36; + // Parameters: + public byte[] Reserved1; // 3 bytes + public uint TotalParameterCount; + public uint TotalDataCount; + //uint ParameterCount; + //uint ParameterOffset; + //uint ParameterDisplacement; + //uint DataCount; + //uint DataOffset; + //uint DataDisplacement; + //byte SetupCount; // In 2-byte words + public byte[] Setup; + // Data: + // Padding (alignment to 4 byte boundary) + public byte[] TransParameters; // Trans_Parameters + // Padding (alignment to 4 byte boundary) + public byte[] TransData; // Trans_Data + + public NTTransactResponse() : base() + { + Reserved1 = new byte[3]; + } + + public NTTransactResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + int readOffset = 0; + Reserved1 = ByteReader.ReadBytes(this.SMBParameters, ref readOffset, 3); + TotalParameterCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + TotalDataCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint parameterCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint parameterOffset = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint parameterDisplacement = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint dataCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint dataOffset = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint dataDisplacement = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + byte setupCount = ByteReader.ReadByte(this.SMBParameters, ref readOffset); + Setup = ByteReader.ReadBytes(this.SMBParameters, ref offset, setupCount * 2); + + TransParameters = ByteReader.ReadBytes(buffer, (int)parameterOffset, (int)parameterCount); + TransData = ByteReader.ReadBytes(buffer, (int)dataOffset, (int)dataCount); + } + + public override byte[] GetBytes(bool isUnicode) + { + byte setupCount = (byte)(Setup.Length / 2); + uint parameterCount = (ushort)TransParameters.Length; + uint dataCount = (ushort)TransData.Length; + uint parameterDisplacement = 0; + uint dataDisplacement = 0; + + // WordCount + ByteCount are additional 3 bytes + uint parameterOffset = (ushort)(SMBHeader.Length + 3 + (FixedSMBParametersLength + Setup.Length)); + int padding1 = (int)(4 - (parameterOffset % 4)) % 4; + parameterOffset += (ushort)padding1; + uint dataOffset = (ushort)(parameterOffset + parameterCount); + int padding2 = (int)(4 - (dataOffset % 4)) % 4; + dataOffset += (ushort)padding2; + + this.SMBParameters = new byte[FixedSMBParametersLength + Setup.Length]; + int writeOffset = 0; + ByteWriter.WriteBytes(this.SMBParameters, ref writeOffset, Reserved1, 3); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, TotalParameterCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, TotalDataCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, parameterCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, parameterOffset); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, parameterDisplacement); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, dataCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, dataOffset); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, dataDisplacement); + ByteWriter.WriteByte(this.SMBParameters, ref writeOffset, setupCount); + ByteWriter.WriteBytes(this.SMBParameters, ref writeOffset, Setup); + + this.SMBData = new byte[parameterCount + dataCount + padding1 + padding2]; + ByteWriter.WriteBytes(this.SMBData, padding1, TransParameters); + ByteWriter.WriteBytes(this.SMBData, (int)(padding1 + parameterCount + padding2), TransData); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_NT_TRANSACT; + } + } + + public static int CalculateMessageSize(int setupLength, int trans2ParametersLength, int trans2DataLength) + { + int parameterOffset = SMBHeader.Length + 3 + (FixedSMBParametersLength + setupLength); + int padding1 = (4 - (parameterOffset % 4)) % 4; + parameterOffset += padding1; + int dataOffset = (parameterOffset + trans2ParametersLength); + int padding2 = (4 - (dataOffset % 4)) % 4; + + int messageParametersLength = FixedSMBParametersLength + setupLength; + int messageDataLength = trans2ParametersLength + trans2DataLength + padding1 + padding2; + // WordCount + ByteCount are additional 3 bytes + return SMBHeader.Length + messageParametersLength + messageDataLength + 3; + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/NTTransactSecondaryRequest.cs b/SMBLibrary/SMB1/SMBCommands/NTTransactSecondaryRequest.cs new file mode 100644 index 0000000..3f7a0e2 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/NTTransactSecondaryRequest.cs @@ -0,0 +1,101 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_NT_TRANSACT_SECONDARY Request + /// + public class NTTransactSecondaryRequest : SMBCommand + { + public const int SMBParametersLength = 36; + // Parameters: + public byte[] Reserved1; // 3 bytes + public uint TotalParameterCount; + public uint TotalDataCount; + //uint ParameterCount; + //uint ParameterOffset; + public uint ParameterDisplacement; + //uint DataCount; + //uint DataOffset; + public uint DataDisplacement; + public byte Reserved2; + // Data: + // Padding (alignment to 4 byte boundary) + public byte[] TransParameters; // Trans_Parameters + // Padding (alignment to 4 byte boundary) + public byte[] TransData; // Trans_Data + + public NTTransactSecondaryRequest() : base() + { + Reserved1 = new byte[3]; + } + + public NTTransactSecondaryRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + int readOffset = 0; + Reserved1 = ByteReader.ReadBytes(this.SMBParameters, ref readOffset, 3); + TotalParameterCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + TotalDataCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint parameterCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint parameterOffset = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + ParameterDisplacement = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint dataCount = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + uint dataOffset = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + DataDisplacement = LittleEndianReader.ReadUInt32(this.SMBParameters, ref readOffset); + Reserved2 = ByteReader.ReadByte(this.SMBParameters, ref readOffset); + + TransParameters = ByteReader.ReadBytes(buffer, (int)parameterOffset, (int)parameterCount); + TransData = ByteReader.ReadBytes(buffer, (int)dataOffset, (int)dataCount); + } + + public override byte[] GetBytes(bool isUnicode) + { + uint parameterCount = (ushort)TransParameters.Length; + uint dataCount = (ushort)TransData.Length; + + // WordCount + ByteCount are additional 3 bytes + uint parameterOffset = (ushort)(SMBHeader.Length + 3 + (SMBParametersLength)); + int padding1 = (int)(4 - (parameterOffset % 4)) % 4; + parameterOffset += (ushort)padding1; + uint dataOffset = (ushort)(parameterOffset + parameterCount); + int padding2 = (int)(4 - (dataOffset % 4)) % 4; + dataOffset += (ushort)padding2; + + this.SMBParameters = new byte[SMBParametersLength]; + int writeOffset = 0; + ByteWriter.WriteBytes(this.SMBParameters, ref writeOffset, Reserved1, 3); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, TotalParameterCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, TotalDataCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, parameterCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, parameterOffset); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, ParameterDisplacement); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, dataCount); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, dataOffset); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref writeOffset, DataDisplacement); + ByteWriter.WriteByte(this.SMBParameters, ref writeOffset, Reserved2); + + this.SMBData = new byte[parameterCount + dataCount + padding1 + padding2]; + ByteWriter.WriteBytes(this.SMBData, padding1, TransParameters); + ByteWriter.WriteBytes(this.SMBData, (int)(padding1 + parameterCount + padding2), TransData); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_NT_TRANSACT_SECONDARY; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/NegotiateRequest.cs b/SMBLibrary/SMB1/SMBCommands/NegotiateRequest.cs new file mode 100644 index 0000000..8dc92cf --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/NegotiateRequest.cs @@ -0,0 +1,72 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_NEGOTIATE Request + /// + public class NegotiateRequest : SMBCommand + { + public const int SupportedBufferFormat = 0x02; + // Data: + public List Dialects = new List(); + + public NegotiateRequest() : base() + { + } + + public NegotiateRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + int dataOffset = 0; + while (dataOffset < this.SMBData.Length) + { + byte bufferFormat = ByteReader.ReadByte(this.SMBData, ref dataOffset); + if (bufferFormat != SupportedBufferFormat) + { + throw new InvalidRequestException("Unsupported Buffer Format"); + } + string dialect = ByteReader.ReadNullTerminatedAnsiString(this.SMBData, dataOffset); + Dialects.Add(dialect); + dataOffset += dialect.Length + 1; + } + } + + public override byte[] GetBytes(bool isUnicode) + { + int length = 0; + foreach (string dialect in this.Dialects) + { + length += 1 + dialect.Length + 1; + } + + this.SMBParameters = new byte[0]; + this.SMBData = new byte[length]; + int offset = 0; + foreach (string dialect in this.Dialects) + { + ByteWriter.WriteByte(this.SMBData, offset, 0x02); + ByteWriter.WriteAnsiString(this.SMBData, offset + 1, dialect, dialect.Length); + ByteWriter.WriteByte(this.SMBData, offset + 1 + dialect.Length, 0x00); + offset += 1 + dialect.Length + 1; + } + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_NEGOTIATE; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/NegotiateResponseNTLM.cs b/SMBLibrary/SMB1/SMBCommands/NegotiateResponseNTLM.cs new file mode 100644 index 0000000..07ab51a --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/NegotiateResponseNTLM.cs @@ -0,0 +1,88 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_NEGOTIATE Response, NT LAN Manager dialect + /// + public class NegotiateResponseNTLM : SMBCommand + { + public const int ParametersLength = 34; + // Parameters: + public ushort DialectIndex; + public SecurityMode SecurityMode; + public ushort MaxMpxCount; + public ushort MaxNumberVcs; + public uint MaxBufferSize; + public uint MaxRawSize; + public uint SessionKey; + public ServerCapabilities Capabilities; + public DateTime SystemTime; + public short ServerTimeZone; + //byte ChallengeLength; + // Data: + public byte[] Challenge; + public string DomainName; // SMB_STRING (If Unicode, this field MUST be aligned to start on a 2-byte boundary from the start of the SMB header) + public string ServerName; // SMB_STRING (this field WILL be aligned to start on a 2-byte boundary from the start of the SMB header) + + public NegotiateResponseNTLM() : base() + { + Challenge = new byte[0]; + DomainName = String.Empty; + ServerName = String.Empty; + } + + public override byte[] GetBytes(bool isUnicode) + { + byte challengeLength = (byte)this.Challenge.Length; + + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, DialectIndex); + ByteWriter.WriteByte(this.SMBParameters, 2, (byte)SecurityMode); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 3, MaxMpxCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 5, MaxNumberVcs); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 7, MaxBufferSize); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 11, MaxRawSize); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 15, SessionKey); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 19, (uint)Capabilities); + LittleEndianWriter.WriteInt64(this.SMBParameters, 23, SystemTime.ToFileTimeUtc()); + LittleEndianWriter.WriteInt16(this.SMBParameters, 31, ServerTimeZone); + ByteWriter.WriteByte(this.SMBParameters, 33, challengeLength); + + int padding = 0; + if (isUnicode) + { + padding = Challenge.Length % 2; + this.SMBData = new byte[Challenge.Length + padding + DomainName.Length * 2 + ServerName.Length * 2 + 4]; + } + else + { + this.SMBData = new byte[Challenge.Length + DomainName.Length + ServerName.Length + 2]; + } + int offset = 0; + ByteWriter.WriteBytes(this.SMBData, ref offset, Challenge); + offset += padding; + SMBHelper.WriteSMBString(this.SMBData, ref offset, isUnicode, DomainName); + SMBHelper.WriteSMBString(this.SMBData, ref offset, isUnicode, ServerName); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_NEGOTIATE; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/NegotiateResponseNTLMExtended.cs b/SMBLibrary/SMB1/SMBCommands/NegotiateResponseNTLMExtended.cs new file mode 100644 index 0000000..a69c825 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/NegotiateResponseNTLMExtended.cs @@ -0,0 +1,79 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_NEGOTIATE Response, NT LAN Manager dialect, Extended Security response + /// + public class NegotiateResponseNTLMExtended : SMBCommand + { + public const int ParametersLength = 34; + // Parameters: + public ushort DialectIndex; + public SecurityMode SecurityMode; + public ushort MaxMpxCount; + public ushort MaxNumberVcs; + public uint MaxBufferSize; + public uint MaxRawSize; + public uint SessionKey; + public ServerCapabilities Capabilities; + public DateTime SystemTime; + public short ServerTimeZone; + //byte ChallengeLength; // MUST be set to 0 + // Data: + public Guid ServerGuid; + public byte[] SecurityBlob; + + public NegotiateResponseNTLMExtended() : base() + { + // MS-SMB Page 129: The server can leave SecurityBlob empty if not configured to send GSS token + SecurityBlob = new byte[0]; + } + + public NegotiateResponseNTLMExtended(byte[] buffer, int offset) : base(buffer, offset, false) + { + throw new NotImplementedException(); + } + + public override byte[] GetBytes(bool isUnicode) + { + byte challengeLength = 0; + + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, DialectIndex); + ByteWriter.WriteByte(this.SMBParameters, 2, (byte)SecurityMode); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 3, MaxMpxCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 5, MaxNumberVcs); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 7, MaxBufferSize); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 11, MaxRawSize); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 15, SessionKey); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 19, (uint)Capabilities); + LittleEndianWriter.WriteInt64(this.SMBParameters, 23, SystemTime.ToFileTimeUtc()); + LittleEndianWriter.WriteInt16(this.SMBParameters, 31, ServerTimeZone); + ByteWriter.WriteByte(this.SMBParameters, 33, challengeLength); + + this.SMBData = new byte[16 + SecurityBlob.Length]; + LittleEndianWriter.WriteGuidBytes(this.SMBData, 0, ServerGuid); + ByteWriter.WriteBytes(this.SMBData, 16, SecurityBlob); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_NEGOTIATE; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/NegotiateResponseNotSupported.cs b/SMBLibrary/SMB1/SMBCommands/NegotiateResponseNotSupported.cs new file mode 100644 index 0000000..e784621 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/NegotiateResponseNotSupported.cs @@ -0,0 +1,49 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_NEGOTIATE Response + /// + public class NegotiateResponseNotSupported : SMBCommand + { + public const int ParametersLength = 2; + public const ushort DialectsNotSupported = 0xFFFF; + + public NegotiateResponseNotSupported() : base() + { + } + + public NegotiateResponseNotSupported(byte[] buffer, int offset) : base(buffer, offset, false) + { + throw new NotImplementedException(); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, DialectsNotSupported); + + this.SMBData = new byte[0]; + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_NEGOTIATE; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/OpenAndXRequest.cs b/SMBLibrary/SMB1/SMBCommands/OpenAndXRequest.cs new file mode 100644 index 0000000..a48bcd1 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/OpenAndXRequest.cs @@ -0,0 +1,74 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_OPEN_ANDX Request + /// + public class OpenAndXRequest : SMBAndXCommand + { + public const int ParametersLength = 30; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public OpenFlags Flags; + public AccessModeOptions AccessMode; + public FileAttributes SearchAttrs; + public FileAttributes FileAttrs; + public DateTime CreationTime; // UTime + public OpenMode OpenMode; + public uint AllocationSize; + public uint Timeout; + public uint Reserved; + // Data: + public string FileName; // SMB_STRING (If Unicode, this field MUST be aligned to start on a 2-byte boundary from the start of the SMB header) + + public OpenAndXRequest() : base() + { + } + + public OpenAndXRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + int parametersOffset = 4; + Flags = (OpenFlags)LittleEndianReader.ReadUInt16(this.SMBParameters, ref parametersOffset); + AccessMode = AccessModeOptions.Read(this.SMBParameters, ref parametersOffset); + SearchAttrs = (FileAttributes)LittleEndianReader.ReadUInt16(this.SMBParameters, ref parametersOffset); + FileAttrs = (FileAttributes)LittleEndianReader.ReadUInt16(this.SMBParameters, ref parametersOffset); + CreationTime = SMBHelper.ReadUTime(this.SMBParameters, ref parametersOffset); + OpenMode = OpenMode.Read(this.SMBParameters, ref parametersOffset); + AllocationSize = LittleEndianReader.ReadUInt32(this.SMBParameters, ref parametersOffset); + Timeout = LittleEndianReader.ReadUInt32(this.SMBParameters, ref parametersOffset); + Reserved = LittleEndianReader.ReadUInt32(this.SMBParameters, ref parametersOffset); + + int dataOffset = 0; + if (isUnicode) + { + dataOffset = 1; // 1 byte padding for 2 byte alignment + } + FileName = SMBHelper.ReadSMBString(this.SMBData, dataOffset, isUnicode); + } + + public override byte[] GetBytes(bool isUnicode) + { + throw new NotImplementedException(); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_OPEN_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/OpenAndXResponse.cs b/SMBLibrary/SMB1/SMBCommands/OpenAndXResponse.cs new file mode 100644 index 0000000..23d23d1 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/OpenAndXResponse.cs @@ -0,0 +1,69 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_OPEN_ANDX Response + /// + public class OpenAndXResponse : SMBAndXCommand + { + public const int ParametersLength = 30; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public ushort FID; + public FileAttributes FileAttrs; + public DateTime LastWriteTime; // UTime + public uint FileDataSize; + public AccessRights AccessRights; + public ResourceType ResourceType; + public NamedPipeStatus NMPipeStatus; + public OpenResults OpenResults; + public byte[] Reserved; // 6 bytes + + public OpenAndXResponse() : base() + { + LastWriteTime = SMBHelper.UTimeNotSpecified; + Reserved = new byte[6]; + } + + public OpenAndXResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + throw new NotImplementedException(); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + int parametersOffset = 4; + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, FID); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, (ushort)FileAttrs); + SMBHelper.WriteUTime(this.SMBParameters, ref parametersOffset, LastWriteTime); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref parametersOffset, FileDataSize); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, (ushort)AccessRights); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, (ushort)ResourceType); + NMPipeStatus.WriteBytes(this.SMBParameters, ref parametersOffset); + OpenResults.WriteBytes(this.SMBParameters, ref parametersOffset); + ByteWriter.WriteBytes(this.SMBParameters, ref parametersOffset, Reserved, 6); + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_OPEN_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/OpenAndXResponseExtended.cs b/SMBLibrary/SMB1/SMBCommands/OpenAndXResponseExtended.cs new file mode 100644 index 0000000..16739d4 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/OpenAndXResponseExtended.cs @@ -0,0 +1,74 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_OPEN_ANDX Response Extended + /// + public class OpenAndXResponseExtended : SMBAndXCommand + { + public const int ParametersLength = 38; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public ushort FID; + public FileAttributes FileAttrs; + public DateTime LastWriteTime; // UTime + public uint FileDataSize; + public AccessRights AccessRights; + public ResourceType ResourceType; + public NamedPipeStatus NMPipeStatus; + public OpenResults OpenResults; + public uint ServerFID; + public ushort Reserved; + public AccessMask MaximalAccessRights; + public AccessMask GuestMaximalAccessRights; + + public OpenAndXResponseExtended() : base() + { + LastWriteTime = SMBHelper.UTimeNotSpecified; + } + + public OpenAndXResponseExtended(byte[] buffer, int offset) : base(buffer, offset, false) + { + throw new NotImplementedException(); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + int parametersOffset = 4; + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, FID); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, (ushort)FileAttrs); + SMBHelper.WriteUTime(this.SMBParameters, ref parametersOffset, LastWriteTime); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref parametersOffset, FileDataSize); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, (ushort)AccessRights); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, (ushort)ResourceType); + NMPipeStatus.WriteBytes(this.SMBParameters, ref parametersOffset); + OpenResults.WriteBytes(this.SMBParameters, ref parametersOffset); + LittleEndianWriter.WriteUInt32(this.SMBParameters, ref parametersOffset, ServerFID); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, Reserved); + MaximalAccessRights.WriteBytes(this.SMBParameters, ref parametersOffset); + GuestMaximalAccessRights.WriteBytes(this.SMBParameters, ref parametersOffset); + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_OPEN_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/QueryInformationRequest.cs b/SMBLibrary/SMB1/SMBCommands/QueryInformationRequest.cs new file mode 100644 index 0000000..736c1a0 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/QueryInformationRequest.cs @@ -0,0 +1,68 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_QUERY_INFORMATION Request. + /// This command is deprecated. + /// This command is used by Windows NT4 SP6. + /// + public class QueryInformationRequest : SMBCommand + { + public const byte SupportedBufferFormat = 0x04; + // Data: + public byte BufferFormat; + public string FileName; // SMB_STRING + + public QueryInformationRequest() : base() + { + BufferFormat = SupportedBufferFormat; + FileName = String.Empty; + } + + public QueryInformationRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + BufferFormat = ByteReader.ReadByte(this.SMBData, 0); + if (BufferFormat != SupportedBufferFormat) + { + throw new InvalidRequestException("Unsupported Buffer Format"); + } + FileName = SMBHelper.ReadSMBString(this.SMBData, 1, isUnicode); + } + + public override byte[] GetBytes(bool isUnicode) + { + int length = 1; + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + this.SMBData = new byte[1 + length]; + ByteWriter.WriteByte(this.SMBData, 0, BufferFormat); + SMBHelper.WriteSMBString(this.SMBData, 1, isUnicode, FileName); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_QUERY_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/QueryInformationResponse.cs b/SMBLibrary/SMB1/SMBCommands/QueryInformationResponse.cs new file mode 100644 index 0000000..ee4770d --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/QueryInformationResponse.cs @@ -0,0 +1,60 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_QUERY_INFORMATION Request. + /// This command is deprecated. + /// This command is used by Windows NT4 SP6. + /// + public class QueryInformationResponse : SMBCommand + { + public const int ParameterLength = 20; + // Parameters: + public FileAttributes FileAttributes; + public DateTime LastWriteTime; + public uint FileSize; + public byte[] Reserved; // 10 bytes + + public QueryInformationResponse() : base() + { + Reserved = new byte[10]; + } + + public QueryInformationResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + FileAttributes = (FileAttributes)LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + LastWriteTime = SMBHelper.ReadSMBDateTime(this.SMBParameters, 2); + FileSize = LittleEndianConverter.ToUInt32(this.SMBParameters, 6); + Reserved = ByteReader.ReadBytes(this.SMBParameters, 10, 10); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParameterLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, (ushort)FileAttributes); + SMBHelper.WriteSMBDateTime(this.SMBParameters, 2, LastWriteTime); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 6, FileSize); + ByteWriter.WriteBytes(this.SMBParameters, 10, Reserved, 10); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_QUERY_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/ReadAndXRequest.cs b/SMBLibrary/SMB1/SMBCommands/ReadAndXRequest.cs new file mode 100644 index 0000000..5f8b73e --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/ReadAndXRequest.cs @@ -0,0 +1,120 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_READ_ANDX Request + /// SMB 1.0: The Timeout field becomes Timeout_or_MaxCountHigh (used when the CAP_LARGE_READX capability has been negotiated) + /// + public class ReadAndXRequest : SMBAndXCommand + { + public const int ParametersFixedLength = 20; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public ushort FID; + public ulong Offset; // 4 bytes + 4 optional 'OffsetHigh' bytes + private ushort MaxCountOfBytesToReturn; // See 'Timeout_or_MaxCountHigh' comment + public ushort MinCountOfBytesToReturn; + /// + /// SMB 1.0: When reading from a regular file, the field MUST be interpreted as + /// MaxCountHigh and the two unused bytes MUST be zero. + /// When reading from a name pipe or I/O device, the field MUST be interpreted as Timeout. + /// + public uint Timeout_or_MaxCountHigh; // CIFS 1.0: Timeout only + public ushort Remaining; + + public ReadAndXRequest() : base() + { + } + + public ReadAndXRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + FID = LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + Offset = LittleEndianConverter.ToUInt32(this.SMBParameters, 6); + MaxCountOfBytesToReturn = LittleEndianConverter.ToUInt16(this.SMBParameters, 10); + MinCountOfBytesToReturn = LittleEndianConverter.ToUInt16(this.SMBParameters, 12); + Timeout_or_MaxCountHigh = LittleEndianConverter.ToUInt32(this.SMBParameters, 14); + Remaining = LittleEndianConverter.ToUInt16(this.SMBParameters, 18); + if (SMBParameters.Length == ParametersFixedLength + 4) + { + uint offsetHigh = LittleEndianConverter.ToUInt32(this.SMBParameters, 20); + Offset |= ((ulong)offsetHigh << 32); + } + } + + public override byte[] GetBytes(bool isUnicode) + { + int parametersLength = ParametersFixedLength; + if (Offset > UInt32.MaxValue) + { + parametersLength += 4; + } + + this.SMBParameters = new byte[parametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, FID); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 6, (uint)(Offset & 0xFFFFFFFF)); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 10, (ushort)(MaxCountOfBytesToReturn & 0xFFFF)); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 12, MinCountOfBytesToReturn); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 14, Timeout_or_MaxCountHigh); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 18, Remaining); + if (Offset > UInt32.MaxValue) + { + uint offsetHigh = (uint)(Offset >> 32); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 20, offsetHigh); + } + + return base.GetBytes(isUnicode); + } + + /// + /// The number of bytes to return when reading from a file and LargeRead is negotiated + /// + public uint MaxCountLarge + { + get + { + ushort maxCountHigh = (ushort)(Timeout_or_MaxCountHigh & 0xFFFF); + return (uint)(maxCountHigh << 16) | MaxCountOfBytesToReturn; + } + set + { + MaxCountOfBytesToReturn = (ushort)(value & 0xFFFF); + Timeout_or_MaxCountHigh = (ushort)(value >> 16); + } + } + + /// + /// The number of bytes to return when reading from a named pipe or LargeRead is not negotiated + /// + public ushort MaxCount + { + get + { + return MaxCountOfBytesToReturn; + } + set + { + MaxCountOfBytesToReturn = value; + } + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_READ_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/ReadAndXResponse.cs b/SMBLibrary/SMB1/SMBCommands/ReadAndXResponse.cs new file mode 100644 index 0000000..6930e35 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/ReadAndXResponse.cs @@ -0,0 +1,99 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_READ_ANDX Response + /// SMB 1.0: The 2 reserved bytes at offset 14 become DataLengthHigh (used when the CAP_LARGE_READX capability has been negotiated) + /// + public class ReadAndXResponse : SMBAndXCommand + { + public const int ParametersLength = 24; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public ushort Available; + public ushort DataCompactionMode; // Not used and MUST be 0x0000 + public ushort Reserved1; + //uint DataLength; // 2 bytes + 2 'DataLengthHigh' bytes + //ushort DataOffset; + public byte[] Reserved2; // 8 bytes + // Data: + // 1 byte padding - if unicode strings are being used, this field MUST be present, otherwise it's optional. + public byte[] Data; + + public ReadAndXResponse() : base() + { + Reserved2 = new byte[8]; + } + + public ReadAndXResponse(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + Available = LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + DataCompactionMode = LittleEndianConverter.ToUInt16(this.SMBParameters, 6); + Reserved1 = LittleEndianConverter.ToUInt16(this.SMBParameters, 8); + uint DataLength = LittleEndianConverter.ToUInt16(this.SMBParameters, 10); + ushort DataOffset = LittleEndianConverter.ToUInt16(this.SMBParameters, 12); + ushort dataLengthHigh = LittleEndianConverter.ToUInt16(this.SMBParameters, 14); + Reserved2 = ByteReader.ReadBytes(buffer, 16, 8); + + DataLength |= (uint)(dataLengthHigh << 16); + + Data = ByteReader.ReadBytes(buffer, DataOffset, (int)DataLength); + } + + public override byte[] GetBytes(bool isUnicode) + { + uint DataLength = (uint)Data.Length; + // WordCount + ByteCount are additional 3 bytes + ushort DataOffset = SMBHeader.Length + 3 + ParametersLength; + if (isUnicode) + { + DataOffset++; + } + ushort dataLengthHigh = (ushort)(DataLength >> 16); + + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, Available); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 6, DataCompactionMode); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 8, Reserved1); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 10, (ushort)(DataLength & 0xFFFF)); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 12, DataOffset); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 14, dataLengthHigh); + ByteWriter.WriteBytes(this.SMBParameters, 16, Reserved2); + + int smbDataLength = Data.Length; + if (isUnicode) + { + smbDataLength++; + } + this.SMBData = new byte[smbDataLength]; + int offset = 0; + if (isUnicode) + { + offset++; + } + ByteWriter.WriteBytes(this.SMBData, offset, this.Data); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_READ_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/ReadRequest.cs b/SMBLibrary/SMB1/SMBCommands/ReadRequest.cs new file mode 100644 index 0000000..1c18a52 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/ReadRequest.cs @@ -0,0 +1,56 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_READ Request + /// + public class ReadRequest : SMBCommand + { + public const int ParametersLength = 10; + // Parameters: + public ushort FID; + public ushort CountOfBytesToRead; + public uint ReadOffsetInBytes; + public ushort EstimateOfRemainingBytesToBeRead; + + public ReadRequest() : base() + { + } + + public ReadRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + FID = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + CountOfBytesToRead = LittleEndianConverter.ToUInt16(this.SMBParameters, 2); + ReadOffsetInBytes = LittleEndianConverter.ToUInt32(this.SMBParameters, 4); + CountOfBytesToRead = LittleEndianConverter.ToUInt16(this.SMBParameters, 8); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, FID); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 2, CountOfBytesToRead); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 4, ReadOffsetInBytes); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 8, CountOfBytesToRead); + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_READ; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/ReadResponse.cs b/SMBLibrary/SMB1/SMBCommands/ReadResponse.cs new file mode 100644 index 0000000..eeb7511 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/ReadResponse.cs @@ -0,0 +1,70 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_READ Response + /// + public class ReadResponse : SMBCommand + { + public const int ParametersLength = 10; + public const int SupportedBufferFormat = 0x01; + // Parameters: + public ushort CountOfBytesReturned; + public byte[] Reserved; // 8 reserved bytes + // Data: + public byte BufferFormat; + // ushort CountOfBytesRead; + public byte[] Bytes; + + public ReadResponse() : base() + { + Reserved = new byte[8]; + } + + public ReadResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + CountOfBytesReturned = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + Reserved = ByteReader.ReadBytes(this.SMBParameters, 2, 8); + + BufferFormat = ByteReader.ReadByte(this.SMBData, 0); + if (BufferFormat != SupportedBufferFormat) + { + throw new InvalidRequestException("Unsupported Buffer Format"); + } + ushort CountOfBytesRead = LittleEndianConverter.ToUInt16(this.SMBData, 1); + Bytes = ByteReader.ReadBytes(this.SMBData, 3, CountOfBytesRead); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, CountOfBytesReturned); + ByteWriter.WriteBytes(this.SMBParameters, 2, Reserved, 8); + + this.SMBData = new byte[3 + Bytes.Length]; + ByteWriter.WriteByte(this.SMBData, 0, BufferFormat); + LittleEndianWriter.WriteUInt16(this.SMBData, 1, (ushort)Bytes.Length); + ByteWriter.WriteBytes(this.SMBData, 3, Bytes); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_READ; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/RenameRequest.cs b/SMBLibrary/SMB1/SMBCommands/RenameRequest.cs new file mode 100644 index 0000000..aa61675 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/RenameRequest.cs @@ -0,0 +1,93 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_RENAME Request + /// + public class RenameRequest : SMBCommand + { + public const int SupportedBufferFormat = 0x04; + public const int ParametersLength = 2; + // Parameters: + public FileAttributes SearchAttributes; + // Data: + public byte BufferFormat1; + public string OldFileName; // SMB_STRING (this field WILL be aligned to start on a 2-byte boundary from the start of the SMB header) + public byte BufferFormat2; + public string NewFileName; // SMB_STRING (If Unicode, this field MUST be aligned to start on a 2-byte boundary from the start of the SMB header) + + public RenameRequest() : base() + { + BufferFormat1 = SupportedBufferFormat; + BufferFormat2 = SupportedBufferFormat; + } + + public RenameRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + SearchAttributes = (FileAttributes)LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + + int dataOffset = 0; + BufferFormat1 = ByteReader.ReadByte(this.SMBData, ref dataOffset); + if (BufferFormat1 != SupportedBufferFormat) + { + throw new InvalidRequestException("Unsupported Buffer Format"); + } + OldFileName = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + BufferFormat2 = ByteReader.ReadByte(this.SMBData, ref dataOffset); + if (BufferFormat2 != SupportedBufferFormat) + { + throw new InvalidRequestException("Unsupported Buffer Format"); + } + if (isUnicode) + { + dataOffset++; + } + NewFileName = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, (ushort)SearchAttributes); + + if (isUnicode) + { + int padding = 1; + this.SMBData = new byte[2 + OldFileName.Length * 2 + NewFileName.Length * 2 + 4 + padding]; + } + else + { + this.SMBData = new byte[2 + OldFileName.Length + NewFileName.Length + 2]; + } + int dataOffset = 0; + ByteWriter.WriteByte(this.SMBData, ref dataOffset, BufferFormat1); + SMBHelper.WriteSMBString(this.SMBData, ref dataOffset, isUnicode, OldFileName); + ByteWriter.WriteByte(this.SMBData, ref dataOffset, BufferFormat2); + if (isUnicode) + { + dataOffset++; // padding + } + SMBHelper.WriteSMBString(this.SMBData, ref dataOffset, isUnicode, NewFileName); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_RENAME; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/RenameResponse.cs b/SMBLibrary/SMB1/SMBCommands/RenameResponse.cs new file mode 100644 index 0000000..f0383e6 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/RenameResponse.cs @@ -0,0 +1,34 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_RENAME Response + /// + public class RenameResponse : SMBCommand + { + public RenameResponse() : base() + { + } + + public RenameResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_RENAME; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/SMBAndXCommand.cs b/SMBLibrary/SMB1/SMBCommands/SMBAndXCommand.cs new file mode 100644 index 0000000..f258eef --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/SMBAndXCommand.cs @@ -0,0 +1,45 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public abstract class SMBAndXCommand : SMBCommand + { + public CommandName AndXCommand; + public byte AndXReserved; + public ushort AndXOffset; + + public SMBAndXCommand() : base() + { + } + + public SMBAndXCommand(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + AndXCommand = (CommandName)ByteReader.ReadByte(this.SMBParameters, 0); + AndXReserved = ByteReader.ReadByte(this.SMBParameters, 1); + AndXOffset = LittleEndianConverter.ToUInt16(this.SMBParameters, 2); + } + + public override byte[] GetBytes(bool isUnicode) + { + ByteWriter.WriteByte(this.SMBParameters, 0, (byte)AndXCommand); + ByteWriter.WriteByte(this.SMBParameters, 1, AndXReserved); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 2, AndXOffset); + return base.GetBytes(isUnicode); + } + + public static void WriteAndXOffset(byte[] buffer, int commandOffset, ushort AndXOffset) + { + // 3 preceding bytes: WordCount, AndXCommand and AndXReserved + LittleEndianWriter.WriteUInt16(buffer, commandOffset + 3, AndXOffset); + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/SMBCommand.cs b/SMBLibrary/SMB1/SMBCommands/SMBCommand.cs new file mode 100644 index 0000000..84ccfb1 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/SMBCommand.cs @@ -0,0 +1,268 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public abstract class SMBCommand + { + public byte[] SMBParameters; // SMB_Parameters + public byte[] SMBData; // SMB_Data + + public SMBCommand() + { + SMBParameters = new byte[0]; + SMBData = new byte[0]; + } + + public SMBCommand(byte[] buffer, int offset, bool isUnicode) + { + byte wordCount = ByteReader.ReadByte(buffer, ref offset); + SMBParameters = ByteReader.ReadBytes(buffer, ref offset, wordCount * 2); + ushort byteCount = LittleEndianReader.ReadUInt16(buffer, ref offset); + SMBData = ByteReader.ReadBytes(buffer, ref offset, byteCount); + } + + public abstract CommandName CommandName + { + get; + } + + public virtual byte[] GetBytes(bool isUnicode) + { + if (SMBParameters.Length % 2 > 0) + { + throw new Exception("SMB_Parameters Length must be a multiple of 2"); + } + int length = 1 + SMBParameters.Length + 2 + SMBData.Length; + byte[] buffer = new byte[length]; + byte wordCount = (byte)(SMBParameters.Length / 2); + if (this is NTCreateAndXResponseExtended) + { + // [MS-SMB] Section 2.2.4.9.2 and Note <51>: + // Windows-based SMB servers send 50 (0x32) words in the extended response + // although they set the WordCount field to 0x2A + // wordCount SHOULD be 0x2A + wordCount = 0x2A; + } + ushort byteCount = (ushort)SMBData.Length; + + int offset = 0; + ByteWriter.WriteByte(buffer, ref offset, wordCount); + ByteWriter.WriteBytes(buffer, ref offset, SMBParameters); + LittleEndianWriter.WriteUInt16(buffer, ref offset, byteCount); + ByteWriter.WriteBytes(buffer, ref offset, SMBData); + + return buffer; + } + + public static SMBCommand ReadCommand(byte[] buffer, int offset, CommandName commandName, SMBHeader header) + { + if ((header.Flags & HeaderFlags.Reply) > 0) + { + return ReadCommandResponse(buffer, offset, commandName, header.UnicodeFlag); + } + else + { + return ReadCommandRequest(buffer, offset, commandName, header.UnicodeFlag); + } + } + + public static SMBCommand ReadCommandRequest(byte[] buffer, int offset, CommandName commandName, bool isUnicode) + { + switch (commandName) + { + case CommandName.SMB_COM_CREATE_DIRECTORY: + return new CreateDirectoryRequest(buffer, offset, isUnicode); + case CommandName.SMB_COM_DELETE_DIRECTORY: + return new DeleteDirectoryRequest(buffer, offset, isUnicode); + case CommandName.SMB_COM_CLOSE: + return new CloseRequest(buffer, offset); + case CommandName.SMB_COM_FLUSH: + return new FlushRequest(buffer, offset); + case CommandName.SMB_COM_DELETE: + return new DeleteRequest(buffer, offset, isUnicode); + case CommandName.SMB_COM_RENAME: + return new RenameRequest(buffer, offset, isUnicode); + case CommandName.SMB_COM_QUERY_INFORMATION: + return new QueryInformationRequest(buffer, offset, isUnicode); + case CommandName.SMB_COM_SET_INFORMATION: + return new SetInformationRequest(buffer, offset, isUnicode); + case CommandName.SMB_COM_READ: + return new ReadRequest(buffer, offset); + case CommandName.SMB_COM_WRITE: + return new WriteRequest(buffer, offset); + case CommandName.SMB_COM_CHECK_DIRECTORY: + return new CheckDirectoryRequest(buffer, offset, isUnicode); + case CommandName.SMB_COM_WRITE_RAW: + return new WriteRawRequest(buffer, offset); + case CommandName.SMB_COM_SET_INFORMATION2: + return new SetInformation2Request(buffer, offset); + case CommandName.SMB_COM_LOCKING_ANDX: + return new LockingAndXRequest(buffer, offset); + case CommandName.SMB_COM_TRANSACTION: + return new TransactionRequest(buffer, offset, isUnicode); + case CommandName.SMB_COM_TRANSACTION_SECONDARY: + return new TransactionSecondaryRequest(buffer, offset); + case CommandName.SMB_COM_ECHO: + return new EchoRequest(buffer, offset); + case CommandName.SMB_COM_OPEN_ANDX: + return new OpenAndXRequest(buffer, offset, isUnicode); + case CommandName.SMB_COM_READ_ANDX: + return new ReadAndXRequest(buffer, offset); + case CommandName.SMB_COM_WRITE_ANDX: + return new WriteAndXRequest(buffer, offset, isUnicode); + case CommandName.SMB_COM_TRANSACTION2: + return new Transaction2Request(buffer, offset, isUnicode); + case CommandName.SMB_COM_TRANSACTION2_SECONDARY: + return new Transaction2SecondaryRequest(buffer, offset); + case CommandName.SMB_COM_FIND_CLOSE2: + return new FindClose2Request(buffer, offset); + case CommandName.SMB_COM_TREE_DISCONNECT: + return new TreeDisconnectRequest(buffer, offset); + case CommandName.SMB_COM_NEGOTIATE: + return new NegotiateRequest(buffer, offset); + case CommandName.SMB_COM_SESSION_SETUP_ANDX: + { + byte wordCount = ByteReader.ReadByte(buffer, offset); + if (wordCount * 2 == SessionSetupAndXRequest.ParametersLength) + { + return new SessionSetupAndXRequest(buffer, offset, isUnicode); + } + else if (wordCount * 2 == SessionSetupAndXRequestExtended.ParametersLength) + { + return new SessionSetupAndXRequestExtended(buffer, offset, isUnicode); + } + else + { + throw new InvalidRequestException(); + } + } + case CommandName.SMB_COM_LOGOFF_ANDX: + return new LogoffAndXRequest(buffer, offset); + case CommandName.SMB_COM_TREE_CONNECT_ANDX: + return new TreeConnectAndXRequest(buffer, offset, isUnicode); + case CommandName.SMB_COM_NT_TRANSACT: + return new NTTransactRequest(buffer, offset); + case CommandName.SMB_COM_NT_TRANSACT_SECONDARY: + return new NTTransactSecondaryRequest(buffer, offset); + case CommandName.SMB_COM_NT_CREATE_ANDX: + return new NTCreateAndXRequest(buffer, offset, isUnicode); + default: + throw new NotImplementedException("SMB Command 0x" + ((byte)commandName).ToString("X")); + } + } + + public static SMBCommand ReadCommandResponse(byte[] buffer, int offset, CommandName commandName, bool isUnicode) + { + byte wordCount = ByteReader.ReadByte(buffer, offset); + switch (commandName) + { + case CommandName.SMB_COM_CREATE_DIRECTORY: + return new CreateDirectoryResponse(buffer, offset); + case CommandName.SMB_COM_DELETE_DIRECTORY: + return new DeleteDirectoryResponse(buffer, offset); + case CommandName.SMB_COM_CLOSE: + return new CloseResponse(buffer, offset); + case CommandName.SMB_COM_FLUSH: + return new FlushResponse(buffer, offset); + case CommandName.SMB_COM_DELETE: + return new DeleteResponse(buffer, offset); + case CommandName.SMB_COM_RENAME: + return new RenameResponse(buffer, offset); + case CommandName.SMB_COM_QUERY_INFORMATION: + return new QueryInformationResponse(buffer, offset); + case CommandName.SMB_COM_SET_INFORMATION: + return new SetInformationResponse(buffer, offset); + case CommandName.SMB_COM_READ: + return new ReadResponse(buffer, offset); + case CommandName.SMB_COM_WRITE: + return new WriteResponse(buffer, offset); + case CommandName.SMB_COM_CHECK_DIRECTORY: + return new CheckDirectoryResponse(buffer, offset); + case CommandName.SMB_COM_WRITE_RAW: + return new WriteRawInterimResponse(buffer, offset); + case CommandName.SMB_COM_WRITE_COMPLETE: + return new WriteRawFinalResponse(buffer, offset); + case CommandName.SMB_COM_SET_INFORMATION2: + return new SetInformation2Response(buffer, offset); + case CommandName.SMB_COM_LOCKING_ANDX: + return new LockingAndXResponse(buffer, offset); + case CommandName.SMB_COM_TRANSACTION: + return new TransactionResponse(buffer, offset); + case CommandName.SMB_COM_ECHO: + return new EchoResponse(buffer, offset); + case CommandName.SMB_COM_OPEN_ANDX: + { + if (wordCount * 2 == OpenAndXResponse.ParametersLength) + { + throw new NotImplementedException(); + } + else if (wordCount * 2 == OpenAndXResponseExtended.ParametersLength) + { + throw new NotImplementedException(); + } + else + { + throw new InvalidRequestException(); ; + } + } + case CommandName.SMB_COM_READ_ANDX: + return new ReadAndXResponse(buffer, offset, isUnicode); + case CommandName.SMB_COM_WRITE_ANDX: + return new WriteAndXResponse(buffer, offset); + case CommandName.SMB_COM_TRANSACTION2: + return new Transaction2Response(buffer, offset); + case CommandName.SMB_COM_FIND_CLOSE2: + return new FindClose2Response(buffer, offset); + case CommandName.SMB_COM_TREE_DISCONNECT: + return new TreeDisconnectResponse(buffer, offset); + case CommandName.SMB_COM_NEGOTIATE: + { + if (wordCount * 2 == NegotiateResponseNTLM.ParametersLength) + { + throw new NotImplementedException(); + //return new NegotiateResponseNTLM(header.UnicodeFlag); + } + else if (wordCount * 2 == NegotiateResponseNTLMExtended.ParametersLength) + { + throw new NotImplementedException(); + //return new NegotiateResponseNTLMExtended(header.UnicodeFlag); + } + else + { + throw new InvalidRequestException();; + } + } + case CommandName.SMB_COM_SESSION_SETUP_ANDX: + return new SessionSetupAndXResponse(buffer, offset, isUnicode); + case CommandName.SMB_COM_LOGOFF_ANDX: + return new LogoffAndXResponse(buffer, offset); + case CommandName.SMB_COM_TREE_CONNECT_ANDX: + return new TreeConnectAndXResponse(buffer, offset, isUnicode); + case CommandName.SMB_COM_NT_TRANSACT: + { + if (wordCount * 2 == NTTransactInterimResponse.ParametersLength) + { + return new NTTransactInterimResponse(buffer, offset); + } + else + { + return new NTTransactResponse(buffer, offset); + } + } + case CommandName.SMB_COM_NT_CREATE_ANDX: + return new NTCreateAndXResponse(buffer, offset); + default: + throw new NotImplementedException("SMB Command 0x" + ((byte)commandName).ToString("X")); + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXRequest.cs b/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXRequest.cs new file mode 100644 index 0000000..30fa905 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXRequest.cs @@ -0,0 +1,73 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_SESSION_SETUP_ANDX Request + /// + public class SessionSetupAndXRequest : SMBAndXCommand + { + public const int ParametersLength = 26; + // Parameters: + public ushort MaxBufferSize; + public ushort MaxMpxCount; + public ushort VcNumber; + public uint SessionKey; + //ushort OEMPasswordLength; + //ushort UnicodePasswordLength; + public uint Reserved; + public ServerCapabilities Capabilities; + // Data: + public byte[] OEMPassword; + public byte[] UnicodePassword; + // Padding + public string AccountName; // SMB_STRING (If Unicode, this field MUST be aligned to start on a 2-byte boundary from the start of the SMB header) + public string PrimaryDomain; // SMB_STRING (this field WILL be aligned to start on a 2-byte boundary from the start of the SMB header) + public string NativeOS; // SMB_STRING (this field WILL be aligned to start on a 2-byte boundary from the start of the SMB header) + public string NativeLanMan; // SMB_STRING (this field WILL be aligned to start on a 2-byte boundary from the start of the SMB header) + + public SessionSetupAndXRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + MaxBufferSize = LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + MaxMpxCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 6); + VcNumber = LittleEndianConverter.ToUInt16(this.SMBParameters, 8); + SessionKey = LittleEndianConverter.ToUInt32(this.SMBParameters, 10); + ushort OEMPasswordLength = LittleEndianConverter.ToUInt16(this.SMBParameters, 14); + ushort UnicodePasswordLength = LittleEndianConverter.ToUInt16(this.SMBParameters, 16); + Reserved = LittleEndianConverter.ToUInt32(this.SMBParameters, 18); + Capabilities = (ServerCapabilities)LittleEndianConverter.ToUInt32(this.SMBParameters, 22); + + OEMPassword = ByteReader.ReadBytes(this.SMBData, 0, OEMPasswordLength); + UnicodePassword = ByteReader.ReadBytes(this.SMBData, OEMPasswordLength, UnicodePasswordLength); + + int dataOffset = OEMPasswordLength + UnicodePasswordLength; + if (isUnicode) + { + // wordCount is 1 byte + int padding = (1 + OEMPasswordLength + UnicodePasswordLength) % 2; + dataOffset += padding; + } + AccountName = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + PrimaryDomain = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + NativeOS = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + NativeLanMan = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_SESSION_SETUP_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXRequestExtended.cs b/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXRequestExtended.cs new file mode 100644 index 0000000..400a276 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXRequestExtended.cs @@ -0,0 +1,63 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_SESSION_SETUP_ANDX Extended Request + /// + public class SessionSetupAndXRequestExtended : SMBAndXCommand + { + public const int ParametersLength = 24; + // Parameters: + public ushort MaxBufferSize; + public ushort MaxMpxCount; + public ushort VcNumber; + public uint SessionKey; + //ushort SecurityBlobLength; + public uint Reserved; + public ServerCapabilities Capabilities; + // Data: + public byte[] SecurityBlob; + public string NativeOS; // SMB_STRING (If Unicode, this field MUST be aligned to start on a 2-byte boundary from the start of the SMB header) + public string NativeLanMan; // SMB_STRING (this field WILL be aligned to start on a 2-byte boundary from the start of the SMB header) + + public SessionSetupAndXRequestExtended(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + MaxBufferSize = LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + MaxMpxCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 6); + VcNumber = LittleEndianConverter.ToUInt16(this.SMBParameters, 8); + SessionKey = LittleEndianConverter.ToUInt32(this.SMBParameters, 10); + ushort securityBlobLength = LittleEndianConverter.ToUInt16(this.SMBParameters, 14); + Reserved = LittleEndianConverter.ToUInt32(this.SMBParameters, 16); + Capabilities = (ServerCapabilities)LittleEndianConverter.ToUInt32(this.SMBParameters, 20); + + SecurityBlob = ByteReader.ReadBytes(this.SMBData, 0, securityBlobLength); + + int dataOffset = SecurityBlob.Length; + if (isUnicode) + { + int padding = securityBlobLength % 2; + dataOffset += padding; + } + NativeOS = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + NativeLanMan = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_SESSION_SETUP_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXResponse.cs b/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXResponse.cs new file mode 100644 index 0000000..fccc681 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXResponse.cs @@ -0,0 +1,76 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public class SessionSetupAndXResponse : SMBAndXCommand + { + public const int ParametersLength = 6; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public SessionSetupAction Action; + // Data: + public string NativeOS; // SMB_STRING (If Unicode, this field MUST be aligned to start on a 2-byte boundary from the start of the SMB header) + public string NativeLanMan; // SMB_STRING (this field WILL be aligned to start on a 2-byte boundary from the start of the SMB header) + public string PrimaryDomain; // SMB_STRING (this field WILL be aligned to start on a 2-byte boundary from the start of the SMB header) + + public SessionSetupAndXResponse() : base() + { + } + + public SessionSetupAndXResponse(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + Action = (SessionSetupAction)LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + + int dataOffset = 0; + if (isUnicode) + { + dataOffset++; + } + NativeOS = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + NativeLanMan = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + PrimaryDomain = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, (ushort)Action); + + int offset = 0; + if (isUnicode) + { + int padding = 1; // 1 byte padding for 2 byte alignment + this.SMBData = new byte[padding + NativeOS.Length * 2 + NativeLanMan.Length * 2 + PrimaryDomain.Length * 2 + 6]; + offset = padding; + } + else + { + this.SMBData = new byte[NativeOS.Length + NativeLanMan.Length + PrimaryDomain.Length + 3]; + } + SMBHelper.WriteSMBString(this.SMBData, ref offset, isUnicode, NativeOS); + SMBHelper.WriteSMBString(this.SMBData, ref offset, isUnicode, NativeLanMan); + SMBHelper.WriteSMBString(this.SMBData, ref offset, isUnicode, PrimaryDomain); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_SESSION_SETUP_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXResponseExtended.cs b/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXResponseExtended.cs new file mode 100644 index 0000000..7ffd593 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/SessionSetupAndXResponseExtended.cs @@ -0,0 +1,71 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public class SessionSetupAndXResponseExtended : SMBAndXCommand + { + public const int ParametersLength = 8; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public SessionSetupAction Action; + //ushort SecurityBlobLength; + // Data: + public byte[] SecurityBlob; + public string NativeOS; // SMB_STRING (If Unicode, this field MUST be aligned to start on a 2-byte boundary from the start of the SMB header) + public string NativeLanMan; // SMB_STRING (this field WILL be aligned to start on a 2-byte boundary from the start of the SMB header) + + public SessionSetupAndXResponseExtended() : base() + { + SecurityBlob = new byte[0]; + NativeOS = String.Empty; + NativeLanMan = String.Empty; + } + + public override byte[] GetBytes(bool isUnicode) + { + ushort securityBlobLength = (ushort)SecurityBlob.Length; + + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, (ushort)Action); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 6, securityBlobLength); + + int padding = 0; + if (isUnicode) + { + // wordCount is 1 byte + padding = (1 + securityBlobLength) % 2; + this.SMBData = new byte[SecurityBlob.Length + padding + NativeOS.Length * 2 + NativeLanMan.Length * 2 + 4]; + } + else + { + this.SMBData = new byte[SecurityBlob.Length + NativeOS.Length + NativeLanMan.Length + 2]; + } + int offset = 0; + ByteWriter.WriteBytes(this.SMBData, ref offset, SecurityBlob); + offset += padding; + SMBHelper.WriteSMBString(this.SMBData, ref offset, isUnicode, NativeOS); + SMBHelper.WriteSMBString(this.SMBData, ref offset, isUnicode, NativeLanMan); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_SESSION_SETUP_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/SetInformation2Request.cs b/SMBLibrary/SMB1/SMBCommands/SetInformation2Request.cs new file mode 100644 index 0000000..40fbea1 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/SetInformation2Request.cs @@ -0,0 +1,75 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_SET_INFORMATION2 Request + /// + public class SetInformation2Request : SMBCommand + { + public const int ParametersLength = 14; + // Parameters: + public ushort FID; + public DateTime? CreationDateTime; // A date and time value of 0 indicates to the server that the values MUST NOT be changed + public DateTime? LastAccessDateTime; // A date and time value of 0 indicates to the server that the values MUST NOT be changed + public DateTime? LastWriteDateTime; // A date and time value of 0 indicates to the server that the values MUST NOT be changed + + public SetInformation2Request() : base() + { + } + + public SetInformation2Request(byte[] buffer, int offset) : base(buffer, offset, false) + { + FID = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + CreationDateTime = ReadSetSMBDateTime(this.SMBParameters, 2); + LastAccessDateTime = ReadSetSMBDateTime(this.SMBParameters, 6); + LastWriteDateTime = ReadSetSMBDateTime(this.SMBParameters, 10); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, FID); + WriteSetSMBDateTime(this.SMBParameters, 2, CreationDateTime); + WriteSetSMBDateTime(this.SMBParameters, 6, LastAccessDateTime); + WriteSetSMBDateTime(this.SMBParameters, 10, LastWriteDateTime); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_SET_INFORMATION2; + } + } + + public static DateTime? ReadSetSMBDateTime(byte[] buffer, int offset) + { + uint value = LittleEndianConverter.ToUInt32(buffer, offset); + if (value > 0) + { + return SMBHelper.ReadSMBDateTime(buffer, offset); + } + return null; + } + + public static void WriteSetSMBDateTime(byte[] buffer, int offset, DateTime? datetime) + { + if (datetime.HasValue) + { + SMBHelper.WriteSMBDateTime(buffer, offset, datetime.Value); + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/SetInformation2Response.cs b/SMBLibrary/SMB1/SMBCommands/SetInformation2Response.cs new file mode 100644 index 0000000..01192a0 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/SetInformation2Response.cs @@ -0,0 +1,40 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_SET_INFORMATION2 Response + /// + public class SetInformation2Response : SMBCommand + { + public SetInformation2Response() : base() + { + } + + public SetInformation2Response(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override byte[] GetBytes(bool isUnicode) + { + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_SET_INFORMATION2; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/SetInformationRequest.cs b/SMBLibrary/SMB1/SMBCommands/SetInformationRequest.cs new file mode 100644 index 0000000..74b0a9e --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/SetInformationRequest.cs @@ -0,0 +1,80 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_SET_INFORMATION Request + /// + public class SetInformationRequest : SMBCommand + { + public const int ParametersLength = 16; + public const int SupportedBufferFormat = 0x04; + // Parameters: + public FileAttributes FileAttributes; + public DateTime LastWriteTime; + public byte[] Reserved; // 10 bytes + // Data: + public byte BufferFormat; + public string FileName; // SMB_STRING + + public SetInformationRequest() : base() + { + Reserved = new byte[10]; + BufferFormat = SupportedBufferFormat; + } + + public SetInformationRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + FileAttributes = (FileAttributes)LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + LastWriteTime = SMBHelper.ReadUTime(this.SMBParameters, 2); + Reserved = ByteReader.ReadBytes(this.SMBParameters, 6, 10); + + BufferFormat = ByteReader.ReadByte(this.SMBData, 0); + if (BufferFormat != SupportedBufferFormat) + { + throw new InvalidRequestException("Unsupported Buffer Format"); + } + FileName = SMBHelper.ReadSMBString(this.SMBData, 1, isUnicode); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, (ushort)FileAttributes); + SMBHelper.WriteUTime(this.SMBParameters, 2, LastWriteTime); + ByteWriter.WriteBytes(this.SMBParameters, 6, Reserved, 10); + + int length = 1; + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + this.SMBData = new byte[length]; + ByteWriter.WriteByte(this.SMBData, 0, BufferFormat); + SMBHelper.WriteSMBString(this.SMBData, 1, isUnicode, FileName); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_SET_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/SetInformationResponse.cs b/SMBLibrary/SMB1/SMBCommands/SetInformationResponse.cs new file mode 100644 index 0000000..a33bc81 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/SetInformationResponse.cs @@ -0,0 +1,40 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_SET_INFORMATION Response + /// + public class SetInformationResponse : SMBCommand + { + public SetInformationResponse() : base() + { + } + + public SetInformationResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override byte[] GetBytes(bool isUnicode) + { + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_SET_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/Transaction2Request.cs b/SMBLibrary/SMB1/SMBCommands/Transaction2Request.cs new file mode 100644 index 0000000..7389f99 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/Transaction2Request.cs @@ -0,0 +1,37 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_TRANSACTION2 Request + /// The SMB_COM_TRANSACTION2 request format is similar to that of the SMB_COM_TRANSACTION request except for the Name field. + /// The differences are in the subcommands supported, and in the purposes and usages of some of the fields. + /// + public class Transaction2Request : TransactionRequest + { + public Transaction2Request() : base() + { + } + + public Transaction2Request(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_TRANSACTION2; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/Transaction2Response.cs b/SMBLibrary/SMB1/SMBCommands/Transaction2Response.cs new file mode 100644 index 0000000..30d179d --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/Transaction2Response.cs @@ -0,0 +1,36 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_TRANSACTION2 Response + /// The SMB_COM_TRANSACTION2 response format is identical to that of the SMB_COM_TRANSACTION response. + /// + public class Transaction2Response : TransactionResponse + { + public Transaction2Response() : base() + { + } + + public Transaction2Response(byte[] buffer, int offset) : base(buffer, offset) + { + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_TRANSACTION2; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/Transaction2SecondaryRequest.cs b/SMBLibrary/SMB1/SMBCommands/Transaction2SecondaryRequest.cs new file mode 100644 index 0000000..caeb30a --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/Transaction2SecondaryRequest.cs @@ -0,0 +1,81 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_TRANSACTION2_SECONDARY Request + /// + public class Transaction2SecondaryRequest : TransactionSecondaryRequest + { + public new const int SMBParametersLength = 18; + // Parameters: + public ushort FID; + + public Transaction2SecondaryRequest() : base() + { + } + + public Transaction2SecondaryRequest(byte[] buffer, int offset) : base(buffer, offset) + { + TotalParameterCount = LittleEndianConverter.ToUInt16(this.SMBData, 0); + TotalDataCount = LittleEndianConverter.ToUInt16(this.SMBData, 2); + ParameterCount = LittleEndianConverter.ToUInt16(this.SMBData, 4); + ParameterOffset = LittleEndianConverter.ToUInt16(this.SMBData, 6); + ParameterDisplacement = LittleEndianConverter.ToUInt16(this.SMBData, 8); + DataCount = LittleEndianConverter.ToUInt16(this.SMBData, 10); + DataOffset = LittleEndianConverter.ToUInt16(this.SMBData, 12); + DataDisplacement = LittleEndianConverter.ToUInt16(this.SMBData, 14); + FID = LittleEndianConverter.ToUInt16(this.SMBData, 16); + + TransParameters = ByteReader.ReadBytes(buffer, ParameterOffset, ParameterCount); + TransData = ByteReader.ReadBytes(buffer, DataOffset, DataCount); + } + + public override byte[] GetBytes(bool isUnicode) + { + ParameterCount = (ushort)TransParameters.Length; + DataCount = (ushort)TransData.Length; + + ParameterOffset = (ushort)(SMBHeader.Length + SMBParametersLength); + int padding1 = (4 - (ParameterOffset % 4)) % 4; + ParameterOffset += (ushort)padding1; + DataOffset = (ushort)(ParameterOffset + ParameterCount); + int padding2 = (4 - (DataOffset % 4)) % 4; + DataOffset += (ushort)padding2; + + this.SMBParameters = new byte[SMBParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, TotalParameterCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 2, TotalDataCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, ParameterCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 6, ParameterOffset); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 8, ParameterDisplacement); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 10, DataCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 12, DataOffset); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 14, DataDisplacement); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 16, FID); + + this.SMBData = new byte[ParameterCount + DataCount + padding1 + padding2]; + ByteWriter.WriteBytes(this.SMBData, padding1, TransParameters); + ByteWriter.WriteBytes(this.SMBData, padding1 + ParameterCount + padding2, TransData); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_TRANSACTION2_SECONDARY; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/TransactionRequest.cs b/SMBLibrary/SMB1/SMBCommands/TransactionRequest.cs new file mode 100644 index 0000000..0c2a806 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/TransactionRequest.cs @@ -0,0 +1,172 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_TRANSACTION Request + /// + public class TransactionRequest : SMBCommand + { + public const int FixedSMBParametersLength = 28; + // Parameters: + public ushort TotalParameterCount; + public ushort TotalDataCount; + public ushort MaxParameterCount; + public ushort MaxDataCount; + public byte MaxSetupCount; + public byte Reserved1; + public TransactionFlags Flags; + public uint Timeout; + public ushort Reserved2; + //ushort ParameterCount; + //ushort ParameterOffset; + //ushort DataCount; + //ushort DataOffset; + //byte SetupCount; // In 2-byte words + public byte Reserved3; + public byte[] Setup; + // Data: + public string Name; // SMB_STRING (If Unicode, this field MUST be aligned to start on a 2-byte boundary from the start of the SMB header) + // Padding (alignment to 4 byte boundary) + public byte[] TransParameters; // Trans_Parameters + // Padding (alignment to 4 byte boundary) + public byte[] TransData; // Trans_Data + + public TransactionRequest() : base() + { + Name = String.Empty; + } + + public TransactionRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + TotalParameterCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + TotalDataCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 2); + MaxParameterCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + MaxDataCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 6); + MaxSetupCount = ByteReader.ReadByte(this.SMBParameters, 8); + Reserved1 = ByteReader.ReadByte(this.SMBParameters, 9); + Flags = (TransactionFlags)LittleEndianConverter.ToUInt16(this.SMBParameters, 10); + Timeout = LittleEndianConverter.ToUInt32(this.SMBParameters, 12); + Reserved2 = LittleEndianConverter.ToUInt16(this.SMBParameters, 16); + ushort ParameterCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 18); + ushort ParameterOffset = LittleEndianConverter.ToUInt16(this.SMBParameters, 20); + ushort DataCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 22); + ushort DataOffset = LittleEndianConverter.ToUInt16(this.SMBParameters, 24); + byte SetupCount = ByteReader.ReadByte(this.SMBParameters, 26); + Reserved3 = ByteReader.ReadByte(this.SMBParameters, 27); + Setup = ByteReader.ReadBytes(this.SMBParameters, 28, SetupCount * 2); + + if (this.SMBData.Length > 0) // Workaround, Some SAMBA clients will set ByteCount to 0 (Popcorn Hour A-400) + { + int dataOffset = 0; + if (this is Transaction2Request) + { + Name = String.Empty; + dataOffset += 1; + } + else + { + if (isUnicode) + { + int namePadding = 1; + dataOffset += namePadding; + } + Name = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + } + } + TransParameters = ByteReader.ReadBytes(buffer, ParameterOffset, ParameterCount); + TransData = ByteReader.ReadBytes(buffer, DataOffset, DataCount); + } + + public override byte[] GetBytes(bool isUnicode) + { + byte SetupCount = (byte)(Setup.Length / 2); + ushort ParameterCount = (ushort)TransParameters.Length; + ushort DataCount = (ushort)TransData.Length; + + // WordCount + ByteCount are additional 3 bytes + ushort ParameterOffset = (ushort)(SMBHeader.Length + 3 + (FixedSMBParametersLength + Setup.Length)); + if (this is Transaction2Request) + { + ParameterOffset += 1; + } + else + { + if (isUnicode) + { + ParameterOffset += (ushort)(Name.Length * 2 + 2); + } + else + { + ParameterOffset += (ushort)(Name.Length + 1); + } + } + int padding1 = (4 - (ParameterOffset % 4)) % 4; + ParameterOffset += (ushort)padding1; + ushort DataOffset = (ushort)(ParameterOffset + ParameterCount); + int padding2 = (4 - (DataOffset % 4)) % 4; + DataOffset += (ushort)padding2; + + this.SMBParameters = new byte[FixedSMBParametersLength + Setup.Length]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, TotalParameterCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 2, TotalDataCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, MaxParameterCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 6, MaxDataCount); + ByteWriter.WriteByte(this.SMBParameters, 8, MaxSetupCount); + ByteWriter.WriteByte(this.SMBParameters, 9, Reserved1); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 10, (ushort)Flags); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 12, Timeout); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 16, Reserved2); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 18, ParameterCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 20, ParameterOffset); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 22, DataCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 24, DataOffset); + ByteWriter.WriteByte(this.SMBParameters, 26, SetupCount); + ByteWriter.WriteByte(this.SMBParameters, 27, Reserved3); + ByteWriter.WriteBytes(this.SMBParameters, 28, Setup); + + int offset; + if (this is Transaction2Request) + { + offset = 0; + this.SMBData = new byte[1 + ParameterCount + DataCount + padding1 + padding2]; + } + else + { + if (isUnicode) + { + int namePadding = 1; + offset = namePadding; + this.SMBData = new byte[namePadding + Name.Length * 2 + 2 + ParameterCount + DataCount + padding1 + padding2]; + } + else + { + offset = 0; + this.SMBData = new byte[Name.Length + 1 + ParameterCount + DataCount + padding1 + padding2]; + } + } + SMBHelper.WriteSMBString(this.SMBData, ref offset, isUnicode, Name); + ByteWriter.WriteBytes(this.SMBData, offset + padding1, TransParameters); + ByteWriter.WriteBytes(this.SMBData, offset + padding1 + ParameterCount + padding2, TransData); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_TRANSACTION; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/TransactionResponse.cs b/SMBLibrary/SMB1/SMBCommands/TransactionResponse.cs new file mode 100644 index 0000000..6478221 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/TransactionResponse.cs @@ -0,0 +1,126 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_TRANSACTION Response + /// + public class TransactionResponse : SMBCommand + { + public const int FixedSMBParametersLength = 20; + // Parameters: + public ushort TotalParameterCount; + public ushort TotalDataCount; + public ushort Reserved1; + //ushort ParameterCount; + //ushort ParameterOffset; + public ushort ParameterDisplacement; + //ushort DataCount; + //ushort DataOffset; + public ushort DataDisplacement; + //byte SetupCount; // In 2-byte words + public byte Reserved2; + public byte[] Setup; + // Data: + // Padding (alignment to 4 byte boundary) + public byte[] TransParameters; // Trans_Parameters + // Padding (alignment to 4 byte boundary) + public byte[] TransData; // Trans_Data + + public TransactionResponse() : base() + { + Setup = new byte[0]; + TransParameters = new byte[0]; + TransData = new byte[0]; + } + + public TransactionResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + TotalParameterCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + TotalDataCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 2); + Reserved1 = LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + ushort parameterCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 6); + ushort parameterOffset = LittleEndianConverter.ToUInt16(this.SMBParameters, 8); + ParameterDisplacement = LittleEndianConverter.ToUInt16(this.SMBParameters, 10); + ushort dataCount = LittleEndianConverter.ToUInt16(this.SMBParameters, 12); + ushort dataOffset = LittleEndianConverter.ToUInt16(this.SMBParameters, 14); + DataDisplacement = LittleEndianConverter.ToUInt16(this.SMBParameters, 16); + byte setupCount = ByteReader.ReadByte(this.SMBParameters, 18); + Reserved2 = ByteReader.ReadByte(this.SMBParameters, 19); + Setup = ByteReader.ReadBytes(this.SMBParameters, 20, setupCount * 2); + + TransParameters = ByteReader.ReadBytes(buffer, parameterOffset, parameterCount); + TransData = ByteReader.ReadBytes(buffer, dataOffset, dataCount); + } + + public override byte[] GetBytes(bool isUnicode) + { + if (TransData.Length > UInt16.MaxValue) + { + throw new ArgumentException("Invalid Trans_Data length"); + } + byte setupCount = (byte)(Setup.Length / 2); + ushort parameterCount = (ushort)TransParameters.Length; + ushort dataCount = (ushort)TransData.Length; + + // WordCount + ByteCount are additional 3 bytes + ushort parameterOffset = (ushort)(SMBHeader.Length + 3 + (FixedSMBParametersLength + Setup.Length)); + int padding1 = (4 - (parameterOffset %4)) % 4; + parameterOffset += (ushort)padding1; + ushort dataOffset = (ushort)(parameterOffset + parameterCount); + int padding2 = (4 - (dataOffset % 4)) % 4; + dataOffset += (ushort)padding2; + + this.SMBParameters = new byte[FixedSMBParametersLength + Setup.Length]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, TotalParameterCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 2, TotalDataCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, Reserved1); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 6, parameterCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 8, parameterOffset); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 10, ParameterDisplacement); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 12, dataCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 14, dataOffset); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 16, DataDisplacement); + ByteWriter.WriteByte(this.SMBParameters, 18, setupCount); + ByteWriter.WriteByte(this.SMBParameters, 19, Reserved2); + ByteWriter.WriteBytes(this.SMBParameters, 20, Setup); + + this.SMBData = new byte[parameterCount + dataCount + padding1 + padding2]; + ByteWriter.WriteBytes(this.SMBData, padding1, TransParameters); + ByteWriter.WriteBytes(this.SMBData, padding1 + parameterCount + padding2, TransData); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_TRANSACTION; + } + } + + public static int CalculateMessageSize(int setupLength, int trans2ParametersLength, int trans2DataLength) + { + int parameterOffset = SMBHeader.Length + 3 + (FixedSMBParametersLength + setupLength); + int padding1 = (4 - (parameterOffset %4)) % 4; + parameterOffset += padding1; + int dataOffset = (parameterOffset + trans2ParametersLength); + int padding2 = (4 - (dataOffset % 4)) % 4; + + int messageParametersLength = FixedSMBParametersLength + setupLength; + int messageDataLength = trans2ParametersLength + trans2DataLength + padding1 + padding2; + // WordCount + ByteCount are additional 3 bytes + return SMBHeader.Length + messageParametersLength + messageDataLength + 3; + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/TransactionSecondaryRequest.cs b/SMBLibrary/SMB1/SMBCommands/TransactionSecondaryRequest.cs new file mode 100644 index 0000000..c75da71 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/TransactionSecondaryRequest.cs @@ -0,0 +1,92 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_TRANSACTION_SECONDARY Request + /// + public class TransactionSecondaryRequest : SMBCommand + { + public const int SMBParametersLength = 16; + // Parameters: + public ushort TotalParameterCount; + public ushort TotalDataCount; + protected ushort ParameterCount; + protected ushort ParameterOffset; + public ushort ParameterDisplacement; + protected ushort DataCount; + protected ushort DataOffset; + public ushort DataDisplacement; + // Data: + // Padding (alignment to 4 byte boundary) + public byte[] TransParameters; // Trans_Parameters + // Padding (alignment to 4 byte boundary) + public byte[] TransData; // Trans_Data + + public TransactionSecondaryRequest() : base() + { + } + + public TransactionSecondaryRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + TotalParameterCount = LittleEndianConverter.ToUInt16(this.SMBData, 0); + TotalDataCount = LittleEndianConverter.ToUInt16(this.SMBData, 2); + ParameterCount = LittleEndianConverter.ToUInt16(this.SMBData, 4); + ParameterOffset = LittleEndianConverter.ToUInt16(this.SMBData, 6); + ParameterDisplacement = LittleEndianConverter.ToUInt16(this.SMBData, 8); + DataCount = LittleEndianConverter.ToUInt16(this.SMBData, 10); + DataOffset = LittleEndianConverter.ToUInt16(this.SMBData, 12); + DataDisplacement = LittleEndianConverter.ToUInt16(this.SMBData, 14); + + TransParameters = ByteReader.ReadBytes(buffer, ParameterOffset, ParameterCount); + TransData = ByteReader.ReadBytes(buffer, DataOffset, DataCount); + } + + public override byte[] GetBytes(bool isUnicode) + { + ParameterCount = (ushort)TransParameters.Length; + DataCount = (ushort)TransData.Length; + + // WordCount + ByteCount are additional 3 bytes + ParameterOffset = (ushort)(SMBHeader.Length + 3 + SMBParametersLength); + int padding1 = (4 - (ParameterOffset % 4)) % 4; + ParameterOffset += (ushort)padding1; + DataOffset = (ushort)(ParameterOffset + ParameterCount); + int padding2 = (4 - (DataOffset % 4)) % 4; + DataOffset += (ushort)padding2; + + this.SMBParameters = new byte[SMBParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, TotalParameterCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 2, TotalDataCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, ParameterCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 6, ParameterOffset); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 8, ParameterDisplacement); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 10, DataCount); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 12, DataOffset); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 14, DataDisplacement); + + this.SMBData = new byte[ParameterCount + DataCount + padding1 + padding2]; + ByteWriter.WriteBytes(this.SMBData, padding1, TransParameters); + ByteWriter.WriteBytes(this.SMBData, padding1 + ParameterCount + padding2, TransData); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_TRANSACTION_SECONDARY; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/TreeConnectAndXRequest.cs b/SMBLibrary/SMB1/SMBCommands/TreeConnectAndXRequest.cs new file mode 100644 index 0000000..c22b7a4 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/TreeConnectAndXRequest.cs @@ -0,0 +1,92 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_TREE_CONNECT_ANDX Request + /// + public class TreeConnectAndXRequest : SMBAndXCommand + { + public const int ParametersLength = 8; + // Parameters: + public TreeConnectFlags Flags; + //ushort PasswordLength; + // Data: + public byte[] Password; + // Padding + public string Path; // SMB_STRING (If Unicode, this field MUST be aligned to start on a 2-byte boundary from the start of the SMB header) + public ServiceName Service; // OEM string + + public TreeConnectAndXRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + int parametersOffset = 4; + Flags = (TreeConnectFlags)LittleEndianReader.ReadUInt16(this.SMBParameters, ref parametersOffset); + ushort passwordLength = LittleEndianReader.ReadUInt16(this.SMBParameters, ref parametersOffset); + + int dataOffset = 0; + Password = ByteReader.ReadBytes(this.SMBData, ref dataOffset, passwordLength); + if (isUnicode) + { + // wordCount is 1 byte + int padding = (1 + passwordLength) % 2; + dataOffset += padding; + } + Path = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + // Should be read as OEM string but it doesn't really matter + string serviceString = ByteReader.ReadNullTerminatedAnsiString(this.SMBData, ref dataOffset); + Service = TreeConnectHelper.GetServiceName(serviceString); + } + + public override byte[] GetBytes(bool isUnicode) + { + ushort passwordLength = (ushort)Password.Length; + + this.SMBParameters = new byte[ParametersLength]; + int parametersOffset = 4; + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, (ushort)Flags); + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, passwordLength); + + string serviceString = TreeConnectHelper.GetServiceString(Service); + int dataLength = Password.Length + serviceString.Length; + if (isUnicode) + { + int padding = (1 + passwordLength) % 2; + dataLength += Path.Length * 2 + 2 + padding; + } + else + { + dataLength += Path.Length + 1; + } + this.SMBData = new byte[dataLength]; + int dataOffset = 0; + ByteWriter.WriteBytes(this.SMBData, ref dataOffset, Password); + if (isUnicode) + { + // wordCount is 1 byte + int padding = (1 + passwordLength) % 2; + dataOffset += padding; + } + SMBHelper.WriteSMBString(this.SMBData, ref dataOffset, isUnicode, Path); + ByteWriter.WriteNullTerminatedAnsiString(this.SMBData, ref dataOffset, serviceString); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_TREE_CONNECT_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/TreeConnectAndXResponse.cs b/SMBLibrary/SMB1/SMBCommands/TreeConnectAndXResponse.cs new file mode 100644 index 0000000..3331151 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/TreeConnectAndXResponse.cs @@ -0,0 +1,75 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_TREE_CONNECT_ANDX Response + /// + public class TreeConnectAndXResponse : SMBAndXCommand + { + public const int ParametersLength = 6; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public OptionalSupportFlags OptionalSupport; + // Data: + public ServiceName Service; // OEM String + public string NativeFileSystem; // SMB_STRING + + public TreeConnectAndXResponse() : base() + { + } + + public TreeConnectAndXResponse(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + OptionalSupport = (OptionalSupportFlags)LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + + int dataOffset = 0; + string serviceString = ByteReader.ReadNullTerminatedAnsiString(this.SMBData, ref dataOffset); + NativeFileSystem = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + + Service = TreeConnectHelper.GetServiceName(serviceString); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, (ushort)OptionalSupport); + + // Should be written as OEM string but it doesn't really matter + string serviceString = TreeConnectHelper.GetServiceString(Service); + if (isUnicode) + { + this.SMBData = new byte[serviceString.Length + NativeFileSystem.Length * 2 + 3]; + } + else + { + this.SMBData = new byte[serviceString.Length + NativeFileSystem.Length + 2]; + } + + int offset = 0; + ByteWriter.WriteNullTerminatedAnsiString(this.SMBData, ref offset, serviceString); + SMBHelper.WriteSMBString(this.SMBData, ref offset, isUnicode, NativeFileSystem); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_TREE_CONNECT_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/TreeConnectAndXResponseExtended.cs b/SMBLibrary/SMB1/SMBCommands/TreeConnectAndXResponseExtended.cs new file mode 100644 index 0000000..20b342c --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/TreeConnectAndXResponseExtended.cs @@ -0,0 +1,83 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_TREE_CONNECT_ANDX Extended Response + /// + public class TreeConnectAndXResponseExtended : SMBAndXCommand + { + public const int ParametersLength = 14; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public OptionalSupportFlags OptionalSupport; + public AccessMask MaximalShareAccessRights; + public AccessMask GuestMaximalShareAccessRights; + // Data: + public ServiceName Service; // OEM String + public string NativeFileSystem; // SMB_STRING + + public TreeConnectAndXResponseExtended() : base() + { + } + + public TreeConnectAndXResponseExtended(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + int parametersOffset = 4; + OptionalSupport = (OptionalSupportFlags)LittleEndianReader.ReadUInt16(this.SMBParameters, ref parametersOffset); + MaximalShareAccessRights = new AccessMask(this.SMBParameters, ref parametersOffset); + GuestMaximalShareAccessRights = new AccessMask(this.SMBParameters, ref parametersOffset); + + int dataOffset = 0; + string serviceString = ByteReader.ReadNullTerminatedAnsiString(this.SMBData, ref dataOffset); + NativeFileSystem = SMBHelper.ReadSMBString(this.SMBData, ref dataOffset, isUnicode); + + Service = TreeConnectHelper.GetServiceName(serviceString); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + int parametersOffset = 4; + LittleEndianWriter.WriteUInt16(this.SMBParameters, ref parametersOffset, (ushort)OptionalSupport); + MaximalShareAccessRights.WriteBytes(this.SMBParameters, ref parametersOffset); + GuestMaximalShareAccessRights.WriteBytes(this.SMBParameters, ref parametersOffset); + + // Should be written as OEM string but it doesn't really matter + string serviceString = TreeConnectHelper.GetServiceString(Service); + if (isUnicode) + { + this.SMBData = new byte[serviceString.Length + NativeFileSystem.Length * 2 + 3]; + } + else + { + this.SMBData = new byte[serviceString.Length + NativeFileSystem.Length + 2]; + } + + int offset = 0; + ByteWriter.WriteNullTerminatedAnsiString(this.SMBData, ref offset, serviceString); + SMBHelper.WriteSMBString(this.SMBData, ref offset, isUnicode, NativeFileSystem); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_TREE_CONNECT_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/TreeDisconnectRequest.cs b/SMBLibrary/SMB1/SMBCommands/TreeDisconnectRequest.cs new file mode 100644 index 0000000..f20ece8 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/TreeDisconnectRequest.cs @@ -0,0 +1,35 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_TREE_DISCONNECT Request + /// + public class TreeDisconnectRequest : SMBCommand + { + public TreeDisconnectRequest() : base() + { + } + + public TreeDisconnectRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_TREE_DISCONNECT; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/TreeDisconnectResponse.cs b/SMBLibrary/SMB1/SMBCommands/TreeDisconnectResponse.cs new file mode 100644 index 0000000..af6e4ac --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/TreeDisconnectResponse.cs @@ -0,0 +1,35 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_TREE_DISCONNECT Response + /// + public class TreeDisconnectResponse : SMBCommand + { + public TreeDisconnectResponse() : base() + { + } + + public TreeDisconnectResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_TREE_DISCONNECT; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/WriteAndXRequest.cs b/SMBLibrary/SMB1/SMBCommands/WriteAndXRequest.cs new file mode 100644 index 0000000..77d62ad --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/WriteAndXRequest.cs @@ -0,0 +1,118 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_WRITE_ANDX Request + /// SMB 1.0: The 2 reserved bytes at offset 18 become DataLengthHigh (used when the CAP_LARGE_WRITEX capability has been negotiated) + /// + public class WriteAndXRequest : SMBAndXCommand + { + public const int ParametersFixedLength = 24; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public ushort FID; + public ulong Offset; // 4 bytes + 4 optional 'OffsetHigh' bytes + public uint Timeout; + public WriteMode WriteMode; + public ushort Remaining; + //uint DataLength; // 2 bytes + 2 'DataLengthHigh' bytes + //ushort DataOffset; + // ulong OffsetHigh; // Optional + // Data: + // Optional 1 byte padding + public byte[] Data; + + public WriteAndXRequest() : base() + {} + + public WriteAndXRequest(byte[] buffer, int offset, bool isUnicode) : base(buffer, offset, isUnicode) + { + FID = LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + Offset = LittleEndianConverter.ToUInt32(this.SMBParameters, 6); + Timeout = LittleEndianConverter.ToUInt32(this.SMBParameters, 10); + WriteMode = (WriteMode)LittleEndianConverter.ToUInt16(this.SMBParameters, 14); + Remaining = LittleEndianConverter.ToUInt16(this.SMBParameters, 16); + ushort dataLengthHigh = LittleEndianConverter.ToUInt16(this.SMBParameters, 18); + uint DataLength = LittleEndianConverter.ToUInt16(this.SMBParameters, 20); + ushort DataOffset = LittleEndianConverter.ToUInt16(this.SMBParameters, 22); + if (SMBParameters.Length == ParametersFixedLength + 4) + { + uint offsetHigh = LittleEndianConverter.ToUInt32(this.SMBParameters, 24); + Offset |= ((ulong)offsetHigh << 32); + } + + DataLength |= (uint)(dataLengthHigh << 16); + + Data = ByteReader.ReadBytes(buffer, (int)DataOffset, (int)DataLength); + } + + public override byte[] GetBytes(bool isUnicode) + { + uint DataLength = (uint)Data.Length; + // WordCount + ByteCount are additional 3 bytes + ushort DataOffset = SMBHeader.Length + 3 + ParametersFixedLength; + if (isUnicode) + { + DataOffset++; + } + ushort dataLengthHigh = (ushort)(DataLength >> 16); + + int parametersLength = ParametersFixedLength; + if (Offset > UInt32.MaxValue) + { + parametersLength += 4; + DataOffset += 4; + } + + this.SMBParameters = new byte[parametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, FID); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 6, (uint)(Offset & 0xFFFFFFFF)); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 10, Timeout); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 14, (ushort)WriteMode); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 16, Remaining); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 18, dataLengthHigh); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 20, (ushort)(DataLength & 0xFFFF)); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 22, DataOffset); + if (Offset > UInt32.MaxValue) + { + uint offsetHigh = (uint)(Offset >> 32); + LittleEndianWriter.WriteUInt32(this.SMBParameters, 24, offsetHigh); + } + + int smbDataLength = Data.Length; + if (isUnicode) + { + smbDataLength++; + } + this.SMBData = new byte[smbDataLength]; + int offset = 0; + if (isUnicode) + { + offset++; + } + ByteWriter.WriteBytes(this.SMBData, ref offset, this.Data); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_WRITE_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/WriteAndXResponse.cs b/SMBLibrary/SMB1/SMBCommands/WriteAndXResponse.cs new file mode 100644 index 0000000..2895e9f --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/WriteAndXResponse.cs @@ -0,0 +1,63 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_WRITE_ANDX Response + /// SMB 1.0: The 2 reserved bytes at offset 8 become CountHigh (used when the CAP_LARGE_WRITEX capability has been negotiated) + /// + public class WriteAndXResponse : SMBAndXCommand + { + public const int ParametersLength = 12; + // Parameters: + //CommandName AndXCommand; + //byte AndXReserved; + //ushort AndXOffset; + public uint Count; // The number of bytes written to the file, 2 bytes + 2 'CountHigh' bytes + public ushort Available; + public ushort Reserved; + + public WriteAndXResponse() : base() + {} + + public WriteAndXResponse(byte[] buffer, int offset): base(buffer, offset, false) + { + Count = LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + Available = LittleEndianConverter.ToUInt16(this.SMBParameters, 6); + ushort countHigh = LittleEndianConverter.ToUInt16(this.SMBParameters, 8); + Reserved = LittleEndianConverter.ToUInt16(this.SMBParameters, 10); + + Count |= (uint)(countHigh << 16); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + ushort counthHigh = (ushort)(Count >> 16); + + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, (ushort)(Count & 0xFFFF)); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 6, Available); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 8, counthHigh); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 10, Reserved); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_WRITE_ANDX; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/WriteRawFinalResponse.cs b/SMBLibrary/SMB1/SMBCommands/WriteRawFinalResponse.cs new file mode 100644 index 0000000..73f5cf5 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/WriteRawFinalResponse.cs @@ -0,0 +1,47 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_WRITE_RAW Final Response + /// + public class WriteRawFinalResponse : SMBCommand + { + public ushort ParametersLength = 2; + // Parameters; + public ushort Count; + + public WriteRawFinalResponse() : base() + {} + + public WriteRawFinalResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + Count = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, Count); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_WRITE_COMPLETE; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/WriteRawInterimResponse.cs b/SMBLibrary/SMB1/SMBCommands/WriteRawInterimResponse.cs new file mode 100644 index 0000000..bf4125f --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/WriteRawInterimResponse.cs @@ -0,0 +1,47 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_WRITE_RAW Interim Response + /// + public class WriteRawInterimResponse : SMBCommand + { + public ushort ParametersLength = 2; + // Parameters; + public ushort Available; + + public WriteRawInterimResponse() : base() + {} + + public WriteRawInterimResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + Available = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, Available); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_WRITE_RAW; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/WriteRawRequest.cs b/SMBLibrary/SMB1/SMBCommands/WriteRawRequest.cs new file mode 100644 index 0000000..d010fca --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/WriteRawRequest.cs @@ -0,0 +1,71 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_WRITE_RAW Request + /// + public class WriteRawRequest : SMBCommand + { + public const int ParametersFixedLength = 24; // + 4 optional bytes + // Parameters: + public ushort FID; + public ushort CountOfBytes; + public ushort Reserved1; + public uint Offset; + public uint Timeout; + public WriteMode WriteMode; + public uint Reserved2; + //ushort DataLength; + //ushort DataOffset; + public uint OffsetHigh; // Optional + // Data: + public byte[] Data; + + public WriteRawRequest() : base() + { + Data = new byte[0]; + } + + public WriteRawRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + FID = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + CountOfBytes = LittleEndianConverter.ToUInt16(this.SMBParameters, 2); + Reserved1 = LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + Offset = LittleEndianConverter.ToUInt32(this.SMBParameters, 6); + Timeout = LittleEndianConverter.ToUInt32(this.SMBParameters, 10); + WriteMode = (WriteMode)LittleEndianConverter.ToUInt16(this.SMBParameters, 14); + Reserved2 = LittleEndianConverter.ToUInt32(this.SMBParameters, 16); + ushort dataLength = LittleEndianConverter.ToUInt16(this.SMBParameters, 20); + ushort dataOffset = LittleEndianConverter.ToUInt16(this.SMBParameters, 22); + if (SMBParameters.Length == ParametersFixedLength + 4) + { + OffsetHigh = LittleEndianConverter.ToUInt32(this.SMBParameters, 24); + } + + Data = ByteReader.ReadBytes(buffer, dataOffset, dataLength); + } + + public override byte[] GetBytes(bool isUnicode) + { + throw new NotImplementedException(); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_WRITE_RAW; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/WriteRequest.cs b/SMBLibrary/SMB1/SMBCommands/WriteRequest.cs new file mode 100644 index 0000000..12cdf0b --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/WriteRequest.cs @@ -0,0 +1,83 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_WRITE Request. + /// This command is obsolete. + /// Windows NT4 SP6 will send this command with empty data for some reason. + /// + public class WriteRequest : SMBCommand + { + public const int ParametersLength = 8; + public const int SupportedBufferFormat = 0x01; + // Parameters: + public ushort FID; + public ushort CountOfBytesToWrite; + public ushort WriteOffsetInBytes; + public ushort EstimateOfRemainingBytesToBeWritten; + // Data: + public byte BufferFormat; + // ushort DataLength; + public byte[] Data; + + public WriteRequest() : base() + { + BufferFormat = SupportedBufferFormat; + Data = new byte[0]; + } + + public WriteRequest(byte[] buffer, int offset) : base(buffer, offset, false) + { + FID = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + CountOfBytesToWrite = LittleEndianConverter.ToUInt16(this.SMBParameters, 2); + WriteOffsetInBytes = LittleEndianConverter.ToUInt16(this.SMBParameters, 4); + EstimateOfRemainingBytesToBeWritten = LittleEndianConverter.ToUInt16(this.SMBParameters, 6); + + BufferFormat = ByteReader.ReadByte(this.SMBData, 0); + if (BufferFormat != SupportedBufferFormat) + { + throw new InvalidRequestException("Unsupported Buffer Format"); + } + ushort dataLength = LittleEndianConverter.ToUInt16(this.SMBData, 1); + Data = ByteReader.ReadBytes(this.SMBData, 3, dataLength); + } + + public override byte[] GetBytes(bool isUnicode) + { + if (Data.Length > UInt16.MaxValue) + { + throw new ArgumentException("Invalid Data length"); + } + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, FID); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 2, CountOfBytesToWrite); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 4, WriteOffsetInBytes); + LittleEndianWriter.WriteUInt16(this.SMBParameters, 6, EstimateOfRemainingBytesToBeWritten); + + this.SMBData = new byte[3 + Data.Length]; + ByteWriter.WriteByte(this.SMBData, 0, BufferFormat); + LittleEndianWriter.WriteUInt16(this.SMBData, 1, (ushort)Data.Length); + ByteWriter.WriteBytes(this.SMBData, 3, Data); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_WRITE; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBCommands/WriteResponse.cs b/SMBLibrary/SMB1/SMBCommands/WriteResponse.cs new file mode 100644 index 0000000..f475942 --- /dev/null +++ b/SMBLibrary/SMB1/SMBCommands/WriteResponse.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_COM_WRITE Response. + /// This command is obsolete. + /// Windows NT4 SP6 will send this command with empty data for some reason. + /// + public class WriteResponse : SMBCommand + { + public const int ParametersLength = 2; + // Parameters: + public ushort CountOfBytesWritten; + + public WriteResponse() : base() + { + } + + public WriteResponse(byte[] buffer, int offset) : base(buffer, offset, false) + { + CountOfBytesWritten = LittleEndianConverter.ToUInt16(this.SMBParameters, 0); + } + + public override byte[] GetBytes(bool isUnicode) + { + this.SMBParameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(this.SMBParameters, 0, CountOfBytesWritten); + + return base.GetBytes(isUnicode); + } + + public override CommandName CommandName + { + get + { + return CommandName.SMB_COM_WRITE; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBHeader.cs b/SMBLibrary/SMB1/SMBHeader.cs new file mode 100644 index 0000000..361c537 --- /dev/null +++ b/SMBLibrary/SMB1/SMBHeader.cs @@ -0,0 +1,119 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public class SMBHeader + { + public const int Length = 32; + public static readonly byte[] ProtocolSignature = new byte[] { 0xFF, 0x53, 0x4D, 0x42 }; + + public byte[] Protocol; // byte[4], 0xFF followed by "SMB" + public CommandName Command; + public NTStatus Status; + public HeaderFlags Flags; + public HeaderFlags2 Flags2; + //ushort PIDHigh + public ulong SecurityFeatures; + // public ushort Reserved; + public ushort TID; // Tree ID + //ushort PIDLow; + public ushort UID; // User ID + public ushort MID; // Multiplex ID + + public uint PID; // Process ID + + public SMBHeader() + { + Protocol = ProtocolSignature; + } + + public SMBHeader(byte[] buffer) + { + Protocol = ByteReader.ReadBytes(buffer, 0, 4); + Command = (CommandName)ByteReader.ReadByte(buffer, 4); + Status = (NTStatus)LittleEndianConverter.ToUInt32(buffer, 5); + Flags = (HeaderFlags)ByteReader.ReadByte(buffer, 9); + Flags2 = (HeaderFlags2)LittleEndianConverter.ToUInt16(buffer, 10); + ushort PIDHigh = LittleEndianConverter.ToUInt16(buffer, 12); + SecurityFeatures = LittleEndianConverter.ToUInt64(buffer, 14); + TID = LittleEndianConverter.ToUInt16(buffer, 24); + ushort PIDLow = LittleEndianConverter.ToUInt16(buffer, 26); + UID = LittleEndianConverter.ToUInt16(buffer, 28); + MID = LittleEndianConverter.ToUInt16(buffer, 30); + + PID = (uint)((PIDHigh << 16) | PIDLow); + } + + public void WriteBytes(byte[] buffer, int offset) + { + ushort PIDHigh = (ushort)(PID >> 16); + ushort PIDLow = (ushort)(PID & 0xFFFF); + + ByteWriter.WriteBytes(buffer, offset + 0, Protocol); + ByteWriter.WriteByte(buffer, offset + 4, (byte)Command); + LittleEndianWriter.WriteUInt32(buffer, offset + 5, (uint)Status); + ByteWriter.WriteByte(buffer, offset + 9, (byte)Flags); + LittleEndianWriter.WriteUInt16(buffer, offset + 10, (ushort)Flags2); + LittleEndianWriter.WriteUInt16(buffer, offset + 12, PIDHigh); + LittleEndianWriter.WriteUInt64(buffer, offset + 14, SecurityFeatures); + LittleEndianWriter.WriteUInt16(buffer, offset + 24, TID); + LittleEndianWriter.WriteUInt16(buffer, offset + 26, PIDLow); + LittleEndianWriter.WriteUInt16(buffer, offset + 28, UID); + LittleEndianWriter.WriteUInt16(buffer, offset + 30, MID); + } + + public byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + WriteBytes(buffer, 0); + return buffer; + } + + public bool ReplyFlag + { + get + { + return (Flags & HeaderFlags.Reply) > 0; + } + } + + /// + /// SMB_FLAGS2_EXTENDED_SECURITY + /// + public bool ExtendedSecurityFlag + { + get + { + return (this.Flags2 & HeaderFlags2.ExtendedSecurity) > 0; + } + set + { + if (value) + { + this.Flags2 |= HeaderFlags2.ExtendedSecurity; + } + else + { + this.Flags2 &= ~HeaderFlags2.ExtendedSecurity; + } + } + } + + public bool UnicodeFlag + { + get + { + return (Flags2 & HeaderFlags2.Unicode) > 0; + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBHelper.cs b/SMBLibrary/SMB1/SMBHelper.cs new file mode 100644 index 0000000..daed92b --- /dev/null +++ b/SMBLibrary/SMB1/SMBHelper.cs @@ -0,0 +1,228 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary +{ + public class SMBHelper + { + public static readonly DateTime UTimeNotSpecified = new DateTime(1970, 1, 1); + public static readonly DateTime FileTimeNotSpecified = new DateTime(1601, 1, 1); + + public static DateTime ReadFileTime(byte[] buffer, int offset) + { + long span = LittleEndianConverter.ToInt64(buffer, offset); + if (span >= 0) + { + return DateTime.FromFileTimeUtc(span); + } + else + { + // Tick = 100ns + return DateTime.Now.Subtract(TimeSpan.FromTicks(span)); + } + } + + public static DateTime ReadFileTime(byte[] buffer, ref int offset) + { + offset += 8; + return ReadFileTime(buffer, offset - 8); + } + + public static DateTime ReadSetFileTime(byte[] buffer, int offset) + { + long span = LittleEndianConverter.ToInt64(buffer, offset); + if (span >= 0) + { + return DateTime.FromFileTimeUtc(span); + } + else + { + return FileTimeNotSpecified; + } + } + + public static void WriteFileTime(byte[] buffer, int offset, DateTime time) + { + long span = time.ToFileTimeUtc(); + LittleEndianWriter.WriteInt64(buffer, offset, span); + } + + public static void WriteFileTime(byte[] buffer, ref int offset, DateTime time) + { + WriteFileTime(buffer, offset, time); + offset += 8; + } + + // UTime - The number of seconds since Jan 1, 1970, 00:00:00 + public static DateTime ReadUTime(byte[] buffer, int offset) + { + uint span = LittleEndianConverter.ToUInt32(buffer, offset); + DateTime result = new DateTime(1970, 1, 1); + return result.AddSeconds(span); + } + + public static DateTime ReadUTime(byte[] buffer, ref int offset) + { + offset += 4; + return ReadUTime(buffer, offset - 4); + } + + // UTime - The number of seconds since Jan 1, 1970, 00:00:00 + public static void WriteUTime(byte[] buffer, int offset, DateTime time) + { + TimeSpan timespan = time - new DateTime(1970, 1, 1); + uint span = (uint)timespan.TotalSeconds; + LittleEndianWriter.WriteUInt32(buffer, offset, span); + } + + public static void WriteUTime(byte[] buffer, ref int offset, DateTime time) + { + WriteUTime(buffer, offset, time); + offset += 4; + } + + /// + /// SMB_DATE + /// + public static DateTime ReadSMBDate(byte[] buffer, int offset) + { + ushort value = LittleEndianConverter.ToUInt16(buffer, offset); + int year = ((value & 0xFE00) >> 9) + 1980; + int month = ((value & 0x01E0) >> 5); + int day = (value & 0x001F); + return new DateTime(year, month, day); + } + + public static void WriteSMBDate(byte[] buffer, int offset, DateTime date) + { + int year = date.Year - 1980; + int month = date.Month; + int day = date.Day; + ushort value = (ushort)(year << 9 | month << 5 | day); + LittleEndianWriter.WriteUInt16(buffer, offset, value); + } + + /// + /// SMB_DATE + /// + public static TimeSpan ReadSMBTime(byte[] buffer, int offset) + { + ushort value = LittleEndianConverter.ToUInt16(buffer, offset); + int hours = ((value & 0xF800) >> 11); + int minutes = ((value & 0x07E0) >> 5); + int seconds = (value & 0x001F); + return new TimeSpan(hours, minutes, seconds); + } + + public static void WriteSMBTime(byte[] buffer, int offset, TimeSpan time) + { + ushort value = (ushort)(time.Hours << 11 | time.Minutes << 5 | time.Seconds); + LittleEndianWriter.WriteUInt16(buffer, offset, value); + } + + public static DateTime ReadSMBDateTime(byte[] buffer, int offset) + { + DateTime date = ReadSMBDate(buffer, offset); + TimeSpan time = ReadSMBTime(buffer, offset + 2); + return date.Add(time); + } + + public static DateTime ReadSMBDateTime(byte[] buffer, ref int offset) + { + offset += 4; + return ReadSMBDateTime(buffer, offset - 4); + } + + public static void WriteSMBDateTime(byte[] buffer, int offset, DateTime dateTime) + { + WriteSMBDate(buffer, offset, dateTime.Date); + WriteSMBTime(buffer, offset + 2, dateTime.TimeOfDay); + } + + public static void WriteSMBDateTime(byte[] buffer, ref int offset, DateTime dateTime) + { + WriteSMBDateTime(buffer, offset, dateTime); + offset += 4; + } + + public static string ReadSMBString(byte[] buffer, int offset, bool isUnicode) + { + if (isUnicode) + { + return ByteReader.ReadNullTerminatedUTF16String(buffer, offset); + } + else + { + return ByteReader.ReadNullTerminatedAnsiString(buffer, offset); + } + } + + public static string ReadSMBString(byte[] buffer, ref int offset, bool isUnicode) + { + if (isUnicode) + { + return ByteReader.ReadNullTerminatedUTF16String(buffer, ref offset); + } + else + { + return ByteReader.ReadNullTerminatedAnsiString(buffer, ref offset); + } + } + + public static void WriteSMBString(byte[] buffer, int offset, bool isUnicode, string value) + { + if (isUnicode) + { + ByteWriter.WriteNullTerminatedUTF16String(buffer, offset, value); + } + else + { + ByteWriter.WriteNullTerminatedAnsiString(buffer, offset, value); + } + } + + public static void WriteSMBString(byte[] buffer, ref int offset, bool isUnicode, string value) + { + if (isUnicode) + { + ByteWriter.WriteNullTerminatedUTF16String(buffer, ref offset, value); + } + else + { + ByteWriter.WriteNullTerminatedAnsiString(buffer, ref offset, value); + } + } + + public static string ReadFixedLengthString(byte[] buffer, ref int offset, bool isUnicode, int byteCount) + { + if (isUnicode) + { + return ByteReader.ReadUTF16String(buffer, ref offset, byteCount / 2); + } + else + { + return ByteReader.ReadAnsiString(buffer, ref offset, byteCount); + } + } + + public static void WriteFixedLengthString(byte[] buffer, ref int offset, bool isUnicode, string value) + { + if (isUnicode) + { + ByteWriter.WriteUTF16String(buffer, ref offset, value); + } + else + { + ByteWriter.WriteAnsiString(buffer, ref offset, value); + } + } + } +} diff --git a/SMBLibrary/SMB1/SMBMessage.cs b/SMBLibrary/SMB1/SMBMessage.cs new file mode 100644 index 0000000..0e8d2bc --- /dev/null +++ b/SMBLibrary/SMB1/SMBMessage.cs @@ -0,0 +1,122 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// Each message has a single header and either a single command or multiple batched (AndX) commands. + /// Multiple command requests or responses can be sent in a single message. + /// + public class SMBMessage + { + public SMBHeader Header; + public List Commands = new List(); + + public SMBMessage() + { + Header = new SMBHeader(); + } + + public SMBMessage(byte[] buffer) + { + Header = new SMBHeader(buffer); + SMBCommand command = SMBCommand.ReadCommand(buffer, SMBHeader.Length, Header.Command, Header); + Commands.Add(command); + while(command is SMBAndXCommand) + { + SMBAndXCommand andXCommand = (SMBAndXCommand)command; + if (andXCommand.AndXCommand == CommandName.SMB_COM_NO_ANDX_COMMAND) + { + break; + } + command = SMBCommand.ReadCommand(buffer, andXCommand.AndXOffset, andXCommand.AndXCommand, Header); + Commands.Add(command); + } + } + + public byte[] GetBytes() + { + if (Commands.Count == 0) + { + throw new ArgumentException("Invalid command sequence"); + } + + for (int index = 0; index < Commands.Count - 1; index++) + { + if (!(Commands[index] is SMBAndXCommand)) + { + throw new ArgumentException("Invalid command sequence"); + } + } + + SMBCommand lastCommand = Commands[Commands.Count - 1]; + if (lastCommand is SMBAndXCommand) + { + ((SMBAndXCommand)lastCommand).AndXCommand = CommandName.SMB_COM_NO_ANDX_COMMAND; + } + + List sequence = new List(); + int length = SMBHeader.Length; + byte[] commandBytes; + for (int index = 0; index < Commands.Count - 1; index++) + { + SMBAndXCommand andXCommand = (SMBAndXCommand)Commands[index]; + andXCommand.AndXCommand = Commands[index + 1].CommandName; + commandBytes = Commands[index].GetBytes(Header.UnicodeFlag); + ushort nextOffset = (ushort)(length + commandBytes.Length); + SMBAndXCommand.WriteAndXOffset(commandBytes, 0, nextOffset); + sequence.Add(commandBytes); + length += commandBytes.Length; + } + + commandBytes = lastCommand.GetBytes(Header.UnicodeFlag); + sequence.Add(commandBytes); + length += commandBytes.Length; + + Header.Command = Commands[0].CommandName; + + byte[] buffer = new byte[length]; + Header.WriteBytes(buffer, 0); + int offset = SMBHeader.Length; + foreach (byte[] bytes in sequence) + { + ByteWriter.WriteBytes(buffer, ref offset, bytes); + } + + return buffer; + } + + public static bool IsValidSMBMessage(byte[] buffer) + { + if (buffer[0] == SMBHeader.ProtocolSignature[0] && + buffer[1] == SMBHeader.ProtocolSignature[1] && + buffer[2] == SMBHeader.ProtocolSignature[2] && + buffer[3] == SMBHeader.ProtocolSignature[3]) + { + return true; + } + else + { + return false; + } + } + + public static SMBMessage GetSMBMessage(byte[] buffer) + { + if (!IsValidSMBMessage(buffer)) + { + throw new InvalidRequestException("Invalid SMB message signature");; + } + return new SMBMessage(buffer); + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/DFSReferral/DfsReferralEntry.cs b/SMBLibrary/SMB1/Transaction2Subcommands/DFSReferral/DfsReferralEntry.cs new file mode 100644 index 0000000..0b3fad6 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/DFSReferral/DfsReferralEntry.cs @@ -0,0 +1,17 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public abstract class DfsReferralEntry + { + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/DFSReferral/RequestGetDfsReferral.cs b/SMBLibrary/SMB1/Transaction2Subcommands/DFSReferral/RequestGetDfsReferral.cs new file mode 100644 index 0000000..9fa793b --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/DFSReferral/RequestGetDfsReferral.cs @@ -0,0 +1,41 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// [MS-DFSC] REQ_GET_DFS_REFERRAL + /// + public class RequestGetDfsReferral + { + public ushort MaxReferralLevel; + public string RequestFileName; // Unicode + + public RequestGetDfsReferral() + { + } + + public RequestGetDfsReferral(byte[] buffer) + { + MaxReferralLevel = LittleEndianConverter.ToUInt16(buffer, 0); + RequestFileName = ByteReader.ReadNullTerminatedUTF16String(buffer, 2); + } + + public byte[] GetBytes() + { + int length = 2 + RequestFileName.Length * 2 + 2; + byte[] buffer = new byte[length]; + LittleEndianWriter.WriteUInt16(buffer, 0, MaxReferralLevel); + ByteWriter.WriteUTF16String(buffer, 2, RequestFileName); + return buffer; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/DFSReferral/ResponseGetDfsReferral.cs.cs b/SMBLibrary/SMB1/Transaction2Subcommands/DFSReferral/ResponseGetDfsReferral.cs.cs new file mode 100644 index 0000000..235682b --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/DFSReferral/ResponseGetDfsReferral.cs.cs @@ -0,0 +1,41 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// [MS-DFSC] RESP_GET_DFS_REFERRAL + /// + public class ResponseGetDfsReferral + { + public ushort PathConsumed; + public ushort NumberOfReferrals; + public uint ReferralHeaderFlags; + public List ReferralEntries; + public List StringBuffer; + // Padding + + public ResponseGetDfsReferral() + { + throw new NotImplementedException(); + } + + public ResponseGetDfsReferral(byte[] buffer) + { + throw new NotImplementedException(); + } + + public byte[] GetBytes() + { + throw new NotImplementedException(); + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/EnumStructures/AccessModeOptions.cs b/SMBLibrary/SMB1/Transaction2Subcommands/EnumStructures/AccessModeOptions.cs new file mode 100644 index 0000000..e4d3942 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/EnumStructures/AccessModeOptions.cs @@ -0,0 +1,89 @@ +/* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public enum AccessMode : byte + { + Read = 0x00, + Write = 0x01, + ReadWrite = 0x02, + Execute = 0x03, + } + + public enum SharingMode : byte + { + Compatibility = 0x00, + DenyReadWriteExecute = 0x01, // Exclusive use + DenyWrite = 0x02, + DenyReadExecute = 0x03, + DenyNothing = 0x04, + } + + public enum ReferenceLocality : byte + { + Unknown = 0x00, + Sequential = 0x01, + Random = 0x02, + RandomWithLocality = 0x03, + } + + public enum CachedMode : byte + { + CachingAllowed = 0x00, + DoNotCacheFile = 0x01, + } + + public enum WriteThroughMode : byte + { + Disabled = 0x00, + + /// + /// Write-through mode. + /// If this flag is set, then no read ahead or write behind is allowed on this file or device. + /// When the response is returned, data is expected to be on the disk or device. + /// + WriteThrough = 0x01, + } + + public struct AccessModeOptions // 2 bytes + { + public AccessMode AccessMode; + public SharingMode SharingMode; + public ReferenceLocality ReferenceLocality; + public CachedMode CachedMode; + public WriteThroughMode WriteThroughMode; + + public AccessModeOptions(byte[] buffer, int offset) + { + AccessMode = (AccessMode)(buffer[offset] & 0x07); + SharingMode = (SharingMode)((buffer[offset] & 0x70) >> 4); + ReferenceLocality = (ReferenceLocality)(buffer[offset + 1] & 0x07); + CachedMode = (CachedMode)((buffer[offset + 1] & 0x10) >> 4); + WriteThroughMode = (WriteThroughMode)((buffer[offset + 1] & 0x40) >> 6); + } + + public void WriteBytes(byte[] buffer, int offset) + { + buffer[offset + 0] = (byte)((byte)AccessMode & 0x07); + buffer[offset + 0] |= (byte)(((byte)SharingMode << 4) & 0x70); + buffer[offset + 1] = (byte)((byte)ReferenceLocality & 0x07); + buffer[offset + 1] |= (byte)(((byte)CachedMode << 4) & 0x10); + buffer[offset + 1] |= (byte)(((byte)WriteThroughMode << 6) & 0x40); + } + + public static AccessModeOptions Read(byte[] buffer, ref int offset) + { + offset += 2; + return new AccessModeOptions(buffer, offset - 2); + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/EnumStructures/ActionTaken.cs b/SMBLibrary/SMB1/Transaction2Subcommands/EnumStructures/ActionTaken.cs new file mode 100644 index 0000000..c8e0cf2 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/EnumStructures/ActionTaken.cs @@ -0,0 +1,34 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; + +namespace SMBLibrary.SMB1 +{ + public enum LockStatus : byte + { + NoOpLockWasRequestedOrGranted = 0x00, + OpLockWasRequestedAndGranted = 0x01, + } + + public struct ActionTaken + { + public OpenResult OpenResult; + public LockStatus LockStatus; + + public ActionTaken(byte[] buffer, int offset) + { + OpenResult = (OpenResult)(buffer[offset + 0] & 0x03); + LockStatus = (LockStatus)(buffer[offset + 1] >> 7); + } + + public void WriteBytes(byte[] buffer, int offset) + { + buffer[offset + 0] = (byte)((byte)OpenResult & 0x03); + buffer[offset + 1] = (byte)((byte)LockStatus << 7); + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/EnumStructures/OpenMode.cs b/SMBLibrary/SMB1/Transaction2Subcommands/EnumStructures/OpenMode.cs new file mode 100644 index 0000000..fb7330b --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/EnumStructures/OpenMode.cs @@ -0,0 +1,49 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.SMB1 +{ + public enum CreateFile : byte + { + ReturnErrorIfNotExist = 0x00, + CreateIfNotExist = 0x01, + } + + public enum FileExistsOpts : byte + { + ReturnError = 0x00, + Append = 0x01, + TruncateToZero = 0x02, + } + + public struct OpenMode // 2 bytes + { + public FileExistsOpts FileExistsOpts; + public CreateFile CreateFile; + + public OpenMode(byte[] buffer, int offset) + { + FileExistsOpts = (FileExistsOpts)(buffer[offset] & 0x3); + CreateFile = (CreateFile)((buffer[offset] & 0x10) >> 4); + } + + public void WriteBytes(byte[] buffer, int offset) + { + buffer[0] = (byte)FileExistsOpts; + buffer[0] |= (byte)((byte)CreateFile << 4); + } + + public static OpenMode Read(byte[] buffer, ref int offset) + { + offset += 2; + return new OpenMode(buffer, offset - 2); + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Enums/ExtendedAttributeFlag.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/ExtendedAttributeFlag.cs new file mode 100644 index 0000000..55010cd --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/ExtendedAttributeFlag.cs @@ -0,0 +1,8 @@ + +namespace SMBLibrary.SMB1 +{ + public enum ExtendedAttributeFlag : byte + { + FILE_NEED_EA = 0x80, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Enums/FindFlags.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/FindFlags.cs new file mode 100644 index 0000000..888aa16 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/FindFlags.cs @@ -0,0 +1,14 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum FindFlags : ushort + { + SMB_FIND_CLOSE_AFTER_REQUEST = 0x0001, + SMB_FIND_CLOSE_AT_EOS = 0x0002, + SMB_FIND_RETURN_RESUME_KEYS = 0x0004, + SMB_FIND_CONTINUE_FROM_LAST = 0x0008, + SMB_FIND_WITH_BACKUP_INTENT = 0x0010, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Enums/FindInformationLevel.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/FindInformationLevel.cs new file mode 100644 index 0000000..756e424 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/FindInformationLevel.cs @@ -0,0 +1,14 @@ + +namespace SMBLibrary.SMB1 +{ + public enum FindInformationLevel : ushort + { + SMB_INFO_STANDARD = 0x0001, + SMB_INFO_QUERY_EA_SIZE = 0x0002, + SMB_INFO_QUERY_EAS_FROM_LIST = 0x0003, + SMB_FIND_FILE_DIRECTORY_INFO = 0x0101, + SMB_FIND_FILE_FULL_DIRECTORY_INFO = 0x0102, + SMB_FIND_FILE_NAMES_INFO = 0x0103, + SMB_FIND_FILE_BOTH_DIRECTORY_INFO = 0x0104, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Enums/Open2Flags.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/Open2Flags.cs new file mode 100644 index 0000000..f76eefc --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/Open2Flags.cs @@ -0,0 +1,30 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum Open2Flags : ushort + { + /// + /// Return additional information in the response; + /// populate the CreationTime, FileDataSize, AccessMode, ResourceType, and NMPipeStatus fields in the response. + /// + REQ_ATTRIB = 0x0001, + + /// + /// Exclusive OpLock requested. + /// + REQ_OPLOCK = 0x0002, + + /// + /// Batch OpLock requested. + /// + REQ_OPBATCH = 0x0004, + + /// + /// Return total length of Extended Attributes (EAs); + /// populate the ExtendedAttributeLength field in the response. + /// + REQ_EASIZE = 0x0008, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Enums/QueryFSInformationLevel.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/QueryFSInformationLevel.cs new file mode 100644 index 0000000..92b2fd9 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/QueryFSInformationLevel.cs @@ -0,0 +1,13 @@ + +namespace SMBLibrary.SMB1 +{ + public enum QueryFSInformationLevel : ushort + { + SMB_INFO_ALLOCATION = 0x0001, + SMB_INFO_VOLUME = 0x0002, + SMB_QUERY_FS_VOLUME_INFO = 0x0102, + SMB_QUERY_FS_SIZE_INFO = 0x0103, + SMB_QUERY_FS_DEVICE_INFO = 0x0104, + SMB_QUERY_FS_ATTRIBUTE_INFO = 0x0105, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Enums/QueryInformationLevel.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/QueryInformationLevel.cs new file mode 100644 index 0000000..c11380c --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/QueryInformationLevel.cs @@ -0,0 +1,20 @@ + +namespace SMBLibrary.SMB1 +{ + public enum QueryInformationLevel : ushort + { + SMB_INFO_STANDARD = 0x0001, + SMB_INFO_QUERY_EA_SIZE = 0x0002, + SMB_INFO_QUERY_EAS_FROM_LIST = 0x0003, + SMB_INFO_QUERY_ALL_EAS = 0x0004, + SMB_INFO_IS_NAME_VALID = 0x0006, + SMB_QUERY_FILE_BASIC_INFO = 0x0101, + SMB_QUERY_FILE_STANDARD_INFO = 0x0102, + SMB_QUERY_FILE_EA_INFO = 0x0103, + SMB_QUERY_FILE_NAME_INFO = 0x0104, + SMB_QUERY_FILE_ALL_INFO = 0x0107, + SMB_QUERY_FILE_ALT_NAME_INFO = 0x0108, + SMB_QUERY_FILE_STREAM_INFO = 0x0109, + SMB_QUERY_FILE_COMPRESSION_INFO = 0x010B, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Enums/SearchStorageType.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/SearchStorageType.cs new file mode 100644 index 0000000..68483c7 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/SearchStorageType.cs @@ -0,0 +1,9 @@ + +namespace SMBLibrary.SMB1 +{ + public enum SearchStorageType : uint + { + FILE_DIRECTORY_FILE = 0x01, + FILE_NON_DIRECTORY_FILE = 0x40, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Enums/SetInformationLevel.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/SetInformationLevel.cs new file mode 100644 index 0000000..a17a91b --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/SetInformationLevel.cs @@ -0,0 +1,13 @@ + +namespace SMBLibrary.SMB1 +{ + public enum SetInformationLevel : ushort + { + SMB_INFO_STANDARD = 0x0001, + SMB_INFO_SET_EAS = 0x0002, + SMB_SET_FILE_BASIC_INFO = 0x0101, + SMB_SET_FILE_DISPOSITION_INFO = 0x0102, + SMB_SET_FILE_ALLOCATION_INFO = 0x0103, + SMB_SET_FILE_END_OF_FILE_INFO = 0x0104, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Enums/Transaction2SubcommandName.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/Transaction2SubcommandName.cs new file mode 100644 index 0000000..2ef40c8 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Enums/Transaction2SubcommandName.cs @@ -0,0 +1,17 @@ + +namespace SMBLibrary.SMB1 +{ + public enum Transaction2SubcommandName : ushort + { + TRANS2_OPEN2 = 0x0000, + TRANS2_FIND_FIRST2 = 0x0001, + TRANS2_FIND_NEXT2 = 0x0002, + TRANS2_QUERY_FS_INFORMATION = 0x0003, + TRANS2_QUERY_PATH_INFORMATION = 0x0005, + TRANS2_SET_PATH_INFORMATION = 0x006, + TRANS2_QUERY_FILE_INFORMATION = 0x0007, + TRANS2_SET_FILE_INFORMATION = 0x0008, + TRANS2_CREATE_DIRECTORY = 0x000D, + TRANS2_GET_DFS_REFERRAL = 0x0010, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileBothDirectoryInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileBothDirectoryInfo.cs new file mode 100644 index 0000000..b5cd67b --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileBothDirectoryInfo.cs @@ -0,0 +1,102 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_FIND_FILE_BOTH_DIRECTORY_INFO + /// + public class FindFileBothDirectoryInfo : FindInformationEntry + { + public const int FixedLength = 94; + + public uint NextEntryOffset; + public uint FileIndex; // SHOULD be set to zero when sent in a response and SHOULD be ignored when received by the client + public DateTime CreationTime; + public DateTime LastAccessTime; + public DateTime LastWriteTime; + public DateTime LastChangeTime; + public ulong EndOfFile; + public ulong AllocationSize; + public ExtendedFileAttributes ExtFileAttributes; + //uint FileNameLength; // In bytes, MUST exclude the null termination. + public uint EASize; + //byte ShortNameLength; // In bytes + public byte Reserved; + public string ShortName; // 24 bytes, 8.3 name of the file in Unicode format + public string FileName; // OEM / Unicode character array. MUST be written as SMB_STRING, and read as fixed length string. + // Omitting the NULL termination from the FileName field in a single SMB_FIND_FILE_BOTH_DIRECTORY_INFO structure + // (as a response to TRANS2_QUERY_PATH_INFORMATION on a single directory) + // Will, in some rare but repeatable cases, cause issues with Windows XP SP3 as a client + // (the client will display an error message that the folder "refers to a location that is unavailable"...) + + public FindFileBothDirectoryInfo() : base(false) + { + } + + public FindFileBothDirectoryInfo(byte[] buffer, ref int offset, bool isUnicode) : base(false) + { + NextEntryOffset = LittleEndianReader.ReadUInt32(buffer, ref offset); + FileIndex = LittleEndianReader.ReadUInt32(buffer, ref offset); + CreationTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastAccessTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastWriteTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastChangeTime = SMBHelper.ReadFileTime(buffer, ref offset); + EndOfFile = LittleEndianReader.ReadUInt64(buffer, ref offset); + AllocationSize = LittleEndianReader.ReadUInt64(buffer, ref offset); + ExtFileAttributes = (ExtendedFileAttributes)LittleEndianReader.ReadUInt32(buffer, ref offset); + uint fileNameLength = LittleEndianReader.ReadUInt32(buffer, ref offset); + EASize = LittleEndianReader.ReadUInt32(buffer, ref offset); + byte shortNameLength = ByteReader.ReadByte(buffer, ref offset); + Reserved = ByteReader.ReadByte(buffer, ref offset); + ShortName = ByteReader.ReadUTF16String(buffer, ref offset, 12); + ShortName = ShortName.Substring(0, shortNameLength); + FileName = SMBHelper.ReadFixedLengthString(buffer, ref offset, isUnicode, (int)fileNameLength); + } + + public override void WriteBytes(byte[] buffer, ref int offset, bool isUnicode) + { + uint fileNameLength = (uint)(isUnicode ? FileName.Length * 2 : FileName.Length); + byte shortNameLength = (byte)(ShortName.Length * 2); + + LittleEndianWriter.WriteUInt32(buffer, ref offset, NextEntryOffset); + LittleEndianWriter.WriteUInt32(buffer, ref offset, FileIndex); + SMBHelper.WriteFileTime(buffer, ref offset, CreationTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastAccessTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastWriteTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastChangeTime); + LittleEndianWriter.WriteUInt64(buffer, ref offset, EndOfFile); + LittleEndianWriter.WriteUInt64(buffer, ref offset, AllocationSize); + LittleEndianWriter.WriteUInt32(buffer, ref offset, (uint)ExtFileAttributes); + LittleEndianWriter.WriteUInt32(buffer, ref offset, fileNameLength); + LittleEndianWriter.WriteUInt32(buffer, ref offset, EASize); + ByteWriter.WriteByte(buffer, ref offset, shortNameLength); + ByteWriter.WriteByte(buffer, ref offset, Reserved); + ByteWriter.WriteUTF16String(buffer, ref offset, ShortName, 12); + SMBHelper.WriteSMBString(buffer, ref offset, isUnicode, FileName); + } + + public override int GetLength(bool isUnicode) + { + int length = FixedLength; + + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + return length; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileDirectoryInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileDirectoryInfo.cs new file mode 100644 index 0000000..98fde65 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileDirectoryInfo.cs @@ -0,0 +1,84 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_FIND_FILE_DIRECTORY_INFO + /// + public class FindFileDirectoryInfo : FindInformationEntry + { + public const int FixedLength = 64; + + public uint NextEntryOffset; + public uint FileIndex; // SHOULD be set to zero when sent in a response and SHOULD be ignored when received by the client + public DateTime CreationTime; + public DateTime LastAccessTime; + public DateTime LastWriteTime; + public DateTime LastAttrChangeTime; + public ulong EndOfFile; + public ulong AllocationSize; + public ExtendedFileAttributes ExtFileAttributes; + //uint FileNameLength; // In bytes, MUST exclude the null termination. + public string FileName; // OEM / Unicode character array. MUST be written as SMB_STRING, and read as fixed length string. + + public FindFileDirectoryInfo() : base(false) + { + } + + public FindFileDirectoryInfo(byte[] buffer, ref int offset, bool isUnicode) : base(false) + { + NextEntryOffset = LittleEndianReader.ReadUInt32(buffer, ref offset); + FileIndex = LittleEndianReader.ReadUInt32(buffer, ref offset); + CreationTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastAccessTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastWriteTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastAttrChangeTime = SMBHelper.ReadFileTime(buffer, ref offset); + EndOfFile = LittleEndianReader.ReadUInt64(buffer, ref offset); + AllocationSize = LittleEndianReader.ReadUInt64(buffer, ref offset); + ExtFileAttributes = (ExtendedFileAttributes)LittleEndianReader.ReadUInt32(buffer, ref offset); + uint fileNameLength = LittleEndianReader.ReadUInt32(buffer, ref offset); + FileName = SMBHelper.ReadFixedLengthString(buffer, ref offset, isUnicode, (int)fileNameLength); + } + + public override void WriteBytes(byte[] buffer, ref int offset, bool isUnicode) + { + uint fileNameLength = (byte)(isUnicode ? FileName.Length * 2 : FileName.Length); + + LittleEndianWriter.WriteUInt32(buffer, ref offset, NextEntryOffset); + LittleEndianWriter.WriteUInt32(buffer, ref offset, FileIndex); + SMBHelper.WriteFileTime(buffer, ref offset, CreationTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastAccessTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastWriteTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastAttrChangeTime); + LittleEndianWriter.WriteUInt64(buffer, ref offset, EndOfFile); + LittleEndianWriter.WriteUInt64(buffer, ref offset, AllocationSize); + LittleEndianWriter.WriteUInt32(buffer, ref offset, (uint)ExtFileAttributes); + LittleEndianWriter.WriteUInt32(buffer, ref offset, fileNameLength); + SMBHelper.WriteSMBString(buffer, ref offset, isUnicode, FileName); + } + + public override int GetLength(bool isUnicode) + { + int length = FixedLength; + + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + return length; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileFullDirectoryInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileFullDirectoryInfo.cs new file mode 100644 index 0000000..ca8d1ca --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileFullDirectoryInfo.cs @@ -0,0 +1,87 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_FIND_FILE_FULL_DIRECTORY_INFO + /// + public class FindFileFullDirectoryInfo : FindInformationEntry + { + public const int FixedLength = 68; + + public uint NextEntryOffset; + public uint FileIndex; // SHOULD be set to zero when sent in a response and SHOULD be ignored when received by the client + public DateTime CreationTime; + public DateTime LastAccessTime; + public DateTime LastWriteTime; + public DateTime LastAttrChangeTime; + public ulong EndOfFile; + public ulong AllocationSize; + public ExtendedFileAttributes ExtFileAttributes; + //uint FileNameLength; // In bytes, MUST exclude the null termination. + public uint EASize; + public string FileName; // OEM / Unicode character array. MUST be written as SMB_STRING, and read as fixed length string. + + public FindFileFullDirectoryInfo() : base(false) + { + } + + public FindFileFullDirectoryInfo(byte[] buffer, ref int offset, bool isUnicode) : base(false) + { + NextEntryOffset = LittleEndianReader.ReadUInt32(buffer, ref offset); + FileIndex = LittleEndianReader.ReadUInt32(buffer, ref offset); + CreationTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastAccessTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastWriteTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastAttrChangeTime = SMBHelper.ReadFileTime(buffer, ref offset); + EndOfFile = LittleEndianReader.ReadUInt64(buffer, ref offset); + AllocationSize = LittleEndianReader.ReadUInt64(buffer, ref offset); + ExtFileAttributes = (ExtendedFileAttributes)LittleEndianReader.ReadUInt32(buffer, ref offset); + uint fileNameLength = LittleEndianReader.ReadUInt32(buffer, ref offset); + EASize = LittleEndianReader.ReadUInt32(buffer, ref offset); + FileName = SMBHelper.ReadFixedLengthString(buffer, ref offset, isUnicode, (int)fileNameLength); + } + + public override void WriteBytes(byte[] buffer, ref int offset, bool isUnicode) + { + uint fileNameLength = (byte)(isUnicode ? FileName.Length * 2 : FileName.Length); + + LittleEndianWriter.WriteUInt32(buffer, ref offset, NextEntryOffset); + LittleEndianWriter.WriteUInt32(buffer, ref offset, FileIndex); + SMBHelper.WriteFileTime(buffer, ref offset, CreationTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastAccessTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastWriteTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastAttrChangeTime); + LittleEndianWriter.WriteUInt64(buffer, ref offset, EndOfFile); + LittleEndianWriter.WriteUInt64(buffer, ref offset, AllocationSize); + LittleEndianWriter.WriteUInt32(buffer, ref offset, (uint)ExtFileAttributes); + LittleEndianWriter.WriteUInt32(buffer, ref offset, fileNameLength); + LittleEndianWriter.WriteUInt32(buffer, ref offset, EASize); + SMBHelper.WriteSMBString(buffer, ref offset, isUnicode, FileName); + } + + public override int GetLength(bool isUnicode) + { + int length = FixedLength; + + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + return length; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileNamesInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileNamesInfo.cs new file mode 100644 index 0000000..1b2daa2 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindFileNamesInfo.cs @@ -0,0 +1,63 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_FIND_FILE_NAMES_INFO + /// + public class FindFileNamesInfo : FindInformationEntry + { + public const int FixedLength = 12; + + public uint NextEntryOffset; + public uint FileIndex; // SHOULD be set to zero when sent in a response and SHOULD be ignored when received by the client + //uint FileNameLength; // In bytes, MUST exclude the null termination. + public string FileName; // OEM / Unicode character array. MUST be written as SMB_STRING, and read as fixed length string. + + public FindFileNamesInfo() : base(false) + { + } + + public FindFileNamesInfo(byte[] buffer, ref int offset, bool isUnicode) : base(false) + { + NextEntryOffset = LittleEndianReader.ReadUInt32(buffer, ref offset); + FileIndex = LittleEndianReader.ReadUInt32(buffer, ref offset); + uint fileNameLength = LittleEndianReader.ReadUInt32(buffer, ref offset); + FileName = SMBHelper.ReadFixedLengthString(buffer, ref offset, isUnicode, (int)fileNameLength); + } + + public override void WriteBytes(byte[] buffer, ref int offset, bool isUnicode) + { + uint fileNameLength = (uint)(isUnicode ? FileName.Length * 2 : FileName.Length); + + LittleEndianWriter.WriteUInt32(buffer, ref offset, NextEntryOffset); + LittleEndianWriter.WriteUInt32(buffer, ref offset, FileIndex); + LittleEndianWriter.WriteUInt32(buffer, ref offset, fileNameLength); + SMBHelper.WriteSMBString(buffer, ref offset, isUnicode, FileName); + } + + public override int GetLength(bool isUnicode) + { + int length = FixedLength; + + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + return length; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInfoQueryEASize.cs b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInfoQueryEASize.cs new file mode 100644 index 0000000..b576932 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInfoQueryEASize.cs @@ -0,0 +1,89 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_INFO_QUERY_EA_SIZE + /// + public class FindInfoQueryEASize : FindInformationEntry + { + public uint ResumeKey; // Optional + public DateTime CreationDateTime; + public DateTime LastAccessDateTime; + public DateTime LastWriteDateTime; + public uint FileDataSize; + public uint AllocationSize; + public FileAttributes Attributes; + public uint EASize; + //byte FileNameLength; // In bytes, MUST exclude the null termination. + public string FileName; // OEM / Unicode character array. MUST be written as SMB_STRING, and read as fixed length string. + + public FindInfoQueryEASize(bool returnResumeKeys) : base(returnResumeKeys) + { + } + + public FindInfoQueryEASize(byte[] buffer, ref int offset, bool isUnicode, bool returnResumeKeys) : base(returnResumeKeys) + { + if (returnResumeKeys) + { + ResumeKey = LittleEndianReader.ReadUInt32(buffer, ref offset); + } + CreationDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + LastAccessDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + LastWriteDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + FileDataSize = LittleEndianReader.ReadUInt32(buffer, ref offset); + AllocationSize = LittleEndianReader.ReadUInt32(buffer, ref offset); + Attributes = (FileAttributes)LittleEndianReader.ReadUInt16(buffer, ref offset); + EASize = LittleEndianReader.ReadUInt32(buffer, ref offset); + byte fileNameLength = ByteReader.ReadByte(buffer, ref offset); + FileName = SMBHelper.ReadFixedLengthString(buffer, ref offset, isUnicode, fileNameLength); + } + + public override void WriteBytes(byte[] buffer, ref int offset, bool isUnicode) + { + byte fileNameLength = (byte)(isUnicode ? FileName.Length * 2 : FileName.Length); + + if (ReturnResumeKeys) + { + LittleEndianWriter.WriteUInt32(buffer, ref offset, ResumeKey); + } + SMBHelper.WriteSMBDateTime(buffer, ref offset, CreationDateTime); + SMBHelper.WriteSMBDateTime(buffer, ref offset, LastAccessDateTime); + SMBHelper.WriteSMBDateTime(buffer, ref offset, LastWriteDateTime); + LittleEndianWriter.WriteUInt32(buffer, ref offset, FileDataSize); + LittleEndianWriter.WriteUInt32(buffer, ref offset, AllocationSize); + LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)Attributes); + LittleEndianWriter.WriteUInt32(buffer, ref offset, EASize); + ByteWriter.WriteByte(buffer, ref offset, fileNameLength); + SMBHelper.WriteSMBString(buffer, ref offset, isUnicode, FileName); + } + + public override int GetLength(bool isUnicode) + { + int length = 31; + if (ReturnResumeKeys) + { + length += 4; + } + + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + return length; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInfoQueryExtendedAttributesFromList.cs b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInfoQueryExtendedAttributesFromList.cs new file mode 100644 index 0000000..54d027b --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInfoQueryExtendedAttributesFromList.cs @@ -0,0 +1,89 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_INFO_QUERY_EAS_FROM_LIST + /// + public class FindInfoQueryExtendedAttributesFromList : FindInformationEntry + { + public uint ResumeKey; // Optional + public DateTime CreationDateTime; + public DateTime LastAccessDateTime; + public DateTime LastWriteDateTime; + public uint FileDataSize; + public uint AllocationSize; + public FileAttributes Attributes; + public FullExtendedAttributeList ExtendedAttributeList; + //byte FileNameLength; // In bytes, MUST exclude the null termination. + public string FileName; // OEM / Unicode character array. MUST be written as SMB_STRING, and read as fixed length string. + + public FindInfoQueryExtendedAttributesFromList(bool returnResumeKeys) : base(returnResumeKeys) + { + } + + public FindInfoQueryExtendedAttributesFromList(byte[] buffer, ref int offset, bool isUnicode, bool returnResumeKeys) : base(returnResumeKeys) + { + if (returnResumeKeys) + { + ResumeKey = LittleEndianReader.ReadUInt32(buffer, ref offset); + } + CreationDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + LastAccessDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + LastWriteDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + FileDataSize = LittleEndianReader.ReadUInt32(buffer, ref offset); + AllocationSize = LittleEndianReader.ReadUInt32(buffer, ref offset); + Attributes = (FileAttributes)LittleEndianReader.ReadUInt16(buffer, ref offset); + ExtendedAttributeList = new FullExtendedAttributeList(buffer, offset); + byte fileNameLength = ByteReader.ReadByte(buffer, ref offset); + FileName = SMBHelper.ReadFixedLengthString(buffer, ref offset, isUnicode, fileNameLength); + } + + public override void WriteBytes(byte[] buffer, ref int offset, bool isUnicode) + { + byte fileNameLength = (byte)(isUnicode ? FileName.Length * 2 : FileName.Length); + + if (ReturnResumeKeys) + { + LittleEndianWriter.WriteUInt32(buffer, ref offset, ResumeKey); + } + SMBHelper.WriteSMBDateTime(buffer, ref offset, CreationDateTime); + SMBHelper.WriteSMBDateTime(buffer, ref offset, LastAccessDateTime); + SMBHelper.WriteSMBDateTime(buffer, ref offset, LastWriteDateTime); + LittleEndianWriter.WriteUInt32(buffer, ref offset, FileDataSize); + LittleEndianWriter.WriteUInt32(buffer, ref offset, AllocationSize); + LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)Attributes); + ExtendedAttributeList.WriteBytes(buffer, ref offset); + ByteWriter.WriteByte(buffer, ref offset, fileNameLength); + SMBHelper.WriteSMBString(buffer, ref offset, isUnicode, FileName); + } + + public override int GetLength(bool isUnicode) + { + int length = 27 + ExtendedAttributeList.Length; + if (ReturnResumeKeys) + { + length += 4; + } + + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + return length; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInfoStandard.cs b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInfoStandard.cs new file mode 100644 index 0000000..bafb915 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInfoStandard.cs @@ -0,0 +1,88 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_INFO_STANDARD + /// + public class FindInfoStandard : FindInformationEntry + { + public const int FixedLength = 23; + + public uint ResumeKey; // Optional + public DateTime CreationDateTime; + public DateTime LastAccessDateTime; + public DateTime LastWriteDateTime; + public uint FileDataSize; + public uint AllocationSize; + public FileAttributes Attributes; + //byte FileNameLength; + public string FileName; // SMB_STRING + + public FindInfoStandard(bool returnResumeKeys) : base(returnResumeKeys) + { + } + + public FindInfoStandard(byte[] buffer, ref int offset, bool isUnicode, bool returnResumeKeys) : base(returnResumeKeys) + { + if (returnResumeKeys) + { + ResumeKey = LittleEndianReader.ReadUInt32(buffer, ref offset); + } + CreationDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + LastAccessDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + LastWriteDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + FileDataSize = LittleEndianReader.ReadUInt32(buffer, ref offset); + AllocationSize = LittleEndianReader.ReadUInt32(buffer, ref offset); + Attributes = (FileAttributes)LittleEndianReader.ReadUInt16(buffer, ref offset); + byte fileNameLength = ByteReader.ReadByte(buffer, ref offset); + FileName = SMBHelper.ReadSMBString(buffer, ref offset, isUnicode); + } + + public override void WriteBytes(byte[] buffer, ref int offset, bool isUnicode) + { + byte fileNameLength = (byte)(isUnicode ? FileName.Length * 2 : FileName.Length); + + if (ReturnResumeKeys) + { + LittleEndianWriter.WriteUInt32(buffer, ref offset, ResumeKey); + } + SMBHelper.WriteSMBDateTime(buffer, ref offset, CreationDateTime); + SMBHelper.WriteSMBDateTime(buffer, ref offset, LastAccessDateTime); + SMBHelper.WriteSMBDateTime(buffer, ref offset, LastWriteDateTime); + LittleEndianWriter.WriteUInt32(buffer, ref offset, FileDataSize); + LittleEndianWriter.WriteUInt32(buffer, ref offset, AllocationSize); + LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)Attributes); + ByteWriter.WriteByte(buffer, ref offset, fileNameLength); + SMBHelper.WriteSMBString(buffer, ref offset, isUnicode, FileName); + } + + public override int GetLength(bool isUnicode) + { + int length = FixedLength; + if (ReturnResumeKeys) + { + length += 4; + } + + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + return length; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInformation.cs b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInformation.cs new file mode 100644 index 0000000..81a4826 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInformation.cs @@ -0,0 +1,78 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public class FindInformation : List + { + public FindInformation() + { + } + + public FindInformation(byte[] buffer, FindInformationLevel informationLevel, bool isUnicode, bool returnResumeKeys) + { + int offset = 0; + while (offset < buffer.Length) + { + FindInformationEntry entry = FindInformationEntry.ReadEntry(buffer, ref offset, informationLevel, isUnicode, returnResumeKeys); + this.Add(entry); + } + } + + public byte[] GetBytes(bool isUnicode) + { + for(int index = 0; index < this.Count; index++) + { + if (index < this.Count - 1) + { + FindInformationEntry entry = this[index]; + int entryLength = entry.GetLength(isUnicode); + if (entry is FindFileBothDirectoryInfo) + { + ((FindFileBothDirectoryInfo)entry).NextEntryOffset = (uint)entryLength; + } + else if (entry is FindFileDirectoryInfo) + { + ((FindFileDirectoryInfo)entry).NextEntryOffset = (uint)entryLength; + } + else if (entry is FindFileFullDirectoryInfo) + { + ((FindFileFullDirectoryInfo)entry).NextEntryOffset = (uint)entryLength; + } + else if (entry is FindFileNamesInfo) + { + ((FindFileNamesInfo)entry).NextEntryOffset = (uint)entryLength; + } + } + } + int length = GetLength(isUnicode); + byte[] buffer = new byte[length]; + int offset = 0; + foreach (FindInformationEntry entry in this) + { + entry.WriteBytes(buffer, ref offset, isUnicode); + } + return buffer; + } + + public int GetLength(bool isUnicode) + { + int length = 0; + for (int index = 0; index < this.Count; index++) + { + FindInformationEntry entry = this[index]; + int entryLength = entry.GetLength(isUnicode); + length += entryLength; + } + return length; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInformationEntry.cs b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInformationEntry.cs new file mode 100644 index 0000000..5c0bcae --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/FindInformation/FindInformationEntry.cs @@ -0,0 +1,57 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.SMB1 +{ + public abstract class FindInformationEntry + { + private bool m_returnResumeKeys; + + public FindInformationEntry(bool returnResumeKeys) + { + m_returnResumeKeys = returnResumeKeys; + } + + public abstract void WriteBytes(byte[] buffer, ref int offset, bool isUnicode); + + public abstract int GetLength(bool isUnicode); + + public bool ReturnResumeKeys + { + get + { + return m_returnResumeKeys; + } + } + + public static FindInformationEntry ReadEntry(byte[] buffer, ref int offset, FindInformationLevel informationLevel, bool isUnicode, bool returnResumeKeys) + { + switch (informationLevel) + { + case FindInformationLevel.SMB_INFO_STANDARD: + return new FindInfoStandard(buffer, ref offset, isUnicode, returnResumeKeys); + case FindInformationLevel.SMB_INFO_QUERY_EA_SIZE: + return new FindInfoQueryEASize(buffer, ref offset, isUnicode, returnResumeKeys); + case FindInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST: + return new FindInfoQueryExtendedAttributesFromList(buffer, ref offset, isUnicode, returnResumeKeys); + case FindInformationLevel.SMB_FIND_FILE_DIRECTORY_INFO: + return new FindFileDirectoryInfo(buffer, ref offset, isUnicode); + case FindInformationLevel.SMB_FIND_FILE_FULL_DIRECTORY_INFO: + return new FindFileFullDirectoryInfo(buffer, ref offset, isUnicode); + case FindInformationLevel.SMB_FIND_FILE_NAMES_INFO: + return new FindFileNamesInfo(buffer, ref offset, isUnicode); + case FindInformationLevel.SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + return new FindFileBothDirectoryInfo(buffer, ref offset, isUnicode); + default: + throw new InvalidRequestException();; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/FullExtendedAttribute.cs b/SMBLibrary/SMB1/Transaction2Subcommands/FullExtendedAttribute.cs new file mode 100644 index 0000000..4da1c8e --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/FullExtendedAttribute.cs @@ -0,0 +1,53 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_FEA + /// + public class FullExtendedAttribute + { + public ExtendedAttributeFlag ExtendedAttributeFlag; + //byte AttributeNameLengthInBytes; + //ushort AttributeValueLengthInBytes; + public string AttributeName; // ANSI, AttributeNameLengthInBytes + 1 byte null termination + public string AttributeValue; // ANSI + + public FullExtendedAttribute(byte[] buffer, int offset) + { + ExtendedAttributeFlag = (ExtendedAttributeFlag)ByteReader.ReadByte(buffer, offset); + byte attributeNameLengthInBytes = ByteReader.ReadByte(buffer, offset + 1); + ushort attributeValueLengthInBytes = LittleEndianConverter.ToUInt16(buffer, offset + 2); + AttributeName = ByteReader.ReadAnsiString(buffer, offset + 4, attributeNameLengthInBytes); + AttributeValue = ByteReader.ReadAnsiString(buffer, offset + 4 + attributeNameLengthInBytes + 1, attributeValueLengthInBytes); + } + + public void WriteBytes(byte[] buffer, int offset) + { + byte attributeNameLengthInBytes = (byte)AttributeName.Length; + ushort attributeValueLengthInBytes = (ushort)AttributeValue.Length; + ByteWriter.WriteByte(buffer, offset, (byte)ExtendedAttributeFlag); + ByteWriter.WriteByte(buffer, offset + 1, attributeNameLengthInBytes); + LittleEndianWriter.WriteUInt16(buffer, offset + 2, attributeValueLengthInBytes); + ByteWriter.WriteAnsiString(buffer, offset + 4, AttributeName, AttributeValue.Length); + ByteWriter.WriteAnsiString(buffer, offset + 4 + attributeNameLengthInBytes + 1, AttributeValue, AttributeValue.Length); + } + + public int Length + { + get + { + return 4 + AttributeName.Length + 1 + AttributeValue.Length; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/FullExtendedAttributeList.cs b/SMBLibrary/SMB1/Transaction2Subcommands/FullExtendedAttributeList.cs new file mode 100644 index 0000000..ab8f1eb --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/FullExtendedAttributeList.cs @@ -0,0 +1,83 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_FEA_LIST + /// + public class FullExtendedAttributeList : List + { + public FullExtendedAttributeList() + { + } + + public FullExtendedAttributeList(byte[] buffer) : this(buffer, 0) + { + } + + public FullExtendedAttributeList(byte[] buffer, ref int offset) : this(buffer, offset) + { + // length MUST contain the total size of the FEAList field, plus the size of the SizeOfListInBytes field + int length = (int)LittleEndianConverter.ToUInt32(buffer, offset); + offset += length; + } + + public FullExtendedAttributeList(byte[] buffer, int offset) + { + // length MUST contain the total size of the FEAList field, plus the size of the SizeOfListInBytes field + int length = (int)LittleEndianConverter.ToUInt32(buffer, offset); + int position = offset + 4; + int eof = offset + length; + while (position < eof) + { + FullExtendedAttribute attribute = new FullExtendedAttribute(buffer, position); + this.Add(attribute); + position += attribute.Length; + } + } + + public byte[] GetBytes() + { + byte[] buffer = new byte[this.Length]; + WriteBytes(buffer, 0); + return buffer; + } + + public void WriteBytes(byte[] buffer, ref int offset) + { + WriteBytes(buffer, offset); + offset += this.Length; + } + + public void WriteBytes(byte[] buffer, int offset) + { + foreach (FullExtendedAttribute entry in this) + { + entry.WriteBytes(buffer, offset); + offset += entry.Length; + } + } + + public int Length + { + get + { + int length = 0; + foreach (FullExtendedAttribute entry in this) + { + length += entry.Length; + } + return length; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/Enums/DeviceCharacteristics.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/Enums/DeviceCharacteristics.cs new file mode 100644 index 0000000..69f47d2 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/Enums/DeviceCharacteristics.cs @@ -0,0 +1,16 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum DeviceCharacteristics : uint + { + FILE_REMOVABLE_MEDIA = 0x0001, + FILE_READ_ONLY_DEVICE = 0x0002, + FILE_FLOPPY_DISKETTE = 0x0004, + FILE_WRITE_ONCE_MEDIA = 0x0008, + FILE_REMOTE_DEVICE = 0x0010, + FILE_DEVICE_IS_MOUNTED = 0x0020, + FILE_VIRTUAL_VOLUME = 0x0040, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/Enums/DeviceType.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/Enums/DeviceType.cs new file mode 100644 index 0000000..cc0810c --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/Enums/DeviceType.cs @@ -0,0 +1,51 @@ + +namespace SMBLibrary.SMB1 +{ + public enum DeviceType : uint + { + FILE_DEVICE_BEEP = 0x0001, + FILE_DEVICE_CD_ROM = 0x0002, + FILE_DEVICE_CD_ROM_FILE_SYSTEM = 0x0003, + FILE_DEVICE_CONTROLLER = 0x0004, + FILE_DEVICE_DATALINK = 0x0005, + FILE_DEVICE_DFS = 0x0006, + FILE_DEVICE_DISK = 0x0007, + FILE_DEVICE_DISK_FILE_SYSTEM = 0x0008, + FILE_DEVICE_FILE_SYSTEM = 0x0009, + FILE_DEVICE_INPORT_PORT = 0x000A, + FILE_DEVICE_KEYBOARD = 0x000B, + FILE_DEVICE_MAILSLOT = 0x000C, + FILE_DEVICE_MIDI_IN = 0x000D, + FILE_DEVICE_MIDI_OUT = 0x000E, + FILE_DEVICE_MOUSE = 0x000F, + FILE_DEVICE_MULTI_UNC_PROVIDER = 0x0010, + FILE_DEVICE_NAMED_PIPE = 0x0011, + FILE_DEVICE_NETWORK = 0x0012, + FILE_DEVICE_NETWORK_BROWSER = 0x0013, + FILE_DEVICE_NETWORK_FILE_SYSTEM = 0x0014, + FILE_DEVICE_NULL = 0x0015, + FILE_DEVICE_PARALLEL_PORT = 0x0016, + FILE_DEVICE_PHYSICAL_NETCARD = 0x0017, + FILE_DEVICE_PRINTER = 0x0018, + FILE_DEVICE_SCANNER = 0x0019, + FILE_DEVICE_SERIAL_MOUSE_PORT = 0x001A, + FILE_DEVICE_SERIAL_PORT = 0x001B, + FILE_DEVICE_SCREEN = 0x001C, + FILE_DEVICE_SOUND = 0x001D, + FILE_DEVICE_STREAMS = 0x001E, + FILE_DEVICE_TAPE = 0x001F, + FILE_DEVICE_TAPE_FILE_SYSTEM = 0x0020, + FILE_DEVICE_TRANSPORT = 0x0021, + FILE_DEVICE_UNKNOWN = 0x0022, + FILE_DEVICE_VIDEO = 0x0023, + FILE_DEVICE_VIRTUAL_DISK = 0x0024, + FILE_DEVICE_WAVE_IN = 0x0025, + FILE_DEVICE_WAVE_OUT = 0x0026, + FILE_DEVICE_8042_PORT = 0x0027, + FILE_DEVICE_NETWORK_REDIRECTOR = 0x0028, + FILE_DEVICE_BATTERY = 0x0029, + FILE_DEVICE_BUS_EXTENDER = 0x002A, + FILE_DEVICE_MODEM = 0x002B, + FILE_DEVICE_VDM = 0x002C, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/Enums/FileSystemAttributes.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/Enums/FileSystemAttributes.cs new file mode 100644 index 0000000..f3d315a --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/Enums/FileSystemAttributes.cs @@ -0,0 +1,29 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum FileSystemAttributes : uint + { + FILE_CASE_SENSITIVE_SEARCH = 0x0001, + FILE_CASE_PRESERVED_NAMES = 0x0002, + FILE_UNICODE_ON_DISK = 0x0004, + FILE_PERSISTENT_ACLS = 0x0008, + FILE_FILE_COMPRESSION = 0x0010, + FILE_VOLUME_QUOTAS = 0x0020, // SMB 1.0 addition + FILE_SUPPORTS_SPARSE_FILES = 0x0040, // SMB 1.0 addition + FILE_SUPPORTS_REPARSE_POINTS = 0x0080, // SMB 1.0 addition + FILE_SUPPORTS_REMOTE_STORAGE = 0x0100, // SMB 1.0 addition + FILE_VOLUME_IS_COMPRESSED = 0x8000, + FILE_SUPPORTS_OBJECT_IDS = 0x00010000, // SMB 1.0 addition + FILE_SUPPORTS_ENCRYPTION = 0x00020000, // SMB 1.0 addition + FILE_NAMED_STREAMS = 0x00040000, // SMB 1.0 addition + FILE_READ_ONLY_VOLUME = 0x00080000, // SMB 1.0 addition + FILE_SEQUENTIAL_WRITE_ONCE = 0x00100000, // SMB 1.0 addition + FILE_SUPPORTS_TRANSACTIONS = 0x00200000, // SMB 1.0 addition + FILE_SUPPORTS_HARD_LINKS = 0x00400000, // SMB 1.0 addition + FILE_SUPPORTS_EXTENDED_ATTRIBUTES = 0x00800000, // SMB 1.0 addition + FILE_SUPPORTS_OPEN_BY_FILE_ID = 0x01000000, // SMB 1.0 addition + FILE_SUPPORTS_USN_JOURNAL = 0x02000000, // SMB 1.0 addition + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSAttibuteInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSAttibuteInfo.cs new file mode 100644 index 0000000..ee68d39 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSAttibuteInfo.cs @@ -0,0 +1,57 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FS_ATTRIBUTE_INFO + /// + public class QueryFSAttibuteInfo : QueryFSInformation + { + public const int FixedLength = 12; + + public FileSystemAttributes FileSystemAttributes; + public uint MaxFileNameLengthInBytes; + //uint LengthOfFileSystemName; // In bytes + public string FileSystemName; // Unicode + + public QueryFSAttibuteInfo() + { + } + + public QueryFSAttibuteInfo(byte[] buffer, int offset) + { + FileSystemAttributes = (FileSystemAttributes)LittleEndianConverter.ToUInt32(buffer, offset + 0); + MaxFileNameLengthInBytes = LittleEndianConverter.ToUInt32(buffer, offset + 4); + uint lengthOfFileSystemName = LittleEndianConverter.ToUInt32(buffer, offset + 8); + FileSystemName = ByteReader.ReadUTF16String(buffer, offset + 12, (int)(lengthOfFileSystemName / 2)); + } + + public override byte[] GetBytes(bool isUnicode) + { + uint lengthOfFileSystemName = (uint)(FileSystemName.Length * 2); + byte[] buffer = new byte[this.Length]; + LittleEndianWriter.WriteUInt32(buffer, 0, (uint)FileSystemAttributes); + LittleEndianWriter.WriteUInt32(buffer, 4, MaxFileNameLengthInBytes); + LittleEndianWriter.WriteUInt32(buffer, 8, lengthOfFileSystemName); + ByteWriter.WriteUTF16String(buffer, 12, FileSystemName); + return buffer; + } + + public int Length + { + get + { + return FixedLength + FileSystemName.Length * 2; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSDeviceInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSDeviceInfo.cs new file mode 100644 index 0000000..fb767d8 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSDeviceInfo.cs @@ -0,0 +1,42 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FS_DEVICE_INFO + /// + public class QueryFSDeviceInfo : QueryFSInformation + { + public const int Length = 8; + + public DeviceType DeviceType; + public DeviceCharacteristics DeviceCharacteristics; + + public QueryFSDeviceInfo() + { + } + + public QueryFSDeviceInfo(byte[] buffer, int offset) + { + DeviceType = (DeviceType)LittleEndianConverter.ToUInt32(buffer, offset + 0); + DeviceCharacteristics = (DeviceCharacteristics)LittleEndianConverter.ToUInt32(buffer, offset + 4); + } + + public override byte[] GetBytes(bool isUnicode) + { + byte[] buffer = new byte[Length]; + LittleEndianWriter.WriteUInt32(buffer, 0, (uint)DeviceType); + LittleEndianWriter.WriteUInt32(buffer, 4, (uint)DeviceCharacteristics); + return buffer; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSInfoAllocation.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSInfoAllocation.cs new file mode 100644 index 0000000..ad3c200 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSInfoAllocation.cs @@ -0,0 +1,51 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_INFO_ALLOCATION + /// + public class QueryFSInfoAllocation : QueryFSInformation + { + public const int Length = 18; + + public uint FileSystemID; // File system identifier, Windows Server will set it to 0 + public uint SectorUnit; // Number of sectors per allocation unit + public uint UnitsTotal; // Total number of allocation units + public uint UnitsAvailable; // Total number of available allocation units + public ushort Sector; // Number of bytes per sector + + public QueryFSInfoAllocation() + { + } + + public QueryFSInfoAllocation(byte[] buffer, int offset) + { + FileSystemID = LittleEndianConverter.ToUInt32(buffer, offset + 0); + SectorUnit = LittleEndianConverter.ToUInt32(buffer, offset + 4); + UnitsTotal = LittleEndianConverter.ToUInt32(buffer, offset + 8); + UnitsAvailable = LittleEndianConverter.ToUInt32(buffer, offset + 12); + Sector = LittleEndianConverter.ToUInt16(buffer, offset + 16); + } + + public override byte[] GetBytes(bool isUnicode) + { + byte[] buffer = new byte[Length]; + LittleEndianWriter.WriteUInt32(buffer, 0, FileSystemID); + LittleEndianWriter.WriteUInt32(buffer, 4, SectorUnit); + LittleEndianWriter.WriteUInt32(buffer, 8, UnitsTotal); + LittleEndianWriter.WriteUInt32(buffer, 12, UnitsAvailable); + LittleEndianWriter.WriteUInt16(buffer, 16, Sector); + return buffer; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSInfoVolume.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSInfoVolume.cs new file mode 100644 index 0000000..fad2dcd --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSInfoVolume.cs @@ -0,0 +1,60 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_INFO_VOLUME + /// + public class QueryFSInfoVolume : QueryFSInformation + { + public uint VolumeSerialNumber; + //byte CharCount; + public string VolumeLabel; // SMB_STRING + + public QueryFSInfoVolume() + { + } + + public QueryFSInfoVolume(bool isUnicode, byte[] buffer, int offset) + { + VolumeSerialNumber = LittleEndianConverter.ToUInt32(buffer, offset + 0); + byte charCount = ByteReader.ReadByte(buffer, offset + 4); + VolumeLabel = SMBHelper.ReadSMBString(buffer, offset + 5, isUnicode); + } + + public override byte[] GetBytes(bool isUnicode) + { + byte charCount = (byte)VolumeLabel.Length; + + int length = GetLength(isUnicode); + byte[] buffer = new byte[length]; + LittleEndianWriter.WriteUInt32(buffer, 0, VolumeSerialNumber); + ByteWriter.WriteByte(buffer, 4, charCount); + SMBHelper.WriteSMBString(buffer, 5, isUnicode, VolumeLabel); + return buffer; + } + + public int GetLength(bool isUnicode) + { + int length = 5; + if (isUnicode) + { + length += VolumeLabel.Length * 2; + } + else + { + length += VolumeLabel.Length; + } + return length; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSInformation.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSInformation.cs new file mode 100644 index 0000000..61ab50c --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSInformation.cs @@ -0,0 +1,39 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public abstract class QueryFSInformation + { + public abstract byte[] GetBytes(bool isUnicode); + + public static QueryFSInformation GetQueryFSInformation(byte[] buffer, QueryFSInformationLevel informationLevel, bool isUnicode) + { + switch (informationLevel) + { + case QueryFSInformationLevel.SMB_INFO_ALLOCATION: + return new QueryFSInfoAllocation(buffer, 0); + case QueryFSInformationLevel.SMB_INFO_VOLUME: + return new QueryFSInfoVolume(isUnicode, buffer, 0); + case QueryFSInformationLevel.SMB_QUERY_FS_VOLUME_INFO: + return new QueryFSVolumeInfo(buffer, 0); + case QueryFSInformationLevel.SMB_QUERY_FS_SIZE_INFO: + return new QueryFSSizeInfo(buffer, 0); + case QueryFSInformationLevel.SMB_QUERY_FS_DEVICE_INFO: + return new QueryFSDeviceInfo(buffer, 0); + case QueryFSInformationLevel.SMB_QUERY_FS_ATTRIBUTE_INFO: + return new QueryFSAttibuteInfo(buffer, 0); + default: + throw new InvalidRequestException(); + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSSizeInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSSizeInfo.cs new file mode 100644 index 0000000..3200812 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSSizeInfo.cs @@ -0,0 +1,48 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FS_SIZE_INFO + /// + public class QueryFSSizeInfo : QueryFSInformation + { + public const int Length = 24; + + public ulong TotalAllocationUnits; + public ulong TotalFreeAllocationUnits; + public uint SectorsPerAllocationUnit; + public uint BytesPerSector; + + public QueryFSSizeInfo() + { + } + + public QueryFSSizeInfo(byte[] buffer, int offset) + { + TotalAllocationUnits = LittleEndianConverter.ToUInt64(buffer, 0); + TotalFreeAllocationUnits = LittleEndianConverter.ToUInt64(buffer, 8); + SectorsPerAllocationUnit = LittleEndianConverter.ToUInt32(buffer, 16); + BytesPerSector = LittleEndianConverter.ToUInt32(buffer, 20); + } + + public override byte[] GetBytes(bool isUnicode) + { + byte[] buffer = new byte[Length]; + LittleEndianWriter.WriteUInt64(buffer, 0, TotalAllocationUnits); + LittleEndianWriter.WriteUInt64(buffer, 8, TotalFreeAllocationUnits); + LittleEndianWriter.WriteUInt32(buffer, 16, SectorsPerAllocationUnit); + LittleEndianWriter.WriteUInt32(buffer, 20, BytesPerSector); + return buffer; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSVolumeInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSVolumeInfo.cs new file mode 100644 index 0000000..ae24bd9 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryFSInformation/QueryFSVolumeInfo.cs @@ -0,0 +1,54 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FS_VOLUME_INFO + /// + public class QueryFSVolumeInfo : QueryFSInformation + { + public const int FixedLength = 18; + + public DateTime VolumeCreationTime; + public uint SerialNumber; + //uint VolumeLabelSize; + public ushort Reserved; + public string VolumeLabel; // Unicode + + public QueryFSVolumeInfo() + { + VolumeLabel = String.Empty; + } + + public QueryFSVolumeInfo(byte[] buffer, int offset) + { + VolumeCreationTime = SMBHelper.ReadFileTime(buffer, offset + 0); + SerialNumber = LittleEndianConverter.ToUInt32(buffer, offset + 8); + uint volumeLabelSize = LittleEndianConverter.ToUInt32(buffer, offset + 12); + Reserved = LittleEndianConverter.ToUInt16(buffer, offset + 16); + VolumeLabel = ByteReader.ReadUTF16String(buffer, offset + 18, (int)volumeLabelSize); + } + + public override byte[] GetBytes(bool isUnicode) + { + uint volumeLabelSize = (uint)(VolumeLabel.Length * 2); + + byte[] buffer = new byte[FixedLength + volumeLabelSize]; + SMBHelper.WriteFileTime(buffer, 0, VolumeCreationTime); + LittleEndianWriter.WriteUInt32(buffer, 8, SerialNumber); + LittleEndianWriter.WriteUInt32(buffer, 12, volumeLabelSize); + LittleEndianWriter.WriteUInt16(buffer, 16, Reserved); + ByteWriter.WriteUTF16String(buffer, 18, VolumeLabel); + return buffer; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/Enums/CompressionFormat.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/Enums/CompressionFormat.cs new file mode 100644 index 0000000..cb9fa27 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/Enums/CompressionFormat.cs @@ -0,0 +1,10 @@ + +namespace SMBLibrary.SMB1 +{ + public enum CompressionFormat : ushort + { + COMPRESSION_FORMAT_NONE = 0x0000, + COMPRESSION_FORMAT_DEFAULT = 0x0001, + COMPRESSION_FORMAT_LZNT1 = 0x0002, + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryAllExtendedAttributes.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryAllExtendedAttributes.cs new file mode 100644 index 0000000..fc2c31e --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryAllExtendedAttributes.cs @@ -0,0 +1,44 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_INFO_QUERY_ALL_EAS + /// + public class QueryAllExtendedAttributes : QueryInformation + { + public FullExtendedAttributeList ExtendedAttributeList; + + public QueryAllExtendedAttributes() + { + ExtendedAttributeList = new FullExtendedAttributeList(); + } + + public QueryAllExtendedAttributes(byte[] buffer, int offset) + { + ExtendedAttributeList = new FullExtendedAttributeList(buffer, offset); + } + + public override byte[] GetBytes() + { + return ExtendedAttributeList.GetBytes(); + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_INFO_QUERY_ALL_EAS; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryEASize.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryEASize.cs new file mode 100644 index 0000000..c8a405d --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryEASize.cs @@ -0,0 +1,66 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_INFO_QUERY_EA_SIZE + /// + public class QueryEASize : QueryInformation + { + public const int Length = 26; + + public DateTime CreationDateTime; + public DateTime LastAccessDateTime; + public DateTime LastWriteDateTime; + public uint FileDataSize; + public uint AllocationSize; + public FileAttributes Attributes; + public uint EASize; + + public QueryEASize() + { + } + + public QueryEASize(byte[] buffer, int offset) + { + CreationDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + LastAccessDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + LastWriteDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + FileDataSize = LittleEndianReader.ReadUInt32(buffer, ref offset); + AllocationSize = LittleEndianReader.ReadUInt32(buffer, ref offset); + Attributes = (FileAttributes)LittleEndianReader.ReadUInt16(buffer, ref offset); + EASize = LittleEndianReader.ReadUInt32(buffer, ref offset); + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + int offset = 0; + SMBHelper.WriteSMBDateTime(buffer, ref offset, CreationDateTime); + SMBHelper.WriteSMBDateTime(buffer, ref offset, LastAccessDateTime); + SMBHelper.WriteSMBDateTime(buffer, ref offset, LastWriteDateTime); + LittleEndianWriter.WriteUInt32(buffer, ref offset, FileDataSize); + LittleEndianWriter.WriteUInt32(buffer, ref offset, AllocationSize); + LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)Attributes); + LittleEndianWriter.WriteUInt32(buffer, ref offset, EASize); + return buffer; + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_INFO_QUERY_EA_SIZE; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryExtendedAttributesFromList.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryExtendedAttributesFromList.cs new file mode 100644 index 0000000..166a6a6 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryExtendedAttributesFromList.cs @@ -0,0 +1,44 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_INFO_QUERY_EAS_FROM_LIST + /// + public class QueryExtendedAttributesFromList : QueryInformation + { + public FullExtendedAttributeList ExtendedAttributeList; + + public QueryExtendedAttributesFromList() + { + ExtendedAttributeList = new FullExtendedAttributeList(); + } + + public QueryExtendedAttributesFromList(byte[] buffer, int offset) + { + ExtendedAttributeList = new FullExtendedAttributeList(buffer, offset); + } + + public override byte[] GetBytes() + { + return ExtendedAttributeList.GetBytes(); + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileAllInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileAllInfo.cs new file mode 100644 index 0000000..ba67c07 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileAllInfo.cs @@ -0,0 +1,91 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FILE_ALL_INFO + /// + public class QueryFileAllInfo : QueryInformation + { + public const int FixedLength = 72; + + public DateTime CreationDateTime; + public DateTime LastAccessDateTime; + public DateTime LastWriteDateTime; + public DateTime LastChangeTime; + public ExtendedFileAttributes ExtFileAttributes; + public uint Reserved1; + public ulong AllocationSize; + public ulong EndOfFile; + public uint NumberOfLinks; + public bool DeletePending; + public bool Directory; + public ushort Reserved2; + public uint EASize; + //uint FileNameLength; // In bytes + public string FileName; // Unicode + + public QueryFileAllInfo() + { + } + + public QueryFileAllInfo(byte[] buffer, int offset) + { + CreationDateTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastAccessDateTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastWriteDateTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastChangeTime = SMBHelper.ReadFileTime(buffer, ref offset); + ExtFileAttributes = (ExtendedFileAttributes)LittleEndianReader.ReadUInt32(buffer, ref offset); + Reserved1 = LittleEndianReader.ReadUInt32(buffer, ref offset); + AllocationSize = LittleEndianReader.ReadUInt64(buffer, ref offset); + EndOfFile = LittleEndianReader.ReadUInt64(buffer, ref offset); + NumberOfLinks = LittleEndianReader.ReadUInt32(buffer, ref offset); + DeletePending = (ByteReader.ReadByte(buffer, ref offset) > 0); + Directory = (ByteReader.ReadByte(buffer, ref offset) > 0); + Reserved2 = LittleEndianReader.ReadUInt16(buffer, ref offset); + EASize = LittleEndianReader.ReadUInt32(buffer, ref offset); + uint fileNameLength = LittleEndianReader.ReadUInt32(buffer, ref offset); + FileName = ByteReader.ReadUTF16String(buffer, ref offset, (int)(fileNameLength / 2)); + } + + public override byte[] GetBytes() + { + uint fileNameLength = (uint)(FileName.Length * 2); + byte[] buffer = new byte[FixedLength + fileNameLength]; + int offset = 0; + SMBHelper.WriteFileTime(buffer, ref offset, CreationDateTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastAccessDateTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastWriteDateTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastChangeTime); + LittleEndianWriter.WriteUInt32(buffer, ref offset, (uint)ExtFileAttributes); + LittleEndianWriter.WriteUInt32(buffer, ref offset, Reserved1); + LittleEndianWriter.WriteUInt64(buffer, ref offset, AllocationSize); + LittleEndianWriter.WriteUInt64(buffer, ref offset, EndOfFile); + LittleEndianWriter.WriteUInt32(buffer, ref offset, NumberOfLinks); + ByteWriter.WriteByte(buffer, ref offset, Convert.ToByte(DeletePending)); + ByteWriter.WriteByte(buffer, ref offset, Convert.ToByte(Directory)); + LittleEndianWriter.WriteUInt16(buffer, ref offset, Reserved2); + LittleEndianWriter.WriteUInt32(buffer, ref offset, EASize); + LittleEndianWriter.WriteUInt32(buffer, ref offset, fileNameLength); + ByteWriter.WriteUTF16String(buffer, ref offset, FileName); + return buffer; + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_QUERY_FILE_ALL_INFO; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileAltNameInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileAltNameInfo.cs new file mode 100644 index 0000000..8f8ebf5 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileAltNameInfo.cs @@ -0,0 +1,49 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FILE_ALT_NAME_INFO + /// + public class QueryFileAltNameInfo : QueryInformation + { + //uint FileNameLength; // In bytes + public string FileName; // Unicode, 8.3 name of the file + + public QueryFileAltNameInfo() + { + } + + public QueryFileAltNameInfo(byte[] buffer, int offset) + { + uint fileNameLength = LittleEndianReader.ReadUInt32(buffer, ref offset); + FileName = ByteReader.ReadUTF16String(buffer, ref offset, (int)(fileNameLength / 2)); + } + + public override byte[] GetBytes() + { + uint fileNameLength = (uint)(FileName.Length * 2); + byte[] buffer = new byte[4 + fileNameLength]; + LittleEndianWriter.WriteUInt32(buffer, 0, fileNameLength); + ByteWriter.WriteUTF16String(buffer, 4, FileName); + return buffer; + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_QUERY_FILE_ALT_NAME_INFO; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileBasicInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileBasicInfo.cs new file mode 100644 index 0000000..2c6b25c --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileBasicInfo.cs @@ -0,0 +1,63 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FILE_BASIC_INFO + /// + public class QueryFileBasicInfo : QueryInformation + { + public const int Length = 40; + + public DateTime CreationDateTime; + public DateTime LastAccessDateTime; + public DateTime LastWriteDateTime; + public DateTime LastChangeTime; + public ExtendedFileAttributes ExtFileAttributes; + public uint Reserved; + + public QueryFileBasicInfo() + { + } + + public QueryFileBasicInfo(byte[] buffer, int offset) + { + CreationDateTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastAccessDateTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastWriteDateTime = SMBHelper.ReadFileTime(buffer, ref offset); + LastChangeTime = SMBHelper.ReadFileTime(buffer, ref offset); + ExtFileAttributes = (ExtendedFileAttributes)LittleEndianReader.ReadUInt32(buffer, ref offset); + Reserved = LittleEndianReader.ReadUInt32(buffer, ref offset); + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + int offset = 0; + SMBHelper.WriteFileTime(buffer, ref offset, CreationDateTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastAccessDateTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastWriteDateTime); + SMBHelper.WriteFileTime(buffer, ref offset, LastChangeTime); + LittleEndianWriter.WriteUInt32(buffer, ref offset, (uint)ExtFileAttributes); + LittleEndianWriter.WriteUInt32(buffer, ref offset, Reserved); + return buffer; + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_QUERY_FILE_BASIC_INFO; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileCompressionInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileCompressionInfo.cs new file mode 100644 index 0000000..61478a5 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileCompressionInfo.cs @@ -0,0 +1,64 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FILE_COMPRESSION_INFO + /// + public class QueryFileCompressionInfo : QueryInformation + { + public const int Length = 16; + + public ulong CompressedFileSize; + public CompressionFormat CompressionFormat; + public byte CompressionUnitShift; + public byte ChunkShift; + public byte ClusterShift; + public byte[] Reserved; // 3 bytes + + public QueryFileCompressionInfo() + { + Reserved = new byte[3]; + } + + public QueryFileCompressionInfo(byte[] buffer, int offset) + { + CompressedFileSize = LittleEndianReader.ReadUInt64(buffer, ref offset); + CompressionFormat = (CompressionFormat)LittleEndianReader.ReadUInt16(buffer, ref offset); + CompressionUnitShift = ByteReader.ReadByte(buffer, ref offset); + ChunkShift = ByteReader.ReadByte(buffer, ref offset); + ClusterShift = ByteReader.ReadByte(buffer, ref offset); + Reserved = ByteReader.ReadBytes(buffer, ref offset, 3); + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + int offset = 0; + LittleEndianWriter.WriteUInt64(buffer, ref offset, CompressedFileSize); + LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)CompressionFormat); + ByteWriter.WriteByte(buffer, ref offset, CompressionUnitShift); + ByteWriter.WriteByte(buffer, ref offset, ChunkShift); + ByteWriter.WriteByte(buffer, ref offset, ClusterShift); + ByteWriter.WriteBytes(buffer, ref offset, Reserved, 3); + return buffer; + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_QUERY_FILE_COMPRESSION_INFO; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileExtendedAttributeInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileExtendedAttributeInfo.cs new file mode 100644 index 0000000..f7a6013 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileExtendedAttributeInfo.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FILE_EA_INFO + /// + public class QueryFileExtendedAttributeInfo : QueryInformation + { + public uint EASize; + + public QueryFileExtendedAttributeInfo() + { + } + + public QueryFileExtendedAttributeInfo(byte[] buffer, int offset) + { + EASize = LittleEndianConverter.ToUInt32(buffer, offset); + } + + public override byte[] GetBytes() + { + return LittleEndianConverter.GetBytes(EASize); + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_QUERY_FILE_EA_INFO; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileNameInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileNameInfo.cs new file mode 100644 index 0000000..ff544ff --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileNameInfo.cs @@ -0,0 +1,49 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FILE_NAME_INFO + /// + public class QueryFileNameInfo : QueryInformation + { + //uint FileNameLength; // In bytes + public string FileName; // Unicode + + public QueryFileNameInfo() + { + } + + public QueryFileNameInfo(byte[] buffer, int offset) + { + uint fileNameLength = LittleEndianConverter.ToUInt32(buffer, 0); + FileName = ByteReader.ReadUTF16String(buffer, 4, (int)(fileNameLength / 2)); + } + + public override byte[] GetBytes() + { + uint fileNameLength = (uint)(FileName.Length * 2); + byte[] buffer = new byte[4 + fileNameLength]; + LittleEndianWriter.WriteUInt32(buffer, 0, fileNameLength); + ByteWriter.WriteUTF16String(buffer, 4, FileName); + return buffer; + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_QUERY_FILE_NAME_INFO; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileStandardInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileStandardInfo.cs new file mode 100644 index 0000000..afdde93 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileStandardInfo.cs @@ -0,0 +1,60 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FILE_STANDARD_INFO + /// + public class QueryFileStandardInfo : QueryInformation + { + public const int Length = 22; + + public ulong AllocationSize; + public ulong EndOfFile; + public uint NumberOfLinks; + public bool DeletePending; + public bool Directory; + + public QueryFileStandardInfo() + { + } + + public QueryFileStandardInfo(byte[] buffer, int offset) + { + AllocationSize = LittleEndianReader.ReadUInt64(buffer, ref offset); + EndOfFile = LittleEndianReader.ReadUInt64(buffer, ref offset); + NumberOfLinks = LittleEndianReader.ReadUInt32(buffer, ref offset); + DeletePending = (ByteReader.ReadByte(buffer, ref offset) > 0); + Directory = (ByteReader.ReadByte(buffer, ref offset) > 0); + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + int offset = 0; + LittleEndianWriter.WriteUInt64(buffer, ref offset, AllocationSize); + LittleEndianWriter.WriteUInt64(buffer, ref offset, EndOfFile); + LittleEndianWriter.WriteUInt32(buffer, ref offset, NumberOfLinks); + ByteWriter.WriteByte(buffer, ref offset, Convert.ToByte(DeletePending)); + ByteWriter.WriteByte(buffer, ref offset, Convert.ToByte(Directory)); + return buffer; + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_QUERY_FILE_STANDARD_INFO; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileStreamInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileStreamInfo.cs new file mode 100644 index 0000000..4a13013 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryFileStreamInfo.cs @@ -0,0 +1,61 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_QUERY_FILE_STREAM_INFO + /// + public class QueryFileStreamInfo : QueryInformation + { + public const int FixedLength = 24; + + public uint NextEntryOffset; + //uint StreamNameLength; // In bytes + public ulong StreamSize; + public ulong StreamAllocationSize; + public string StreamName; // Unicode + + public QueryFileStreamInfo() + { + } + + public QueryFileStreamInfo(byte[] buffer, int offset) + { + NextEntryOffset = LittleEndianReader.ReadUInt32(buffer, ref offset); + uint streamNameLength = LittleEndianReader.ReadUInt32(buffer, ref offset); + StreamSize = LittleEndianReader.ReadUInt64(buffer, ref offset); + StreamAllocationSize = LittleEndianReader.ReadUInt64(buffer, ref offset); + StreamName = ByteReader.ReadUTF16String(buffer, ref offset, (int)(streamNameLength / 2)); + } + + public override byte[] GetBytes() + { + uint streamNameLength = (uint)(StreamName.Length * 2); + byte[] buffer = new byte[FixedLength + streamNameLength]; + int offset = 0; + LittleEndianWriter.WriteUInt32(buffer, ref offset, NextEntryOffset); + LittleEndianWriter.WriteUInt32(buffer, ref offset, streamNameLength); + LittleEndianWriter.WriteUInt64(buffer, ref offset, StreamSize); + LittleEndianWriter.WriteUInt64(buffer, ref offset, StreamAllocationSize); + ByteWriter.WriteUTF16String(buffer, ref offset, StreamName); + return buffer; + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_QUERY_FILE_STREAM_INFO; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryInfoStandard.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryInfoStandard.cs new file mode 100644 index 0000000..19c2904 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryInfoStandard.cs @@ -0,0 +1,63 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_INFO_STANDARD + /// + public class QueryInfoStandard : QueryInformation + { + public const int Length = 22; + + public DateTime CreationDateTime; + public DateTime LastAccessDateTime; + public DateTime LastWriteDateTime; + public uint FileDataSize; + public uint AllocationSize; + public FileAttributes Attributes; + + public QueryInfoStandard() + { + } + + public QueryInfoStandard(byte[] buffer, int offset) + { + CreationDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + LastAccessDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + LastWriteDateTime = SMBHelper.ReadSMBDateTime(buffer, ref offset); + FileDataSize = LittleEndianReader.ReadUInt32(buffer, ref offset); + AllocationSize = LittleEndianReader.ReadUInt32(buffer, ref offset); + Attributes = (FileAttributes)LittleEndianReader.ReadUInt16(buffer, ref offset); + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + int offset = 0; + SMBHelper.WriteSMBDateTime(buffer, ref offset, CreationDateTime); + SMBHelper.WriteSMBDateTime(buffer, ref offset, LastAccessDateTime); + SMBHelper.WriteSMBDateTime(buffer, ref offset, LastWriteDateTime); + LittleEndianWriter.WriteUInt32(buffer, ref offset, FileDataSize); + LittleEndianWriter.WriteUInt32(buffer, ref offset, AllocationSize); + LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)Attributes); + return buffer; + } + + public override QueryInformationLevel InformationLevel + { + get + { + return QueryInformationLevel.SMB_INFO_STANDARD; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryInformation.cs b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryInformation.cs new file mode 100644 index 0000000..8dd0cd7 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/QueryInformation/QueryInformation.cs @@ -0,0 +1,59 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public abstract class QueryInformation + { + public abstract byte[] GetBytes(); + + public abstract QueryInformationLevel InformationLevel + { + get; + } + + /// + /// SMB_INFO_IS_NAME_VALID will return null + /// + public static QueryInformation GetQueryInformation(byte[] buffer, QueryInformationLevel informationLevel) + { + switch (informationLevel) + { + case QueryInformationLevel.SMB_INFO_STANDARD: + return new QueryInfoStandard(buffer, 0); + case QueryInformationLevel.SMB_INFO_QUERY_EA_SIZE: + return new QueryEASize(buffer, 0); + case QueryInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST: + return new QueryExtendedAttributesFromList(buffer, 0); + case QueryInformationLevel.SMB_INFO_QUERY_ALL_EAS: + return new QueryAllExtendedAttributes(buffer, 0); + case QueryInformationLevel.SMB_QUERY_FILE_BASIC_INFO: + return new QueryFileBasicInfo(buffer, 0); + case QueryInformationLevel.SMB_QUERY_FILE_STANDARD_INFO: + return new QueryFileStandardInfo(buffer, 0); + case QueryInformationLevel.SMB_QUERY_FILE_EA_INFO: + return new QueryFileExtendedAttributeInfo(buffer, 0); + case QueryInformationLevel.SMB_QUERY_FILE_NAME_INFO: + return new QueryFileNameInfo(buffer, 0); + case QueryInformationLevel.SMB_QUERY_FILE_ALL_INFO: + return new QueryFileAllInfo(buffer, 0); + case QueryInformationLevel.SMB_QUERY_FILE_ALT_NAME_INFO: + return new QueryFileAltNameInfo(buffer, 0); + case QueryInformationLevel.SMB_QUERY_FILE_STREAM_INFO: + return new QueryFileStreamInfo(buffer, 0); + case QueryInformationLevel.SMB_QUERY_FILE_COMPRESSION_INFO: + return new QueryFileCompressionInfo(buffer, 0); + default: + throw new InvalidRequestException(); + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetExtendedAttributes.cs b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetExtendedAttributes.cs new file mode 100644 index 0000000..b9c150b --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetExtendedAttributes.cs @@ -0,0 +1,40 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_INFO_SET_EAS + /// + public class SetExtendedAttributes : SetInformation + { + public FullExtendedAttributeList ExtendedAttributeList; + + public SetExtendedAttributes() + { + ExtendedAttributeList = new FullExtendedAttributeList(); + } + + public SetExtendedAttributes(byte[] buffer) : this(buffer, 0) + { + } + + public SetExtendedAttributes(byte[] buffer, int offset) + { + ExtendedAttributeList = new FullExtendedAttributeList(buffer, offset); + } + + public override byte[] GetBytes() + { + return ExtendedAttributeList.GetBytes(); + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileAllocationInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileAllocationInfo.cs new file mode 100644 index 0000000..82a62a8 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileAllocationInfo.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_SET_FILE_ALLOCATION_INFO + /// + public class SetFileAllocationInfo : SetInformation + { + public const int Length = 8; + + public ulong AllocationSize; + + public SetFileAllocationInfo() + { + } + + public SetFileAllocationInfo(byte[] buffer) : this(buffer, 0) + { + } + + public SetFileAllocationInfo(byte[] buffer, int offset) + { + AllocationSize = LittleEndianConverter.ToUInt64(buffer, offset); + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + LittleEndianWriter.WriteUInt64(buffer, 0, AllocationSize); + return buffer; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileBasicInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileBasicInfo.cs new file mode 100644 index 0000000..04b2840 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileBasicInfo.cs @@ -0,0 +1,58 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_SET_FILE_BASIC_INFO + /// + public class SetFileBasicInfo : SetInformation + { + public const int Length = 40; + + public DateTime CreationTime; + public DateTime LastAccessTime; + public DateTime LastWriteTime; + public DateTime LastChangeTime; + public ExtendedFileAttributes ExtFileAttributes; + public uint Reserved; + + public SetFileBasicInfo() + { + } + + public SetFileBasicInfo(byte[] buffer) : this(buffer, 0) + { + } + + public SetFileBasicInfo(byte[] buffer, int offset) + { + CreationTime = SMBHelper.ReadSetFileTime(buffer, offset + 0); + LastAccessTime = SMBHelper.ReadSetFileTime(buffer, offset + 8); + LastWriteTime = SMBHelper.ReadSetFileTime(buffer, offset + 16); + LastChangeTime = SMBHelper.ReadSetFileTime(buffer, offset + 24); + ExtFileAttributes = (ExtendedFileAttributes)LittleEndianConverter.ToUInt32(buffer, offset + 32); + Reserved = LittleEndianConverter.ToUInt32(buffer, offset + 36); + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + SMBHelper.WriteFileTime(buffer, 0, CreationTime); + SMBHelper.WriteFileTime(buffer, 8, LastAccessTime); + SMBHelper.WriteFileTime(buffer, 16, LastWriteTime); + SMBHelper.WriteFileTime(buffer, 24, LastChangeTime); + LittleEndianWriter.WriteUInt32(buffer, 32, (uint)ExtFileAttributes); + LittleEndianWriter.WriteUInt32(buffer, 36, Reserved); + return buffer; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileDispositionInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileDispositionInfo.cs new file mode 100644 index 0000000..2a2e44a --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileDispositionInfo.cs @@ -0,0 +1,45 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_SET_FILE_DISPOSITION_INFO + /// + public class SetFileDispositionInfo : SetInformation + { + public const int Length = 1; + /// + /// Indicate that a file SHOULD be deleted when it is closed. + /// + public bool DeletePending; + + public SetFileDispositionInfo() + { + } + + public SetFileDispositionInfo(byte[] buffer) : this(buffer, 0) + { + } + + public SetFileDispositionInfo(byte[] buffer, int offset) + { + DeletePending = (ByteReader.ReadByte(buffer, ref offset) > 0); + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + ByteWriter.WriteByte(buffer, 0, Convert.ToByte(DeletePending)); + return buffer; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileEndOfFileInfo.cs b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileEndOfFileInfo.cs new file mode 100644 index 0000000..ea05df0 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetFileEndOfFileInfo.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_SET_FILE_END_OF_FILE_INFO + /// + public class SetFileEndOfFileInfo : SetInformation + { + public const int Length = 8; + + public ulong EndOfFile; + + public SetFileEndOfFileInfo() + { + } + + public SetFileEndOfFileInfo(byte[] buffer) : this(buffer, 0) + { + } + + public SetFileEndOfFileInfo(byte[] buffer, int offset) + { + EndOfFile = LittleEndianConverter.ToUInt64(buffer, offset); + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + LittleEndianWriter.WriteUInt64(buffer, 0, EndOfFile); + return buffer; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetInfoStandard.cs b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetInfoStandard.cs new file mode 100644 index 0000000..8d8da61 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetInfoStandard.cs @@ -0,0 +1,53 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// SMB_INFO_STANDARD + /// + public class SetInfoStandard : SetInformation + { + public const int Length = 22; + + public DateTime CreationDateTime; + public DateTime LastAccessDateTime; + public DateTime LastWriteDateTime; + public byte[] Reserved; // 10 bytes + + public SetInfoStandard() + { + Reserved = new byte[10]; + } + + public SetInfoStandard(byte[] buffer) : this(buffer, 0) + { + } + + public SetInfoStandard(byte[] buffer, int offset) + { + CreationDateTime = SMBHelper.ReadSMBDateTime(buffer, offset + 0); + LastAccessDateTime = SMBHelper.ReadSMBDateTime(buffer, offset + 4); + LastWriteDateTime = SMBHelper.ReadSMBDateTime(buffer, offset + 8); + Reserved = ByteReader.ReadBytes(buffer, offset + 12, 10); + } + + public override byte[] GetBytes() + { + byte[] buffer = new byte[Length]; + SMBHelper.WriteSMBDateTime(buffer, 0, CreationDateTime); + SMBHelper.WriteSMBDateTime(buffer, 4, LastAccessDateTime); + SMBHelper.WriteSMBDateTime(buffer, 8, LastWriteDateTime); + ByteWriter.WriteBytes(buffer, 12, Reserved); + return buffer; + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetInformation.cs b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetInformation.cs new file mode 100644 index 0000000..dc34f37 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/SetInformation/SetInformation.cs @@ -0,0 +1,39 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public abstract class SetInformation + { + public abstract byte[] GetBytes(); + + public static SetInformation GetSetInformation(byte[] buffer, SetInformationLevel informationLevel) + { + switch (informationLevel) + { + case SetInformationLevel.SMB_INFO_STANDARD: + return new SetInfoStandard(buffer); + case SetInformationLevel.SMB_INFO_SET_EAS: + return new SetExtendedAttributes(buffer); + case SetInformationLevel.SMB_SET_FILE_BASIC_INFO: + return new SetFileBasicInfo(buffer); + case SetInformationLevel.SMB_SET_FILE_DISPOSITION_INFO: + return new SetFileDispositionInfo(buffer); + case SetInformationLevel.SMB_SET_FILE_ALLOCATION_INFO: + return new SetFileAllocationInfo(buffer); + case SetInformationLevel.SMB_SET_FILE_END_OF_FILE_INFO: + return new SetFileEndOfFileInfo(buffer); + default: + throw new UnsupportedInformationLevelException(); + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2CreateDirectoryRequest.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2CreateDirectoryRequest.cs new file mode 100644 index 0000000..bd670dd --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2CreateDirectoryRequest.cs @@ -0,0 +1,63 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_CREATE_DIRECTORY Request + /// + public class Transaction2CreateDirectoryRequest : Transaction2Subcommand + { + // Parameters + public uint Reserved; + public string DirectoryName; // SMB_STRING + // Data + public FullExtendedAttributeList ExtendedAttributeList; + + public Transaction2CreateDirectoryRequest() : base() + {} + + public Transaction2CreateDirectoryRequest(byte[] parameters, byte[] data, bool isUnicode) : base() + { + Reserved = LittleEndianConverter.ToUInt32(parameters, 0); + DirectoryName = SMBHelper.ReadSMBString(parameters, 4, isUnicode); + ExtendedAttributeList = new FullExtendedAttributeList(data); + } + + public override byte[] GetSetup() + { + return LittleEndianConverter.GetBytes((ushort)SubcommandName); + } + + public override byte[] GetParameters(bool isUnicode) + { + int length = 4; + length += isUnicode ? DirectoryName.Length * 2 + 2 : DirectoryName.Length + 1 + 1; + byte[] parameters = new byte[length]; + LittleEndianWriter.WriteUInt32(parameters, 0, Reserved); + SMBHelper.WriteSMBString(parameters, 4, isUnicode, DirectoryName); + return parameters; + } + + public override byte[] GetData(bool isUnicode) + { + return ExtendedAttributeList.GetBytes(); + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_CREATE_DIRECTORY; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2CreateDirectoryResponse.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2CreateDirectoryResponse.cs new file mode 100644 index 0000000..64b9663 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2CreateDirectoryResponse.cs @@ -0,0 +1,47 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_CREATE_DIRECTORY Response + /// + public class Transaction2CreateDirectoryResponse : Transaction2Subcommand + { + // Parameters: + public ushort EaErrorOffset; + + public Transaction2CreateDirectoryResponse() : base() + { + + } + + public Transaction2CreateDirectoryResponse(byte[] parameters, byte[] data, QueryInformationLevel informationLevel, bool isUnicode) : base() + { + EaErrorOffset = LittleEndianConverter.ToUInt16(parameters, 0); + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[2]; + LittleEndianWriter.WriteUInt16(parameters, 0, EaErrorOffset); + return parameters; + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_CREATE_DIRECTORY; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindFirst2Request.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindFirst2Request.cs new file mode 100644 index 0000000..ac7c968 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindFirst2Request.cs @@ -0,0 +1,97 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_FIND_FIRST2 Request + /// + public class Transaction2FindFirst2Request : Transaction2Subcommand + { + // Parameters: + public FileAttributes SearchAttributes; + public ushort SearchCount; + public FindFlags Flags; + public FindInformationLevel InformationLevel; + public SearchStorageType SearchStorageType; + public string FileName; // SMB_STRING + // Data: + FullExtendedAttributeList GetExtendedAttributeList; // Used with FindInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST + + public Transaction2FindFirst2Request() : base() + { + GetExtendedAttributeList = new FullExtendedAttributeList(); + } + + public Transaction2FindFirst2Request(byte[] parameters, byte[] data, bool isUnicode) : base() + { + SearchAttributes = (FileAttributes)LittleEndianConverter.ToUInt16(parameters, 0); + SearchCount = LittleEndianConverter.ToUInt16(parameters, 2); + Flags = (FindFlags)LittleEndianConverter.ToUInt16(parameters, 4); + InformationLevel = (FindInformationLevel)LittleEndianConverter.ToUInt16(parameters, 6); + SearchStorageType = (SearchStorageType)LittleEndianConverter.ToUInt32(parameters, 8); + FileName = SMBHelper.ReadSMBString(parameters, 12, isUnicode); + + if (InformationLevel == FindInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST) + { + GetExtendedAttributeList = new FullExtendedAttributeList(data, 0); + } + } + + public override byte[] GetSetup() + { + return LittleEndianConverter.GetBytes((ushort)SubcommandName); + } + + public override byte[] GetParameters(bool isUnicode) + { + int length = 12; + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + + byte[] parameters = new byte[length]; + LittleEndianWriter.WriteUInt16(parameters, 0, (ushort)SearchAttributes); + LittleEndianWriter.WriteUInt16(parameters, 2, SearchCount); + LittleEndianWriter.WriteUInt16(parameters, 4, (ushort)Flags); + LittleEndianWriter.WriteUInt16(parameters, 6, (ushort)InformationLevel); + LittleEndianWriter.WriteUInt32(parameters, 8, (uint)SearchStorageType); + SMBHelper.WriteSMBString(parameters, 12, isUnicode, FileName); + + return parameters; + } + + public override byte[] GetData(bool isUnicode) + { + if (InformationLevel == FindInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST) + { + return GetExtendedAttributeList.GetBytes(); + } + else + { + return new byte[0]; + } + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_FIND_FIRST2; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindFirst2Response.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindFirst2Response.cs new file mode 100644 index 0000000..7514a2a --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindFirst2Response.cs @@ -0,0 +1,71 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_FIND_FIRST2 Response + /// + public class Transaction2FindFirst2Response : Transaction2Subcommand + { + public const int ParametersLength = 10; + // Parameters: + public ushort SID; // Search handle + public ushort SearchCount; + public bool EndOfSearch; + public ushort EaErrorOffset; + public ushort LastNameOffset; + // Data: + public FindInformation FindInfoList; + + public Transaction2FindFirst2Response() : base() + { + FindInfoList = new FindInformation(); + } + + public Transaction2FindFirst2Response(byte[] parameters, byte[] data, FindInformationLevel informationLevel, bool isUnicode, bool returnResumeKeys) : base() + { + SID = LittleEndianConverter.ToUInt16(parameters, 0); + SearchCount = LittleEndianConverter.ToUInt16(parameters, 2); + EndOfSearch = LittleEndianConverter.ToUInt16(parameters, 4) != 0; + EaErrorOffset = LittleEndianConverter.ToUInt16(parameters, 6); + LastNameOffset = LittleEndianConverter.ToUInt16(parameters, 8); + + FindInfoList = new FindInformation(data, informationLevel, isUnicode, returnResumeKeys); + } + + public override byte[] GetParameters(bool isUnicode) + { + SearchCount = (ushort)FindInfoList.Count; + + byte[] parameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(parameters, 0, SID); + LittleEndianWriter.WriteUInt16(parameters, 2, SearchCount); + LittleEndianWriter.WriteUInt16(parameters, 4, Convert.ToUInt16(EndOfSearch)); + LittleEndianWriter.WriteUInt16(parameters, 6, EaErrorOffset); + LittleEndianWriter.WriteUInt16(parameters, 8, LastNameOffset); + return parameters; + } + + public override byte[] GetData(bool isUnicode) + { + return FindInfoList.GetBytes(isUnicode); + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_FIND_FIRST2; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindNext2Request.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindNext2Request.cs new file mode 100644 index 0000000..aaa3db2 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindNext2Request.cs @@ -0,0 +1,77 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_FIND_NEXT2 Request + /// + public class Transaction2FindNext2Request : Transaction2Subcommand + { + // Parameters: + public ushort SID; // Search handle + public ushort SearchCount; + public FindInformationLevel InformationLevel; + public uint ResumeKey; + public FindFlags Flags; + public string FileName; // SMB_STRING + + public Transaction2FindNext2Request() : base() + { + } + + public Transaction2FindNext2Request(byte[] parameters, byte[] data, bool isUnicode) : base() + { + SID = LittleEndianConverter.ToUInt16(parameters, 0); + SearchCount = LittleEndianConverter.ToUInt16(parameters, 2); + InformationLevel = (FindInformationLevel)LittleEndianConverter.ToUInt16(parameters, 4); + ResumeKey = LittleEndianConverter.ToUInt32(parameters, 6); + Flags = (FindFlags)LittleEndianConverter.ToUInt16(parameters, 10); + FileName = SMBHelper.ReadSMBString(parameters, 12, isUnicode); + } + + public override byte[] GetSetup() + { + return LittleEndianConverter.GetBytes((ushort)SubcommandName); + } + + public override byte[] GetParameters(bool isUnicode) + { + int length = 12; + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + + byte[] parameters = new byte[length]; + LittleEndianWriter.WriteUInt16(parameters, 0, SID); + LittleEndianWriter.WriteUInt16(parameters, 2, SearchCount); + LittleEndianWriter.WriteUInt16(parameters, 4, (ushort)InformationLevel); + LittleEndianWriter.WriteUInt32(parameters, 6, ResumeKey); + LittleEndianWriter.WriteUInt16(parameters, 10, (ushort)Flags); + SMBHelper.WriteSMBString(parameters, 12, isUnicode, FileName); + + return parameters; + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_FIND_NEXT2; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindNext2Response.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindNext2Response.cs new file mode 100644 index 0000000..1c19b52 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2FindNext2Response.cs @@ -0,0 +1,66 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_FIND_NEXT2 Response + /// + public class Transaction2FindNext2Response : Transaction2Subcommand + { + public const int ParametersLength = 8; + // Parameters: + public ushort SearchCount; + public bool EndOfSearch; + public ushort EaErrorOffset; + public ushort LastNameOffset; + // Data: + public FindInformation FindInfoList; + + public Transaction2FindNext2Response() : base() + { + FindInfoList = new FindInformation(); + } + + public Transaction2FindNext2Response(byte[] parameters, byte[] data, FindInformationLevel informationLevel, bool isUnicode, bool returnResumeKeys) : base() + { + SearchCount = LittleEndianConverter.ToUInt16(parameters, 0); + EndOfSearch = LittleEndianConverter.ToUInt16(parameters, 2) != 0; + EaErrorOffset = LittleEndianConverter.ToUInt16(parameters, 4); + LastNameOffset = LittleEndianConverter.ToUInt16(parameters, 6); + + FindInfoList = new FindInformation(data, informationLevel, isUnicode, returnResumeKeys); + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(parameters, 0, SearchCount); + LittleEndianWriter.WriteUInt16(parameters, 2, Convert.ToUInt16(EndOfSearch)); + LittleEndianWriter.WriteUInt16(parameters, 4, EaErrorOffset); + LittleEndianWriter.WriteUInt16(parameters, 6, LastNameOffset); + return parameters; + } + + public override byte[] GetData(bool isUnicode) + { + return FindInfoList.GetBytes(isUnicode); + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_FIND_NEXT2; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2GetDfsReferralRequest.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2GetDfsReferralRequest.cs new file mode 100644 index 0000000..fc904f8 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2GetDfsReferralRequest.cs @@ -0,0 +1,48 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_GET_DFS_REFERRAL Request + /// + public class Transaction2GetDfsReferralRequest : Transaction2Subcommand + { + // Parameters: + RequestGetDfsReferral ReferralRequest; + + public Transaction2GetDfsReferralRequest() : base() + {} + + public Transaction2GetDfsReferralRequest(byte[] parameters, byte[] data) : base() + { + ReferralRequest = new RequestGetDfsReferral(parameters); + } + + public override byte[] GetSetup() + { + return LittleEndianConverter.GetBytes((ushort)SubcommandName); + } + + public override byte[] GetParameters(bool isUnicode) + { + return ReferralRequest.GetBytes(); + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_GET_DFS_REFERRAL; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2GetDfsReferralResponse.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2GetDfsReferralResponse.cs new file mode 100644 index 0000000..e1627c5 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2GetDfsReferralResponse.cs @@ -0,0 +1,45 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_GET_DFS_REFERRAL Response + /// We will always return STATUS_NO_SUCH_DEVICE + /// + public class Transaction2GetDfsReferralResponse : Transaction2Subcommand + { + // Data: + ResponseGetDfsReferral ReferralResponse; + + public Transaction2GetDfsReferralResponse() : base() + { + } + + public Transaction2GetDfsReferralResponse(byte[] parameters, byte[] data) : base() + { + ReferralResponse = new ResponseGetDfsReferral(data); + } + + public override byte[] GetData(bool isUnicode) + { + return ReferralResponse.GetBytes(); + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_GET_DFS_REFERRAL; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2Open2Request.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2Open2Request.cs new file mode 100644 index 0000000..391500d --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2Open2Request.cs @@ -0,0 +1,96 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_OPEN2 Request + /// + public class Transaction2Open2Request : Transaction2Subcommand + { + // Parameters: + public Open2Flags Flags; + public AccessModeOptions AccessMode; + public ushort Reserved1; + public FileAttributes FileAttributes; + public DateTime CreationTime; // UTIME (seconds since Jan 1, 1970) + public OpenMode OpenMode; + public uint AllocationSize; + public byte[] Reserved; // 10 bytes + public string FileName; // SMB_STRING + // Data: + public FullExtendedAttributeList ExtendedAttributeList; + + public Transaction2Open2Request() : base() + { + CreationTime = SMBHelper.UTimeNotSpecified; + Reserved = new byte[10]; + } + + public Transaction2Open2Request(byte[] parameters, byte[] data, bool isUnicode) : base() + { + Flags = (Open2Flags)LittleEndianConverter.ToUInt16(parameters, 0); + AccessMode = new AccessModeOptions(parameters, 2); + Reserved1 = LittleEndianConverter.ToUInt16(parameters, 4); + FileAttributes = (FileAttributes)LittleEndianConverter.ToUInt16(parameters, 6); + CreationTime = SMBHelper.ReadUTime(parameters, 8); + OpenMode = new OpenMode(parameters, 12); + AllocationSize = LittleEndianConverter.ToUInt32(parameters, 14); + Reserved = ByteReader.ReadBytes(parameters, 18, 10); + FileName = SMBHelper.ReadSMBString(parameters, 28, isUnicode); + + ExtendedAttributeList = new FullExtendedAttributeList(data, 0); + } + + public override byte[] GetSetup() + { + return LittleEndianConverter.GetBytes((ushort)SubcommandName); + } + + public override byte[] GetParameters(bool isUnicode) + { + int length = 28; + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + + byte[] parameters = new byte[length]; + LittleEndianWriter.WriteUInt16(parameters, 0, (ushort)Flags); + AccessMode.WriteBytes(parameters, 2); + LittleEndianWriter.WriteUInt16(parameters, 4, Reserved1); + LittleEndianWriter.WriteUInt16(parameters, 6, (ushort)FileAttributes); + SMBHelper.WriteUTime(parameters, 8, CreationTime); + OpenMode.WriteBytes(parameters, 12); + LittleEndianWriter.WriteUInt32(parameters, 14, AllocationSize); + ByteWriter.WriteBytes(parameters, 18, Reserved, 10); + SMBHelper.WriteSMBString(parameters, 28, isUnicode, FileName); + return parameters; + } + + public override byte[] GetData(bool isUnicode) + { + return ExtendedAttributeList.GetBytes(); + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_OPEN2; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2Open2Response.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2Open2Response.cs new file mode 100644 index 0000000..68a71b9 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2Open2Response.cs @@ -0,0 +1,78 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_OPEN2 Response + /// + public class Transaction2Open2Response : Transaction2Subcommand + { + public int ParametersLength = 30; + // Parameters + public ushort FID; + public FileAttributes FileAttributes; + public DateTime CreationTime; + public uint FileDataSize; + public AccessModeOptions AccessMode; + public ResourceType ResourceType; + public NamedPipeStatus NMPipeStatus; + public ActionTaken ActionTaken; + public uint Reserved; + public ushort ExtendedAttributeErrorOffset; + public uint ExtendedAttributeLength; + + public Transaction2Open2Response() : base() + { + CreationTime = SMBHelper.FileTimeNotSpecified; + } + + public Transaction2Open2Response(byte[] parameters, byte[] data, bool isUnicode) : base() + { + FID = LittleEndianConverter.ToUInt16(parameters, 0); + FileAttributes = (FileAttributes)LittleEndianConverter.ToUInt16(parameters, 2); + CreationTime = SMBHelper.ReadUTime(parameters, 4); + FileDataSize = LittleEndianConverter.ToUInt32(parameters, 8); + AccessMode = new AccessModeOptions(parameters, 12); + ResourceType = (ResourceType)LittleEndianConverter.ToUInt16(parameters, 14); + NMPipeStatus = new NamedPipeStatus(parameters, 16); + ActionTaken = new ActionTaken(parameters, 18); + Reserved = LittleEndianConverter.ToUInt32(parameters, 20); + ExtendedAttributeErrorOffset = LittleEndianConverter.ToUInt16(parameters, 24); + ExtendedAttributeLength = LittleEndianConverter.ToUInt32(parameters, 26); + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(parameters, 0, FID); + LittleEndianWriter.WriteUInt16(parameters, 2, (ushort)FileAttributes); + SMBHelper.WriteUTime(parameters, 4, CreationTime); + LittleEndianWriter.WriteUInt32(parameters, 8, FileDataSize); + AccessMode.WriteBytes(parameters, 12); + LittleEndianWriter.WriteUInt16(parameters, 14, (ushort)ResourceType); + NMPipeStatus.WriteBytes(parameters, 16); + ActionTaken.WriteBytes(parameters, 18); + LittleEndianWriter.WriteUInt32(parameters, 20, Reserved); + LittleEndianWriter.WriteUInt16(parameters, 24, ExtendedAttributeErrorOffset); + LittleEndianWriter.WriteUInt32(parameters, 26, ExtendedAttributeLength); + return parameters; + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_OPEN2; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFSInformationRequest.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFSInformationRequest.cs new file mode 100644 index 0000000..37394fe --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFSInformationRequest.cs @@ -0,0 +1,53 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_QUERY_FS_INFORMATION Request + /// + public class Transaction2QueryFSInformationRequest : Transaction2Subcommand + { + public int ParametersLength = 2; + + public QueryFSInformationLevel InformationLevel; + + public Transaction2QueryFSInformationRequest() : base() + { + + } + + public Transaction2QueryFSInformationRequest(byte[] parameters, byte[] data, bool isUnicode) : base() + { + InformationLevel = (QueryFSInformationLevel)LittleEndianConverter.ToUInt16(parameters, 0); + } + + public override byte[] GetSetup() + { + return LittleEndianConverter.GetBytes((ushort)SubcommandName); + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(parameters, 0, (ushort)InformationLevel); + return parameters; + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_QUERY_FS_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFSInformationResponse.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFSInformationResponse.cs new file mode 100644 index 0000000..0d328fa --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFSInformationResponse.cs @@ -0,0 +1,45 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_QUERY_FS_INFORMATION Response + /// + public class Transaction2QueryFSInformationResponse : Transaction2Subcommand + { + // Data: + public QueryFSInformation QueryFSInfo; + + public Transaction2QueryFSInformationResponse() : base() + { + + } + + public Transaction2QueryFSInformationResponse(byte[] parameters, byte[] data, QueryFSInformationLevel informationLevel, bool isUnicode) : base() + { + QueryFSInfo = QueryFSInformation.GetQueryFSInformation(data, informationLevel, isUnicode); + } + + public override byte[] GetData(bool isUnicode) + { + return QueryFSInfo.GetBytes(isUnicode); + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_QUERY_FS_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFileInformationRequest.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFileInformationRequest.cs new file mode 100644 index 0000000..3c0e820 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFileInformationRequest.cs @@ -0,0 +1,75 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_QUERY_FILE_INFORMATION Request + /// + public class Transaction2QueryFileInformationRequest : Transaction2Subcommand + { + public int ParametersLength = 4; + // Parameters: + public ushort FID; + public QueryInformationLevel InformationLevel; + // Data: + FullExtendedAttributeList GetExtendedAttributeList; // Used with QueryInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST + + public Transaction2QueryFileInformationRequest() : base() + { + GetExtendedAttributeList = new FullExtendedAttributeList(); + } + + public Transaction2QueryFileInformationRequest(byte[] parameters, byte[] data, bool isUnicode) : base() + { + FID = LittleEndianConverter.ToUInt16(parameters, 0); + InformationLevel = (QueryInformationLevel)LittleEndianConverter.ToUInt16(parameters, 2); + + if (InformationLevel == QueryInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST) + { + GetExtendedAttributeList = new FullExtendedAttributeList(data, 0); + } + } + + public override byte[] GetSetup() + { + return LittleEndianConverter.GetBytes((ushort)SubcommandName); + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(parameters, 0, FID); + LittleEndianWriter.WriteUInt16(parameters, 2, (ushort)InformationLevel); + return parameters; + } + + public override byte[] GetData(bool isUnicode) + { + if (InformationLevel == QueryInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST) + { + return GetExtendedAttributeList.GetBytes(); + } + else + { + return new byte[0]; + } + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_QUERY_FILE_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFileInformationResponse.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFileInformationResponse.cs new file mode 100644 index 0000000..de4c8e2 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryFileInformationResponse.cs @@ -0,0 +1,70 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_QUERY_FILE_INFORMATION Response + /// 0x0100) + { + EaErrorOffset = LittleEndianConverter.ToUInt16(parameters, 0); + } + QueryInfo = QueryInformation.GetQueryInformation(data, informationLevel); + } + + public override byte[] GetParameters(bool isUnicode) + { + if ((ushort)QueryInfo.InformationLevel > 0x0100) + { + byte[] parameters = new byte[2]; + LittleEndianWriter.WriteUInt16(parameters, 0, EaErrorOffset); + return parameters; + } + return new byte[0]; + } + + public override byte[] GetData(bool isUnicode) + { + if (QueryInfo == null) + { + // SMB_INFO_IS_NAME_VALID + return new byte[0]; + } + else + { + return QueryInfo.GetBytes(); + } + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_QUERY_FILE_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryPathInformationRequest.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryPathInformationRequest.cs new file mode 100644 index 0000000..988356e --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryPathInformationRequest.cs @@ -0,0 +1,86 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_QUERY_PATH_INFORMATION Request + /// + public class Transaction2QueryPathInformationRequest : Transaction2Subcommand + { + // Parameters: + public QueryInformationLevel InformationLevel; + public uint Reserved; + public string FileName; // SMB_STRING + // Data: + FullExtendedAttributeList GetExtendedAttributeList; // Used with QueryInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST + + public Transaction2QueryPathInformationRequest() : base() + { + GetExtendedAttributeList = new FullExtendedAttributeList(); + } + + public Transaction2QueryPathInformationRequest(byte[] parameters, byte[] data, bool isUnicode) : base() + { + InformationLevel = (QueryInformationLevel)LittleEndianConverter.ToUInt16(parameters, 0); + Reserved = LittleEndianConverter.ToUInt32(parameters, 4); + FileName = SMBHelper.ReadSMBString(parameters, 6, isUnicode); + + if (InformationLevel == QueryInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST) + { + GetExtendedAttributeList = new FullExtendedAttributeList(data, 0); + } + } + + public override byte[] GetSetup() + { + return LittleEndianConverter.GetBytes((ushort)SubcommandName); + } + + public override byte[] GetParameters(bool isUnicode) + { + int length = 6; + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + byte[] parameters = new byte[length]; + LittleEndianWriter.WriteUInt16(parameters, 0, (ushort)InformationLevel); + LittleEndianWriter.WriteUInt32(parameters, 2, Reserved); + SMBHelper.WriteSMBString(parameters, 6, isUnicode, FileName); + return parameters; + } + + public override byte[] GetData(bool isUnicode) + { + if (InformationLevel == QueryInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST) + { + return GetExtendedAttributeList.GetBytes(); + } + else + { + return new byte[0]; + } + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_QUERY_PATH_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryPathInformationResponse.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryPathInformationResponse.cs new file mode 100644 index 0000000..cdda7cc --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2QueryPathInformationResponse.cs @@ -0,0 +1,70 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_QUERY_PATH_INFORMATION Response + /// + public class Transaction2QueryPathInformationResponse : Transaction2Subcommand + { + // Parameters: + public ushort EaErrorOffset; + // Data: + public QueryInformation QueryInfo; + + public Transaction2QueryPathInformationResponse() : base() + { + + } + + public Transaction2QueryPathInformationResponse(byte[] parameters, byte[] data, QueryInformationLevel informationLevel, bool isUnicode) : base() + { + if ((ushort)informationLevel > 0x0100) + { + EaErrorOffset = LittleEndianConverter.ToUInt16(parameters, 0); + } + QueryInfo = QueryInformation.GetQueryInformation(data, informationLevel); + } + + public override byte[] GetParameters(bool isUnicode) + { + if ((ushort)QueryInfo.InformationLevel > 0x0100) + { + byte[] parameters = new byte[2]; + LittleEndianWriter.WriteUInt16(parameters, 0, EaErrorOffset); + return parameters; + } + return new byte[0]; + } + + public override byte[] GetData(bool isUnicode) + { + if (QueryInfo == null) + { + // SMB_INFO_IS_NAME_VALID + return new byte[0]; + } + else + { + return QueryInfo.GetBytes(); + } + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_QUERY_PATH_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetFileInformationRequest.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetFileInformationRequest.cs new file mode 100644 index 0000000..5af2ed0 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetFileInformationRequest.cs @@ -0,0 +1,67 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_SET_FILE_INFORMATION Request + /// + public class Transaction2SetFileInformationRequest : Transaction2Subcommand + { + public int ParametersLength = 6; + // Parameters: + public ushort FID; + public SetInformationLevel InformationLevel; + public ushort Reserved; + // Data: + public SetInformation SetInfo; + + public Transaction2SetFileInformationRequest() : base() + { + } + + public Transaction2SetFileInformationRequest(byte[] parameters, byte[] data, bool isUnicode) : base() + { + FID = LittleEndianConverter.ToUInt16(parameters, 0); + InformationLevel = (SetInformationLevel)LittleEndianConverter.ToUInt16(parameters, 2); + Reserved = LittleEndianConverter.ToUInt16(parameters, 4); + + SetInfo = SetInformation.GetSetInformation(data, InformationLevel); + } + + public override byte[] GetSetup() + { + return LittleEndianConverter.GetBytes((ushort)SubcommandName); + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(parameters, 0, FID); + LittleEndianWriter.WriteUInt16(parameters, 2, (ushort)InformationLevel); + LittleEndianWriter.WriteUInt16(parameters, 4, Reserved); + return parameters; + } + + public override byte[] GetData(bool isUnicode) + { + return SetInfo.GetBytes(); + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_SET_FILE_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetFileInformationResponse.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetFileInformationResponse.cs new file mode 100644 index 0000000..312bfc4 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetFileInformationResponse.cs @@ -0,0 +1,47 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_SET_FILE_INFORMATION Response + /// + public class Transaction2SetFileInformationResponse : Transaction2Subcommand + { + // Parameters: + public ushort EaErrorOffset; + + public Transaction2SetFileInformationResponse() : base() + { + + } + + public Transaction2SetFileInformationResponse(byte[] parameters, byte[] data, QueryInformationLevel informationLevel, bool isUnicode) : base() + { + EaErrorOffset = LittleEndianConverter.ToUInt16(parameters, 0); + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[2]; + LittleEndianWriter.WriteUInt16(parameters, 0, EaErrorOffset); + return parameters; + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_SET_FILE_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetPathInformationRequest.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetPathInformationRequest.cs new file mode 100644 index 0000000..deac64e --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetPathInformationRequest.cs @@ -0,0 +1,74 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_SET_PATH_INFORMATION Request + /// + public class Transaction2SetPathInformationRequest : Transaction2Subcommand + { + public const int ParametersFixedLength = 6; + // Parameters: + public SetInformationLevel InformationLevel; + public uint Reserved; + public string FileName; // SMB_STRING + // Data: + public SetInformation SetInfo; + + public Transaction2SetPathInformationRequest() : base() + {} + + public Transaction2SetPathInformationRequest(byte[] parameters, byte[] data, bool isUnicode) : base() + { + InformationLevel = (SetInformationLevel)LittleEndianConverter.ToUInt16(parameters, 0); + Reserved = LittleEndianConverter.ToUInt32(parameters, 2); + FileName = SMBHelper.ReadSMBString(parameters, 6, isUnicode); + } + + public override byte[] GetSetup() + { + return LittleEndianConverter.GetBytes((ushort)SubcommandName); + } + + public override byte[] GetParameters(bool isUnicode) + { + int length = ParametersFixedLength; + if (isUnicode) + { + length += FileName.Length * 2 + 2; + } + else + { + length += FileName.Length + 1; + } + + byte[] parameters = new byte[length]; + LittleEndianWriter.WriteUInt16(parameters, 0, (ushort)InformationLevel); + LittleEndianWriter.WriteUInt32(parameters, 2, Reserved); + SMBHelper.WriteSMBString(parameters, 6, isUnicode, FileName); + return parameters; + } + + public override byte[] GetData(bool isUnicode) + { + return SetInfo.GetBytes(); + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_SET_PATH_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetPathInformationResponse.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetPathInformationResponse.cs new file mode 100644 index 0000000..3ea5e46 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2SetPathInformationResponse.cs @@ -0,0 +1,47 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS2_SET_PATH_INFORMATION Response + /// + public class Transaction2SetPathInformationResponse : Transaction2Subcommand + { + // Parameters: + public ushort EaErrorOffset; + + public Transaction2SetPathInformationResponse() : base() + { + + } + + public Transaction2SetPathInformationResponse(byte[] parameters, byte[] data, QueryInformationLevel informationLevel, bool isUnicode) : base() + { + EaErrorOffset = LittleEndianConverter.ToUInt16(parameters, 0); + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[2]; + LittleEndianWriter.WriteUInt16(parameters, 0, EaErrorOffset); + return parameters; + } + + public override Transaction2SubcommandName SubcommandName + { + get + { + return Transaction2SubcommandName.TRANS2_SET_PATH_INFORMATION; + } + } + } +} diff --git a/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2Subcommand.cs b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2Subcommand.cs new file mode 100644 index 0000000..9eb3f13 --- /dev/null +++ b/SMBLibrary/SMB1/Transaction2Subcommands/Transaction2Subcommand.cs @@ -0,0 +1,72 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public abstract class Transaction2Subcommand + { + public Transaction2Subcommand() + { + } + + public virtual byte[] GetSetup() + { + return new byte[0]; + } + + public virtual byte[] GetParameters(bool isUnicode) + { + return new byte[0]; + } + + public virtual byte[] GetData(bool isUnicode) + { + return new byte[0]; + } + + public abstract Transaction2SubcommandName SubcommandName + { + get; + } + + public static Transaction2Subcommand GetSubcommandRequest(byte[] setup, byte[] parameters, byte[] data, bool isUnicode) + { + if (setup.Length == 2) + { + Transaction2SubcommandName subcommandName = (Transaction2SubcommandName)LittleEndianConverter.ToUInt16(setup, 0); + switch (subcommandName) + { + case Transaction2SubcommandName.TRANS2_OPEN2: + return new Transaction2Open2Request(parameters, data, isUnicode); + case Transaction2SubcommandName.TRANS2_FIND_FIRST2: + return new Transaction2FindFirst2Request(parameters, data, isUnicode); + case Transaction2SubcommandName.TRANS2_FIND_NEXT2: + return new Transaction2FindNext2Request(parameters, data, isUnicode); + case Transaction2SubcommandName.TRANS2_QUERY_FS_INFORMATION: + return new Transaction2QueryFSInformationRequest(parameters, data, isUnicode); + case Transaction2SubcommandName.TRANS2_QUERY_PATH_INFORMATION: + return new Transaction2QueryPathInformationRequest(parameters, data, isUnicode); + case Transaction2SubcommandName.TRANS2_SET_PATH_INFORMATION: + return new Transaction2SetPathInformationRequest(parameters, data, isUnicode); + case Transaction2SubcommandName.TRANS2_QUERY_FILE_INFORMATION: + return new Transaction2QueryFileInformationRequest(parameters, data, isUnicode); + case Transaction2SubcommandName.TRANS2_SET_FILE_INFORMATION: + return new Transaction2SetFileInformationRequest(parameters, data, isUnicode); + case Transaction2SubcommandName.TRANS2_CREATE_DIRECTORY: + return new Transaction2CreateDirectoryRequest(parameters, data, isUnicode); + case Transaction2SubcommandName.TRANS2_GET_DFS_REFERRAL: + return new Transaction2GetDfsReferralRequest(parameters, data); + } + } + throw new InvalidRequestException(); + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/Enums/NamedPipeState.cs b/SMBLibrary/SMB1/TransactionSubcommands/Enums/NamedPipeState.cs new file mode 100644 index 0000000..b220f45 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/Enums/NamedPipeState.cs @@ -0,0 +1,11 @@ + +namespace SMBLibrary.SMB1 +{ + public enum NamedPipeState : ushort + { + DisconnectedByServer = 0x0001, + Listening = 0x0002, + ConnectionToServerOK = 0x0003, + ServerEndClosed = 0x0004, + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/Enums/PipeState.cs b/SMBLibrary/SMB1/TransactionSubcommands/Enums/PipeState.cs new file mode 100644 index 0000000..7a32e0c --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/Enums/PipeState.cs @@ -0,0 +1,25 @@ +using System; + +namespace SMBLibrary.SMB1 +{ + [Flags] + public enum PipeState : ushort + { + /// + /// If set, the named pipe is operating in message mode. + /// If not set, the named pipe is operating in byte mode. + /// In message mode, the system treats the bytes read or written in each I/O operation to the pipe as a message unit. + /// The system MUST perform write operations on message-type pipes as if write-through mode were enabled. + /// + ReadMode = 0x0100, + + /// + /// If set, a read or a raw read request returns all data available to be read from the named pipe, up to the maximum read size set in the request. + /// A write request returns after writing data to the named pipe without waiting for the data to be consumed. + /// Named pipe non-blocking raw writes are not allowed. Raw writes MUST be performed in blocking mode. + /// If not set, a read or a raw read request will wait (block) until sufficient data to satisfy the read request becomes available, + /// or until the request is canceled. A write request blocks until its data is consumed, if the write request length is greater than zero. + /// + Nonblocking = 0x8000, + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/Enums/TransactionSubcommandName.cs b/SMBLibrary/SMB1/TransactionSubcommands/Enums/TransactionSubcommandName.cs new file mode 100644 index 0000000..d9ddfcc --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/Enums/TransactionSubcommandName.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public enum TransactionSubcommandName : ushort + { + /// + /// The 0x0001 subcommand code is interpreted as TRANS_MAILSLOT_WRITE if the operation is being performed on a mailslot. + /// The same code is interpreted as a TRANS_SET_NMPIPE_STATE (section 2.2.5.1) if the operation is performed on a named pipe. + /// + TRANS_MAILSLOT_WRITE = 0x0001, + TRANS_SET_NMPIPE_STATE = 0x0001, + TRANS_RAW_READ_NMPIPE = 0x0011, + TRANS_QUERY_NMPIPE_STATE = 0x0021, + TRANS_QUERY_NMPIPE_INFO = 0x0022, + TRANS_PEEK_NMPIPE = 0x0023, + TRANS_TRANSACT_NMPIPE = 0x0026, + TRANS_RAW_WRITE_NMPIPE = 0x0031, + TRANS_READ_NMPIPE = 0x0036, + TRANS_WRITE_NMPIPE = 0x0037, + TRANS_WAIT_NMPIPE = 0x0053, + TRANS_CALL_NMPIPE = 0x0054, + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionCallNamedPipeRequest.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionCallNamedPipeRequest.cs new file mode 100644 index 0000000..de139f5 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionCallNamedPipeRequest.cs @@ -0,0 +1,57 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_CALL_NMPIPE Request + /// + public class TransactionCallNamedPipeRequest : TransactionSubcommand + { + // Setup: + public ushort FID; + // Data: + public byte[] WriteData; + + public TransactionCallNamedPipeRequest() : base() + { + + } + + public TransactionCallNamedPipeRequest(byte[] setup, byte[] data) : base() + { + FID = LittleEndianConverter.ToUInt16(setup, 2); + + WriteData = data; + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[4]; + LittleEndianWriter.WriteUInt16(setup, 0, (ushort)this.SubcommandName); + LittleEndianWriter.WriteUInt16(setup, 2, FID); + return base.GetSetup(); + } + + public override byte[] GetData() + { + return WriteData; + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_CALL_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionCallNamedPipeResponse.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionCallNamedPipeResponse.cs new file mode 100644 index 0000000..c7fa683 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionCallNamedPipeResponse.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_CALL_NMPIPE Response + /// + public class TransactionCallNamedPipeResponse : TransactionSubcommand + { + // Data: + public byte[] ReadData; + + public TransactionCallNamedPipeResponse() : base() + {} + + public TransactionCallNamedPipeResponse(byte[] data) : base() + { + ReadData = data; + } + + public override byte[] GetData() + { + return ReadData; + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_CALL_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionPeekNamedPipeRequest.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionPeekNamedPipeRequest.cs new file mode 100644 index 0000000..577aca1 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionPeekNamedPipeRequest.cs @@ -0,0 +1,46 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_PEEK_NMPIPE Request + /// + public class TransactionPeekNamedPipeRequest : TransactionSubcommand + { + // Setup: + public ushort FID; + + public TransactionPeekNamedPipeRequest() : base() + {} + + public TransactionPeekNamedPipeRequest(byte[] setup) : base() + { + FID = LittleEndianConverter.ToUInt16(setup, 2); + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[4]; + LittleEndianWriter.WriteUInt16(setup, 0, (ushort)this.SubcommandName); + LittleEndianWriter.WriteUInt16(setup, 2, FID); + return base.GetSetup(); + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_PEEK_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionPeekNamedPipeResponse.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionPeekNamedPipeResponse.cs new file mode 100644 index 0000000..7f9a1a7 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionPeekNamedPipeResponse.cs @@ -0,0 +1,61 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_PEEK_NMPIPE Response + /// + public class TransactionPeekNamedPipeResponse : TransactionSubcommand + { + public const int ParametersLength = 6; + // Parameters: + public ushort ReadDataAvailable; + public ushort MessageBytesLength; + public NamedPipeState NamedPipeState; + // Data: + public byte[] ReadData; + + public TransactionPeekNamedPipeResponse() : base() + { } + + public TransactionPeekNamedPipeResponse(byte[] parameters, byte[] data) : base() + { + ReadDataAvailable = LittleEndianConverter.ToUInt16(parameters, 0); + MessageBytesLength = LittleEndianConverter.ToUInt16(parameters, 2); + NamedPipeState = (NamedPipeState)LittleEndianConverter.ToUInt16(parameters, 4); + + ReadData = data; + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[ParametersLength]; + LittleEndianWriter.WriteUInt16(parameters, 0, ReadDataAvailable); + LittleEndianWriter.WriteUInt16(parameters, 2, MessageBytesLength); + LittleEndianWriter.WriteUInt16(parameters, 4, (ushort)NamedPipeState); + return parameters; + } + + public override byte[] GetData() + { + return ReadData; + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_PEEK_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeInfoRequest.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeInfoRequest.cs new file mode 100644 index 0000000..26aea31 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeInfoRequest.cs @@ -0,0 +1,56 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_QUERY_NMPIPE_INFO Request + /// + public class TransactionQueryNamedPipeInfoRequest : TransactionSubcommand + { + // Setup: + public ushort FID; + // Parameters: + public ushort Level; // Must be 0x0001 + + public TransactionQueryNamedPipeInfoRequest() : base() + { + } + + public TransactionQueryNamedPipeInfoRequest(byte[] setup, byte[] parameters) : base() + { + FID = LittleEndianConverter.ToUInt16(setup, 2); + + Level = LittleEndianConverter.ToUInt16(parameters, 0); + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[4]; + LittleEndianWriter.WriteUInt16(setup, 0, (ushort)this.SubcommandName); + LittleEndianWriter.WriteUInt16(setup, 2, FID); + return base.GetSetup(); + } + + public override byte[] GetParameters(bool isUnicode) + { + return LittleEndianConverter.GetBytes(Level); + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_QUERY_NMPIPE_INFO; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeInfoResponse.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeInfoResponse.cs new file mode 100644 index 0000000..2ae6476 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeInfoResponse.cs @@ -0,0 +1,70 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_QUERY_NMPIPE_INFO Response + /// + public class TransactionQueryNamedPipeInfoResponse : TransactionSubcommand + { + // Parameters: + public ushort OutputBufferSize; + public ushort InputBufferSize; + public byte MaximumInstances; + public byte CurrentInstances; + public byte PipeNameLength; + public string PipeName; // SMB_STRING (this field WILL be aligned to start on a 2-byte boundary from the start of the SMB header) + + public TransactionQueryNamedPipeInfoResponse() : base() + {} + + public TransactionQueryNamedPipeInfoResponse(byte[] parameters, bool isUnicode) : base() + { + OutputBufferSize = LittleEndianConverter.ToUInt16(parameters, 0); + InputBufferSize = LittleEndianConverter.ToUInt16(parameters, 2); + MaximumInstances = ByteReader.ReadByte(parameters, 4); + CurrentInstances = ByteReader.ReadByte(parameters, 5); + PipeNameLength = ByteReader.ReadByte(parameters, 6); + // Note: Trans_Parameters is aligned to 4 byte boundary + PipeName = SMBHelper.ReadSMBString(parameters, 8, isUnicode); + } + + public override byte[] GetParameters(bool isUnicode) + { + int length = 8; + if (isUnicode) + { + length += PipeName.Length * 2 + 2; + } + else + { + length += PipeName.Length + 1; + } + byte[] parameters = new byte[length]; + LittleEndianWriter.WriteUInt16(parameters, 0, OutputBufferSize); + LittleEndianWriter.WriteUInt16(parameters, 2, InputBufferSize); + ByteWriter.WriteByte(parameters, 4, MaximumInstances); + ByteWriter.WriteByte(parameters, 5, CurrentInstances); + ByteWriter.WriteByte(parameters, 6, PipeNameLength); + SMBHelper.WriteSMBString(parameters, 8, isUnicode, PipeName); + return parameters; ; + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_QUERY_NMPIPE_INFO; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeStateRequest.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeStateRequest.cs new file mode 100644 index 0000000..a9dea32 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeStateRequest.cs @@ -0,0 +1,44 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_QUERY_NMPIPE_STATE Request + /// + public class TransactionQueryNamedPipeStateRequest : TransactionSubcommand + { + // Setup: + public ushort FID; + + public TransactionQueryNamedPipeStateRequest() : base() + { + } + + public TransactionQueryNamedPipeStateRequest(byte[] setup, byte[] parameters) : base() + { + FID = LittleEndianConverter.ToUInt16(setup, 2); + } + + public override byte[] GetSetup() + { + return LittleEndianConverter.GetBytes((ushort)SubcommandName); + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_QUERY_NMPIPE_STATE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeStateResponse.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeStateResponse.cs new file mode 100644 index 0000000..832d657 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionQueryNamedPipeStateResponse.cs @@ -0,0 +1,41 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_QUERY_NMPIPE_STATE Response + /// + public class TransactionQueryNamedPipeStateResponse : TransactionSubcommand + { + public NamedPipeStatus NMPipeStatus; + + public override byte[] GetSetup() + { + return new byte[0]; + } + + public override byte[] GetParameters(bool isUnicode) + { + byte[] parameters = new byte[2]; + NMPipeStatus.WriteBytes(parameters, 0); + return parameters; + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_QUERY_NMPIPE_STATE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawReadNamedPipeRequest.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawReadNamedPipeRequest.cs new file mode 100644 index 0000000..c60bbc8 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawReadNamedPipeRequest.cs @@ -0,0 +1,48 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_RAW_READ_NMPIPE Request + /// + public class TransactionRawReadNamedPipeRequest : TransactionSubcommand + { + // Setup: + public ushort FID; + + public TransactionRawReadNamedPipeRequest() + { + } + + public TransactionRawReadNamedPipeRequest(byte[] setup) + { + FID = LittleEndianConverter.ToUInt16(setup, 2); + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[4]; + LittleEndianWriter.WriteUInt16(setup, 0, (ushort)this.SubcommandName); + LittleEndianWriter.WriteUInt16(setup, 2, FID); + return base.GetSetup(); + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_RAW_READ_NMPIPE; + } + } + + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawReadNamedPipeResponse.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawReadNamedPipeResponse.cs new file mode 100644 index 0000000..661cbd2 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawReadNamedPipeResponse.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_RAW_READ_NMPIPE Request + /// + public class TransactionRawReadNamedPipeResponse : TransactionSubcommand + { + // Data: + public byte[] BytesRead; + + public TransactionRawReadNamedPipeResponse() : base() + {} + + public TransactionRawReadNamedPipeResponse(byte[] data) : base() + { + BytesRead = data; + } + + public override byte[] GetData() + { + return BytesRead; + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_RAW_READ_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawWriteNamedPipeRequest.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawWriteNamedPipeRequest.cs new file mode 100644 index 0000000..c37afab --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawWriteNamedPipeRequest.cs @@ -0,0 +1,55 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_RAW_WRITE_NMPIPE Request + /// + public class TransactionRawWriteNamedPipeRequest : TransactionSubcommand + { + // Setup: + public ushort FID; + // Data: + public byte[] WriteData; + + public TransactionRawWriteNamedPipeRequest() : base() + { + } + public TransactionRawWriteNamedPipeRequest(byte[] setup, byte[] data) : base() + { + FID = LittleEndianConverter.ToUInt16(setup, 2); + + WriteData = data; + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[4]; + LittleEndianWriter.WriteUInt16(setup, 0, (ushort)this.SubcommandName); + LittleEndianWriter.WriteUInt16(setup, 2, FID); + return base.GetSetup(); + } + + public override byte[] GetData() + { + return WriteData; + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_RAW_WRITE_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawWriteNamedPipeResponse.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawWriteNamedPipeResponse.cs new file mode 100644 index 0000000..a8d9a45 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionRawWriteNamedPipeResponse.cs @@ -0,0 +1,44 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_RAW_WRITE_NMPIPE Response + /// + public class TransactionRawWriteNamedPipeResponse : TransactionSubcommand + { + // Parameters; + public ushort BytesWritten; + + public TransactionRawWriteNamedPipeResponse() : base() + { + } + + public TransactionRawWriteNamedPipeResponse(byte[] parameters) : base() + { + BytesWritten = LittleEndianConverter.ToUInt16(parameters, 0); + } + + public override byte[] GetParameters(bool isUnicode) + { + return LittleEndianConverter.GetBytes(BytesWritten); + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_RAW_WRITE_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionReadNamedPipeRequest.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionReadNamedPipeRequest.cs new file mode 100644 index 0000000..febaecc --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionReadNamedPipeRequest.cs @@ -0,0 +1,46 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_READ_NMPIPE Request + /// + public class TransactionReadNamedPipeRequest : TransactionSubcommand + { + // Setup: + public ushort FID; + + public TransactionReadNamedPipeRequest() : base() + {} + + public TransactionReadNamedPipeRequest(byte[] setup): base() + { + FID = LittleEndianConverter.ToUInt16(setup, 2); + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[4]; + LittleEndianWriter.WriteUInt16(setup, 0, (ushort)this.SubcommandName); + LittleEndianWriter.WriteUInt16(setup, 2, FID); + return base.GetSetup(); + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_READ_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionReadNamedPipeResponse.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionReadNamedPipeResponse.cs new file mode 100644 index 0000000..53e7d46 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionReadNamedPipeResponse.cs @@ -0,0 +1,44 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_READ_NMPIPE Request + /// + public class TransactionReadNamedPipeResponse : TransactionSubcommand + { + // Data: + public byte[] ReadData; + + public TransactionReadNamedPipeResponse() : base() + { + } + + public TransactionReadNamedPipeResponse(byte[] data) : base() + { + ReadData = data; + } + + public override byte[] GetData() + { + return ReadData; + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_READ_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionSetNamedPipeStateRequest.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionSetNamedPipeStateRequest.cs new file mode 100644 index 0000000..bd87402 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionSetNamedPipeStateRequest.cs @@ -0,0 +1,56 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_SET_NMPIPE_STATE Request + /// + public class TransactionSetNamedPipeStateRequest : TransactionSubcommand + { + // Setup: + public ushort FID; + // Parameters: + public PipeState PipeState; + + public TransactionSetNamedPipeStateRequest() : base() + { + } + + public TransactionSetNamedPipeStateRequest(byte[] setup, byte[] parameters) : base() + { + FID = LittleEndianConverter.ToUInt16(setup, 2); + + PipeState = (PipeState)LittleEndianConverter.ToUInt16(parameters, 0); + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[4]; + LittleEndianWriter.WriteUInt16(setup, 0, (ushort)this.SubcommandName); + LittleEndianWriter.WriteUInt16(setup, 2, FID); + return base.GetSetup(); + } + + public override byte[] GetParameters(bool isUnicode) + { + return LittleEndianConverter.GetBytes((ushort)PipeState); + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_SET_NMPIPE_STATE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionSetNamedPipeStateResponse.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionSetNamedPipeStateResponse.cs new file mode 100644 index 0000000..6a93b93 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionSetNamedPipeStateResponse.cs @@ -0,0 +1,27 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_SET_NMPIPE_STATE Request + /// + public class TransactionSetNamedPipeStateResponse : TransactionSubcommand + { + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_SET_NMPIPE_STATE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionSubcommand.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionSubcommand.cs new file mode 100644 index 0000000..d527359 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionSubcommand.cs @@ -0,0 +1,79 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public abstract class TransactionSubcommand + { + public TransactionSubcommand() + { + } + + public virtual byte[] GetSetup() + { + return new byte[0]; + } + + public virtual byte[] GetParameters(bool isUnicode) + { + return new byte[0]; + } + + public virtual byte[] GetData() + { + return new byte[0]; + } + + public abstract TransactionSubcommandName SubcommandName + { + get; + } + + public static TransactionSubcommand GetSubcommandRequest(byte[] setup, byte[] parameters, byte[] data, bool isUnicode) + { + if (setup.Length == 4) + { + TransactionSubcommandName subcommandName = (TransactionSubcommandName)LittleEndianConverter.ToUInt16(setup, 0); + switch (subcommandName) + { + case TransactionSubcommandName.TRANS_SET_NMPIPE_STATE: + return new TransactionSetNamedPipeStateRequest(setup, parameters); + case TransactionSubcommandName.TRANS_RAW_READ_NMPIPE: + return new TransactionRawReadNamedPipeRequest(setup); + case TransactionSubcommandName.TRANS_QUERY_NMPIPE_STATE: + return new TransactionQueryNamedPipeStateRequest(setup, parameters); + case TransactionSubcommandName.TRANS_QUERY_NMPIPE_INFO: + return new TransactionQueryNamedPipeInfoRequest(setup, parameters); + case TransactionSubcommandName.TRANS_PEEK_NMPIPE: + return new TransactionPeekNamedPipeRequest(setup); + case TransactionSubcommandName.TRANS_TRANSACT_NMPIPE: + return new TransactionTransactNamedPipeRequest(setup, data); + case TransactionSubcommandName.TRANS_RAW_WRITE_NMPIPE: + return new TransactionRawWriteNamedPipeRequest(setup, data); + case TransactionSubcommandName.TRANS_READ_NMPIPE: + return new TransactionReadNamedPipeRequest(setup); + case TransactionSubcommandName.TRANS_WRITE_NMPIPE: + return new TransactionWriteNamedPipeRequest(setup, data); + case TransactionSubcommandName.TRANS_WAIT_NMPIPE: + return new TransactionWaitNamedPipeRequest(setup); + case TransactionSubcommandName.TRANS_CALL_NMPIPE: + return new TransactionCallNamedPipeRequest(setup, data); + } + } + else if (setup.Length == 0 && data.Length == 0) + { + // [MS-RAP] Remote Administration Protocol request + throw new NotImplementedException("Remote Administration Protocol request"); + } + throw new InvalidRequestException(); + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionTransactNamedPipeRequest.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionTransactNamedPipeRequest.cs new file mode 100644 index 0000000..7016978 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionTransactNamedPipeRequest.cs @@ -0,0 +1,55 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_TRANSACT_NMPIPE Request + /// + public class TransactionTransactNamedPipeRequest : TransactionSubcommand + { + // Setup: + public ushort FID; + // Data: + public byte[] WriteData; + + public TransactionTransactNamedPipeRequest() : base() + { + } + public TransactionTransactNamedPipeRequest(byte[] setup, byte[] data) : base() + { + FID = LittleEndianConverter.ToUInt16(setup, 2); + + WriteData = data; + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[4]; + LittleEndianWriter.WriteUInt16(setup, 0, (ushort)this.SubcommandName); + LittleEndianWriter.WriteUInt16(setup, 2, FID); + return base.GetSetup(); + } + + public override byte[] GetData() + { + return WriteData; + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_TRANSACT_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionTransactNamedPipeResponse.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionTransactNamedPipeResponse.cs new file mode 100644 index 0000000..17ead8e --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionTransactNamedPipeResponse.cs @@ -0,0 +1,44 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_TRANSACT_NMPIPE Request + /// + public class TransactionTransactNamedPipeResponse : TransactionSubcommand + { + // Data: + public byte[] ReadData; + + public TransactionTransactNamedPipeResponse() : base() + { + } + + public TransactionTransactNamedPipeResponse(byte[] data) : base() + { + ReadData = data; + } + + public override byte[] GetData() + { + return ReadData; + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_TRANSACT_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionWaitNamedPipeRequest.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionWaitNamedPipeRequest.cs new file mode 100644 index 0000000..9cf921b --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionWaitNamedPipeRequest.cs @@ -0,0 +1,46 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_WAIT_NMPIPE Request + /// + public class TransactionWaitNamedPipeRequest : TransactionSubcommand + { + // Setup: + public ushort FID; + public TransactionWaitNamedPipeRequest() : base() + { + } + + public TransactionWaitNamedPipeRequest(byte[] setup) : base() + { + FID = LittleEndianConverter.ToUInt16(setup, 2); + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[4]; + LittleEndianWriter.WriteUInt16(setup, 0, (ushort)this.SubcommandName); + LittleEndianWriter.WriteUInt16(setup, 2, FID); + return base.GetSetup(); + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_WAIT_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionWriteNamedPipeRequest.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionWriteNamedPipeRequest.cs new file mode 100644 index 0000000..9341432 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionWriteNamedPipeRequest.cs @@ -0,0 +1,55 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_WRITE_NMPIPE Request + /// + public class TransactionWriteNamedPipeRequest : TransactionSubcommand + { + // Setup: + public ushort FID; + // Data: + public byte[] WriteData; + + public TransactionWriteNamedPipeRequest() : base() + { + } + + public TransactionWriteNamedPipeRequest(byte[] setup, byte[] data): base() + { + FID = LittleEndianConverter.ToUInt16(setup, 2); + WriteData = data; + } + + public override byte[] GetSetup() + { + byte[] setup = new byte[4]; + LittleEndianWriter.WriteUInt16(setup, 0, (ushort)this.SubcommandName); + LittleEndianWriter.WriteUInt16(setup, 2, FID); + return base.GetSetup(); + } + + public override byte[] GetData() + { + return WriteData; + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_WRITE_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TransactionSubcommands/TransactionWriteNamedPipeResponse.cs b/SMBLibrary/SMB1/TransactionSubcommands/TransactionWriteNamedPipeResponse.cs new file mode 100644 index 0000000..78b9d99 --- /dev/null +++ b/SMBLibrary/SMB1/TransactionSubcommands/TransactionWriteNamedPipeResponse.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + /// + /// TRANS_WRITE_NMPIPE Response + /// + public class TransactionWriteNamedPipeResponse : TransactionSubcommand + { + // Parameters; + public ushort BytesWritten; + + public TransactionWriteNamedPipeResponse() : base() + {} + + public TransactionWriteNamedPipeResponse(byte[] parameters) : base() + { + BytesWritten = LittleEndianConverter.ToUInt16(parameters, 0); + } + + public override byte[] GetParameters(bool isUnicode) + { + return LittleEndianConverter.GetBytes(BytesWritten); + } + + public override TransactionSubcommandName SubcommandName + { + get + { + return TransactionSubcommandName.TRANS_WRITE_NMPIPE; + } + } + } +} diff --git a/SMBLibrary/SMB1/TreeConnectHelper.cs b/SMBLibrary/SMB1/TreeConnectHelper.cs new file mode 100644 index 0000000..7b04c9d --- /dev/null +++ b/SMBLibrary/SMB1/TreeConnectHelper.cs @@ -0,0 +1,50 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.SMB1 +{ + public class TreeConnectHelper + { + public static string GetServiceString(ServiceName serviceName) + { + switch (serviceName) + { + case ServiceName.DiskShare: + return "A:"; + case ServiceName.PrinterShare: + return "LPT1:"; + case ServiceName.NamedPipe: + return "IPC"; + case ServiceName.SerialCommunicationsDevice: + return "COMM"; + default: + return "?????"; + } + } + + public static ServiceName GetServiceName(string serviceString) + { + switch (serviceString) + { + case "A:": + return ServiceName.DiskShare; + case "LPT1:": + return ServiceName.PrinterShare; + case "IPC": + return ServiceName.NamedPipe; + case "COMM": + return ServiceName.SerialCommunicationsDevice; + default: + return ServiceName.AnyType; + } + } + } +} diff --git a/SMBLibrary/SMBLibrary.csproj b/SMBLibrary/SMBLibrary.csproj new file mode 100644 index 0000000..a39afad --- /dev/null +++ b/SMBLibrary/SMBLibrary.csproj @@ -0,0 +1,417 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {8D9E8F5D-FD13-4E4C-9723-A333DA2034A7} + Library + Properties + SMBLibrary + SMBLibrary + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {6E0F2D1E-6167-4032-BA90-DEE3A99207D0} + Utilities + + + + + \ No newline at end of file diff --git a/SMBLibrary/Server/Exceptions/EmptyPasswordNotAllowedException.cs b/SMBLibrary/Server/Exceptions/EmptyPasswordNotAllowedException.cs new file mode 100644 index 0000000..72214d3 --- /dev/null +++ b/SMBLibrary/Server/Exceptions/EmptyPasswordNotAllowedException.cs @@ -0,0 +1,23 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.Server +{ + public class EmptyPasswordNotAllowedException : Exception + { + public EmptyPasswordNotAllowedException() : base() + { + } + + public EmptyPasswordNotAllowedException(string message) : base(message) + { + } + } +} diff --git a/SMBLibrary/Server/Exceptions/InvalidRequestException.cs b/SMBLibrary/Server/Exceptions/InvalidRequestException.cs new file mode 100644 index 0000000..fe36b41 --- /dev/null +++ b/SMBLibrary/Server/Exceptions/InvalidRequestException.cs @@ -0,0 +1,23 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary +{ + public class InvalidRequestException : Exception + { + public InvalidRequestException() : base() + { + } + + public InvalidRequestException(string message) : base(message) + { + } + } +} diff --git a/SMBLibrary/Server/Exceptions/UnsupportedInformationLevelException.cs b/SMBLibrary/Server/Exceptions/UnsupportedInformationLevelException.cs new file mode 100644 index 0000000..c913329 --- /dev/null +++ b/SMBLibrary/Server/Exceptions/UnsupportedInformationLevelException.cs @@ -0,0 +1,23 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary +{ + public class UnsupportedInformationLevelException : Exception + { + public UnsupportedInformationLevelException() : base() + { + } + + public UnsupportedInformationLevelException(string message) : base(message) + { + } + } +} diff --git a/SMBLibrary/Server/FileSystemShare.cs b/SMBLibrary/Server/FileSystemShare.cs new file mode 100644 index 0000000..6b29869 --- /dev/null +++ b/SMBLibrary/Server/FileSystemShare.cs @@ -0,0 +1,48 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.Server +{ + public class FileSystemShare + { + public string Name; + public List ReadAccess; + public List WriteAccess; + public IFileSystem FileSystem; + + public bool HasReadAccess(string userName) + { + return Contains(ReadAccess, userName); + } + + public bool HasWriteAccess(string userName) + { + return Contains(WriteAccess, userName); + } + + public static bool Contains(List list, string value) + { + return (IndexOf(list, value) >= 0); + } + + public static int IndexOf(List list, string value) + { + for (int index = 0; index < list.Count; index++) + { + if (string.Equals(list[index], value, StringComparison.InvariantCultureIgnoreCase)) + { + return index; + } + } + return -1; + } + } +} diff --git a/SMBLibrary/Server/INTLMAuthenticationProvider.cs b/SMBLibrary/Server/INTLMAuthenticationProvider.cs new file mode 100644 index 0000000..c85bbf5 --- /dev/null +++ b/SMBLibrary/Server/INTLMAuthenticationProvider.cs @@ -0,0 +1,31 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.Authentication; + +namespace SMBLibrary.Server +{ + public interface INTLMAuthenticationProvider + { + // CIFS style NTLM + byte[] GenerateServerChallenge(); + User Authenticate(string accountNameToAuth, byte[] lmResponse, byte[] ntlmResponse); + + // SSPI style NTLM + byte[] GetChallengeMessageBytes(byte[] negotiateMessageBytes); + User Authenticate(byte[] authenticateMessageBytes); + + List ListUsers(); + + bool EnableGuestLogin + { + get; + } + } +} diff --git a/SMBLibrary/Server/IndependentUserCollection.cs b/SMBLibrary/Server/IndependentUserCollection.cs new file mode 100644 index 0000000..2f4525a --- /dev/null +++ b/SMBLibrary/Server/IndependentUserCollection.cs @@ -0,0 +1,196 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; +using SMBLibrary.Authentication; + +namespace SMBLibrary.Server +{ + public class IndependentUserCollection : UserCollection, INTLMAuthenticationProvider + { + private byte[] m_serverChallenge = new byte[8]; + + public IndependentUserCollection() + { + } + + public IndependentUserCollection(UserCollection users) + { + this.AddRange(users); + } + + /// + /// LM v1 / NTLM v1 + /// + public User AuthenticateV1(string accountNameToAuth, byte[] serverChallenge, byte[] lmResponse, byte[] ntlmResponse) + { + for (int index = 0; index < this.Count; index++) + { + string accountName = this[index].AccountName; + string password = this[index].Password; + + if (String.Equals(accountName, accountNameToAuth, StringComparison.InvariantCultureIgnoreCase)) + { + byte[] expectedLMResponse = NTAuthentication.ComputeLMv1Response(serverChallenge, password); + if (ByteUtils.AreByteArraysEqual(expectedLMResponse, lmResponse)) + { + return this[index]; + } + + byte[] expectedNTLMResponse = NTAuthentication.ComputeNTLMv1Response(serverChallenge, password); + if (ByteUtils.AreByteArraysEqual(expectedNTLMResponse, ntlmResponse)) + { + return this[index]; + } + } + } + return null; + } + + /// + /// LM v1 / NTLM v1 Extended Security + /// + public User AuthenticateV1Extended(string accountNameToAuth, byte[] serverChallenge, byte[] lmResponse, byte[] ntlmResponse) + { + for (int index = 0; index < this.Count; index++) + { + string accountName = this[index].AccountName; + string password = this[index].Password; + + if (String.Equals(accountName, accountNameToAuth, StringComparison.InvariantCultureIgnoreCase)) + { + byte[] clientChallenge = ByteReader.ReadBytes(lmResponse, 0, 8); + byte[] expectedNTLMv1Response = NTAuthentication.ComputeNTLMv1ExtendedSecurityResponse(serverChallenge, clientChallenge, password); + + if (ByteUtils.AreByteArraysEqual(expectedNTLMv1Response, ntlmResponse)) + { + return this[index]; + } + } + } + return null; + } + + /// + /// LM v2 / NTLM v2 + /// + public User AuthenticateV2(string domainNameToAuth, string accountNameToAuth, byte[] serverChallenge, byte[] lmResponse, byte[] ntlmResponse) + { + for (int index = 0; index < this.Count; index++) + { + string accountName = this[index].AccountName; + string password = this[index].Password; + + if (String.Equals(accountName, accountNameToAuth, StringComparison.InvariantCultureIgnoreCase)) + { + byte[] _LMv2ClientChallenge = ByteReader.ReadBytes(lmResponse, 16, 8); + byte[] expectedLMv2Response = NTAuthentication.ComputeLMv2Response(serverChallenge, _LMv2ClientChallenge, password, accountName, domainNameToAuth); + if (ByteUtils.AreByteArraysEqual(expectedLMv2Response, lmResponse)) + { + return this[index]; + } + + if (ntlmResponse.Length > 24) + { + NTLMv2ClientChallengeStructure clientChallengeStructure = new NTLMv2ClientChallengeStructure(ntlmResponse, 16); + byte[] clientChallengeStructurePadded = clientChallengeStructure.GetBytesPadded(); + byte[] expectedNTLMv2Response = NTAuthentication.ComputeNTLMv2Response(serverChallenge, clientChallengeStructurePadded, password, accountName, domainNameToAuth); + + if (ByteUtils.AreByteArraysEqual(expectedNTLMv2Response, ntlmResponse)) + { + return this[index]; + } + } + } + } + return null; + } + + public byte[] GenerateServerChallenge() + { + new Random().NextBytes(m_serverChallenge); + return m_serverChallenge; + } + + public ChallengeMessage GetChallengeMessage(byte[] negotiateMessageBytes) + { + byte[] serverChallenge = GenerateServerChallenge(); + + ChallengeMessage message = new ChallengeMessage(); + message.NegotiateFlags = NegotiateFlags.NegotiateUnicode | + NegotiateFlags.RequestTarget | + NegotiateFlags.NegotiateNTLMKey | + NegotiateFlags.NegotiateExtendedSecurity | + NegotiateFlags.NegotiateTargetInfo | + NegotiateFlags.NegotiateVersion | + NegotiateFlags.Negotiate128 | + NegotiateFlags.Negotiate56; + message.TargetName = Environment.MachineName; + message.ServerChallenge = serverChallenge; + message.TargetInfo = AVPairUtils.GetAVPairSequence(Environment.MachineName, Environment.MachineName); + message.Version = Authentication.Version.Server2003; + return message; + } + + public byte[] GetChallengeMessageBytes(byte[] negotiateMessageBytes) + { + ChallengeMessage message = GetChallengeMessage(negotiateMessageBytes); + return message.GetBytes(); + } + + public User Authenticate(byte[] authenticateMessageBytes) + { + AuthenticateMessage message = new AuthenticateMessage(authenticateMessageBytes); + return Authenticate(message); + } + + public User Authenticate(AuthenticateMessage message) + { + User user; + if ((message.NegotiateFlags & NegotiateFlags.NegotiateExtendedSecurity) > 0) + { + user = AuthenticateV1Extended(message.UserName, m_serverChallenge, message.LmChallengeResponse, message.NtChallengeResponse); + } + else + { + user = AuthenticateV1(message.UserName, m_serverChallenge, message.LmChallengeResponse, message.NtChallengeResponse); + } + + if (user == null) + { + // NTLM v2 + user = AuthenticateV2(message.DomainName, message.UserName, m_serverChallenge, message.LmChallengeResponse, message.NtChallengeResponse); + } + + return user; + } + + public User Authenticate(string accountNameToAuth, byte[] lmResponse, byte[] ntlmResponse) + { + return AuthenticateV1(accountNameToAuth, m_serverChallenge, lmResponse, ntlmResponse); + } + + public bool EnableGuestLogin + { + get + { + int index = IndexOf("Guest"); + return (index >= 0 && this[index].Password == String.Empty); + } + } + + public byte[] ServerChallenge + { + get + { + return m_serverChallenge; + } + } + } +} diff --git a/SMBLibrary/Server/NameServer.cs b/SMBLibrary/Server/NameServer.cs new file mode 100644 index 0000000..f5f5d1c --- /dev/null +++ b/SMBLibrary/Server/NameServer.cs @@ -0,0 +1,196 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using SMBLibrary.NetBios; +using Utilities; + +namespace SMBLibrary.Server +{ + /// + /// NetBIOS name service server + /// + public class NameServer + { + public const int NetBiosNameServicePort = 137; + public const string WorkgroupName = "WORKGROUP"; + + private IPAddress m_serverAddress; + private UdpClient m_client; + private bool m_listening; + + public NameServer(IPAddress serverAddress) + { + m_serverAddress = serverAddress; + } + + public void Start() + { + if (!m_listening) + { + m_listening = true; + + m_client = new UdpClient(new IPEndPoint(m_serverAddress, NetBiosNameServicePort)); + m_client.BeginReceive(ReceiveCallback, null); + + ThreadStart threadStart = new ThreadStart(RegisterNetBIOSName); + Thread thread = new Thread(threadStart); + thread.Start(); + } + } + + public void Stop() + { + m_listening = false; + m_client.Close(); + } + + + private void ReceiveCallback(IAsyncResult result) + { + if (!m_listening) + { + return; + } + + IPEndPoint remoteEP = null; + byte[] buffer; + try + { + buffer = m_client.EndReceive(result, ref remoteEP); + m_client.BeginReceive(ReceiveCallback, null); + } + catch (ObjectDisposedException) + { + return; + } + catch (SocketException) + { + return; + } + + // Process buffer + if (buffer.Length > NameServicePacketHeader.Length) + { + NameServicePacketHeader header = new NameServicePacketHeader(buffer, 0); + if (header.OpCode == NameServiceOperation.QueryRequest) + { + NameQueryRequest request = null; + try + { + request = new NameQueryRequest(buffer, 0); + } + catch + { + } + if (request != null) + { + if (request.Question.Type == NameRecordType.NB) + { + string name = NetBiosUtils.GetNameFromMSNetBiosName(request.Question.Name); + NetBiosSuffix suffix = (NetBiosSuffix)request.Question.Name[15]; + + bool nameMatch = String.Equals(name, Environment.MachineName, StringComparison.InvariantCultureIgnoreCase); + + if (nameMatch && ((suffix == NetBiosSuffix.WorkstationService) || (suffix == NetBiosSuffix.FileServiceService))) + { + PositiveNameQueryResponse response = new PositiveNameQueryResponse(); + response.Header.TransactionID = request.Header.TransactionID; + response.Resource.Name = request.Question.Name; + NameFlags nameFlags = new NameFlags(); + response.Addresses.Add(m_serverAddress.GetAddressBytes(), nameFlags); + byte[] responseBytes = response.GetBytes(); + m_client.Send(responseBytes, responseBytes.Length, remoteEP); + + } + } + else // NBStat + { + NodeStatusResponse response = new NodeStatusResponse(); + response.Header.TransactionID = request.Header.TransactionID; + response.Resource.Name = request.Question.Name; + NameFlags nameFlags = new NameFlags(); + string name1 = NetBiosUtils.GetMSNetBiosName(Environment.MachineName, NetBiosSuffix.WorkstationService); + string name2 = NetBiosUtils.GetMSNetBiosName(Environment.MachineName, NetBiosSuffix.FileServiceService); + NameFlags nameFlags3 = new NameFlags(); + nameFlags3.WorkGroup = true; + string name3 = NetBiosUtils.GetMSNetBiosName(WorkgroupName, NetBiosSuffix.WorkstationService); + response.Names.Add(name1, nameFlags); + response.Names.Add(name2, nameFlags); + response.Names.Add(name3, nameFlags3); + byte[] responseBytes = response.GetBytes(); + try + { + m_client.Send(responseBytes, responseBytes.Length, remoteEP); + } + catch (ObjectDisposedException) + { + } + } + } + } + } + + try + { + m_client.BeginReceive(ReceiveCallback, null); + } + catch (ObjectDisposedException) + { + } + catch (SocketException) + { + } + } + + private void RegisterNetBIOSName() + { + NameRegistrationRequest request1 = new NameRegistrationRequest(Environment.MachineName, NetBiosSuffix.WorkstationService, m_serverAddress); + NameRegistrationRequest request2 = new NameRegistrationRequest(Environment.MachineName, NetBiosSuffix.FileServiceService, m_serverAddress); + NameRegistrationRequest request3 = new NameRegistrationRequest(WorkgroupName, NetBiosSuffix.WorkstationService, m_serverAddress); + request3.NameFlags.WorkGroup = true; + RegisterName(request1); + RegisterName(request2); + RegisterName(request3); + } + + private IPAddress GetLocalSubnetBroadcastAddress(IPAddress address) + { + byte[] broadcastAddress = m_serverAddress.GetAddressBytes(); + broadcastAddress[3] = 0xFF; + return new IPAddress(broadcastAddress); + } + + private void RegisterName(NameRegistrationRequest request) + { + byte[] packet = request.GetBytes(); + + IPAddress broadcastAddress = GetLocalSubnetBroadcastAddress(m_serverAddress); + + IPEndPoint broadcastEP = new IPEndPoint(broadcastAddress, NetBiosNameServicePort); + for (int index = 0; index < 4; index++) + { + try + { + m_client.Send(packet, packet.Length, broadcastEP); + } + catch (ObjectDisposedException) + { + } + + if (index < 3) + { + System.Threading.Thread.Sleep(250); + } + } + } + } +} diff --git a/SMBLibrary/Server/NamedPipeShare.cs b/SMBLibrary/Server/NamedPipeShare.cs new file mode 100644 index 0000000..76f216d --- /dev/null +++ b/SMBLibrary/Server/NamedPipeShare.cs @@ -0,0 +1,35 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using SMBLibrary.Services; + +namespace SMBLibrary.Server +{ + public class NamedPipeShare : List + { + public NamedPipeShare(List shareList) + { + this.Add(new ServerService(Environment.MachineName, shareList)); + this.Add(new WorkstationService(Environment.MachineName, Environment.MachineName)); + } + + public RemoteService GetService(string path) + { + foreach (RemoteService service in this) + { + if (String.Equals(path, service.PipeName, StringComparison.InvariantCultureIgnoreCase)) + { + return service; + } + } + return null; + } + } +} diff --git a/SMBLibrary/Server/OpenedFileObject.cs b/SMBLibrary/Server/OpenedFileObject.cs new file mode 100644 index 0000000..b16758f --- /dev/null +++ b/SMBLibrary/Server/OpenedFileObject.cs @@ -0,0 +1,25 @@ +/* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace SMBLibrary.Server +{ + public class OpenedFileObject + { + public string Path; + public Stream Stream; + + public OpenedFileObject(string path, Stream stream) + { + Path = path; + Stream = stream; + } + } +} diff --git a/SMBLibrary/Server/ProcessStateObject.cs b/SMBLibrary/Server/ProcessStateObject.cs new file mode 100644 index 0000000..d60691a --- /dev/null +++ b/SMBLibrary/Server/ProcessStateObject.cs @@ -0,0 +1,23 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.Server +{ + public class ProcessStateObject + { + public ushort SubcommandID; + public byte[] TransactionSetup; + public byte[] TransactionParameters; + public byte[] TransactionData; + public int TransactionParametersReceived; // length in bytes + public int TransactionDataReceived; // length in bytes + public uint MaxDataCount; + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/FileSystemResponseHelper.cs b/SMBLibrary/Server/ResponseHelpers/FileSystemResponseHelper.cs new file mode 100644 index 0000000..0217749 --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/FileSystemResponseHelper.cs @@ -0,0 +1,265 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server +{ + public class FileSystemResponseHelper + { + internal static SMBCommand GetCreateDirectoryResponse(SMBHeader header, CreateDirectoryRequest request, FileSystemShare share, StateObject state) + { + string userName = state.GetConnectedUserName(header.UID); + if (!share.HasWriteAccess(userName)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_CREATE_DIRECTORY); + } + IFileSystem fileSystem = share.FileSystem; + + try + { + fileSystem.CreateDirectory(request.DirectoryName); + } + catch (IOException) + { + header.Status = NTStatus.STATUS_OBJECT_NAME_INVALID; + return new ErrorResponse(CommandName.SMB_COM_CREATE_DIRECTORY); + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_CREATE_DIRECTORY); + } + + return new CreateDirectoryResponse(); + } + + internal static SMBCommand GetDeleteDirectoryResponse(SMBHeader header, DeleteDirectoryRequest request, FileSystemShare share, StateObject state) + { + string userName = state.GetConnectedUserName(header.UID); + if (!share.HasWriteAccess(userName)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_DELETE_DIRECTORY); + } + IFileSystem fileSystem = share.FileSystem; + + FileSystemEntry entry = fileSystem.GetEntry(request.DirectoryName); + if (entry == null) + { + header.Status = NTStatus.STATUS_NO_SUCH_FILE; + return new ErrorResponse(CommandName.SMB_COM_DELETE_DIRECTORY); + } + + if (!entry.IsDirectory) + { + header.Status = NTStatus.STATUS_OBJECT_PATH_INVALID; + return new ErrorResponse(CommandName.SMB_COM_DELETE_DIRECTORY); + } + + try + { + fileSystem.Delete(request.DirectoryName); + return new DeleteDirectoryResponse(); + } + catch (IOException) + { + header.Status = NTStatus.STATUS_CANNOT_DELETE; + return new ErrorResponse(CommandName.SMB_COM_DELETE_DIRECTORY); + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_DELETE_DIRECTORY); + } + } + + internal static SMBCommand GetCheckDirectoryResponse(SMBHeader header, CheckDirectoryRequest request, FileSystemShare share) + { + IFileSystem fileSystem = share.FileSystem; + FileSystemEntry entry = fileSystem.GetEntry(request.DirectoryName); + if (entry == null || !entry.IsDirectory) + { + header.Status = NTStatus.STATUS_NO_SUCH_FILE; + return new ErrorResponse(CommandName.SMB_COM_CHECK_DIRECTORY); + } + + return new CheckDirectoryResponse(); + } + + internal static SMBCommand GetDeleteResponse(SMBHeader header, DeleteRequest request, FileSystemShare share, StateObject state) + { + string userName = state.GetConnectedUserName(header.UID); + if (!share.HasWriteAccess(userName)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_DELETE); + } + IFileSystem fileSystem = share.FileSystem; + + FileSystemEntry entry = fileSystem.GetEntry(request.FileName); + if (entry == null) + { + header.Status = NTStatus.STATUS_NO_SUCH_FILE; + return new ErrorResponse(CommandName.SMB_COM_DELETE); + } + + if (!entry.IsDirectory && (request.SearchAttributes & SMBLibrary.SMB1.FileAttributes.Directory) > 0 + || entry.IsDirectory && (request.SearchAttributes & SMBLibrary.SMB1.FileAttributes.Directory) == 0) + { + header.Status = NTStatus.STATUS_OBJECT_PATH_INVALID; + return new ErrorResponse(CommandName.SMB_COM_DELETE); + } + + try + { + fileSystem.Delete(request.FileName); + return new DeleteResponse(); + } + catch (IOException) + { + header.Status = NTStatus.STATUS_CANNOT_DELETE; + return new ErrorResponse(CommandName.SMB_COM_DELETE); + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_DELETE); + } + } + + internal static SMBCommand GetRenameResponse(SMBHeader header, RenameRequest request, FileSystemShare share, StateObject state) + { + string userName = state.GetConnectedUserName(header.UID); + if (!share.HasWriteAccess(userName)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_RENAME); + } + IFileSystem fileSystem = share.FileSystem; + + FileSystemEntry sourceEntry = fileSystem.GetEntry(request.OldFileName); + if (sourceEntry == null) + { + header.Status = NTStatus.STATUS_NO_SUCH_FILE; + return new ErrorResponse(CommandName.SMB_COM_RENAME); + } + + // The file must not already exist unless we just want to upcase / downcase a filename letter + FileSystemEntry destinationEntry = fileSystem.GetEntry(request.NewFileName); + if (destinationEntry != null && + !String.Equals(request.OldFileName, request.NewFileName, StringComparison.InvariantCultureIgnoreCase)) + { + // The new file already exists. + header.Status = NTStatus.STATUS_OBJECT_NAME_COLLISION; + return new ErrorResponse(CommandName.SMB_COM_RENAME); + } + + try + { + fileSystem.Move(request.OldFileName, request.NewFileName); + return new RenameResponse(); + } + catch (IOException) + { + header.Status = NTStatus.STATUS_SHARING_VIOLATION; + return new ErrorResponse(CommandName.SMB_COM_RENAME); + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_RENAME); + } + } + + internal static SMBCommand GetQueryInformationResponse(SMBHeader header, QueryInformationRequest request, FileSystemShare share) + { + IFileSystem fileSystem = share.FileSystem; + FileSystemEntry entry = fileSystem.GetEntry(request.FileName); + if (entry == null) + { + header.Status = NTStatus.STATUS_OBJECT_PATH_INVALID; + return new ErrorResponse(CommandName.SMB_COM_QUERY_INFORMATION); + } + + QueryInformationResponse response = new QueryInformationResponse(); + response.FileAttributes = InfoHelper.GetFileAttributes(entry); + response.LastWriteTime = entry.LastWriteTime; + response.FileSize = (uint)Math.Min(UInt32.MaxValue, entry.Size); + + return response; + } + + internal static SMBCommand GetSetInformationResponse(SMBHeader header, SetInformationRequest request, FileSystemShare share, StateObject state) + { + string userName = state.GetConnectedUserName(header.UID); + if (!share.HasWriteAccess(userName)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_SET_INFORMATION2); + } + IFileSystem fileSystem = share.FileSystem; + + FileSystemEntry entry = fileSystem.GetEntry(request.FileName); + if (entry == null) + { + header.Status = NTStatus.STATUS_NO_SUCH_FILE; + return new ErrorResponse(CommandName.SMB_COM_SET_INFORMATION); + } + + bool? isHidden = null; + bool? isReadOnly = null; + bool? isArchived = null; + if ((request.FileAttributes & SMBLibrary.SMB1.FileAttributes.Hidden) > 0) + { + isHidden = true; + } + if ((request.FileAttributes & SMBLibrary.SMB1.FileAttributes.ReadOnly) > 0) + { + isReadOnly = true; + } + if ((request.FileAttributes & SMBLibrary.SMB1.FileAttributes.Archive) > 0) + { + isArchived = true; + } + fileSystem.SetAttributes(request.FileName, isHidden, isReadOnly, isArchived); + + if (request.LastWriteTime != SMBHelper.UTimeNotSpecified) + { + fileSystem.SetDates(request.FileName, null, request.LastWriteTime, null); + } + + return new SetInformationResponse(); + } + + internal static SMBCommand GetSetInformation2Response(SMBHeader header, SetInformation2Request request, FileSystemShare share, StateObject state) + { + string openedFilePath = state.GetOpenedFilePath(request.FID); + if (openedFilePath == null) + { + header.Status = NTStatus.STATUS_SMB_BAD_FID; + return new ErrorResponse(CommandName.SMB_COM_SET_INFORMATION2); + } + + string userName = state.GetConnectedUserName(header.UID); + if (!share.HasWriteAccess(userName)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_SET_INFORMATION2); + } + IFileSystem fileSystem = share.FileSystem; + + fileSystem.SetDates(openedFilePath, request.CreationDateTime, request.LastWriteDateTime, request.LastAccessDateTime); + return new SetInformation2Response(); + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/IOExceptionHelper.cs b/SMBLibrary/Server/ResponseHelpers/IOExceptionHelper.cs new file mode 100644 index 0000000..a925144 --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/IOExceptionHelper.cs @@ -0,0 +1,31 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using Utilities; + +namespace SMBLibrary.Server +{ + public class IOExceptionHelper + { + public static ushort GetWin32ErrorCode(IOException ex) + { + int hResult = GetExceptionHResult(ex); + // The Win32 error code is stored in the 16 first bits of the value + return (ushort)(hResult & 0x0000FFFF); + } + + public static int GetExceptionHResult(IOException ex) + { + PropertyInfo hResult = ex.GetType().GetProperty("HResult", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + return (int)hResult.GetValue(ex, null); + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/InfoHelper.cs b/SMBLibrary/Server/ResponseHelpers/InfoHelper.cs new file mode 100644 index 0000000..ad1f0ad --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/InfoHelper.cs @@ -0,0 +1,367 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server +{ + public class InfoHelper + { + public const int BytesPerSector = 512; + public const int ClusterSize = 4096; + + internal static FindInformationEntry FromFileSystemEntry(FileSystemEntry entry, FindInformationLevel informationLevel, bool isUnicode, bool returnResumeKeys) + { + switch (informationLevel) + { + case FindInformationLevel.SMB_INFO_STANDARD: + { + FindInfoStandard result = new FindInfoStandard(returnResumeKeys); + result.CreationDateTime = entry.CreationTime; + result.LastAccessDateTime = entry.LastAccessTime; + result.LastWriteDateTime = entry.LastWriteTime; + result.FileDataSize = (uint)Math.Min(entry.Size, UInt32.MaxValue); + result.AllocationSize = (uint)Math.Min(GetAllocationSize(entry.Size), UInt32.MaxValue); + result.Attributes = GetFileAttributes(entry); + result.FileName = entry.Name; + return result; + } + case FindInformationLevel.SMB_INFO_QUERY_EA_SIZE: + { + FindInfoQueryEASize result = new FindInfoQueryEASize(returnResumeKeys); + result.CreationDateTime = entry.CreationTime; + result.LastAccessDateTime = entry.LastAccessTime; + result.LastWriteDateTime = entry.LastWriteTime; + result.FileDataSize = (uint)Math.Min(entry.Size, UInt32.MaxValue); + result.AllocationSize = (uint)Math.Min(GetAllocationSize(entry.Size), UInt32.MaxValue); + result.Attributes = GetFileAttributes(entry); + result.EASize = 0; + result.FileName = entry.Name; + return result; + } + case FindInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST: + { + FindInfoQueryExtendedAttributesFromList result = new FindInfoQueryExtendedAttributesFromList(returnResumeKeys); + result.CreationDateTime = entry.CreationTime; + result.LastAccessDateTime = entry.LastAccessTime; + result.LastWriteDateTime = entry.LastWriteTime; + result.FileDataSize = (uint)Math.Min(entry.Size, UInt32.MaxValue); + result.AllocationSize = (uint)Math.Min(GetAllocationSize(entry.Size), UInt32.MaxValue); + result.Attributes = GetFileAttributes(entry); + result.ExtendedAttributeList = new FullExtendedAttributeList(); + return result; + } + case FindInformationLevel.SMB_FIND_FILE_DIRECTORY_INFO: + { + FindFileDirectoryInfo result = new FindFileDirectoryInfo(); + result.CreationTime = entry.CreationTime; + result.LastAccessTime = entry.LastAccessTime; + result.LastWriteTime = entry.LastWriteTime; + result.LastAttrChangeTime = entry.LastWriteTime; + result.EndOfFile = entry.Size; + result.AllocationSize = GetAllocationSize(entry.Size); + result.ExtFileAttributes = GetExtendedFileAttributes(entry); + result.FileName = entry.Name; + return result; + } + case FindInformationLevel.SMB_FIND_FILE_FULL_DIRECTORY_INFO: + { + FindFileFullDirectoryInfo result = new FindFileFullDirectoryInfo(); + result.CreationTime = entry.CreationTime; + result.LastAccessTime = entry.LastAccessTime; + result.LastWriteTime = entry.LastWriteTime; + result.LastAttrChangeTime = entry.LastWriteTime; + result.EndOfFile = entry.Size; + result.AllocationSize = GetAllocationSize(entry.Size); + result.ExtFileAttributes = GetExtendedFileAttributes(entry); + result.FileName = entry.Name; + return result; + } + case FindInformationLevel.SMB_FIND_FILE_NAMES_INFO: + { + FindFileNamesInfo result = new FindFileNamesInfo(); + result.FileName = entry.Name; + return result; + } + case FindInformationLevel.SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + { + FindFileBothDirectoryInfo result = new FindFileBothDirectoryInfo(); + result.CreationTime = entry.CreationTime; + result.LastAccessTime = entry.LastAccessTime; + result.LastWriteTime = entry.LastWriteTime; + result.LastChangeTime = entry.LastWriteTime; + result.EndOfFile = entry.Size; + result.AllocationSize = GetAllocationSize(entry.Size); + result.ExtFileAttributes = GetExtendedFileAttributes(entry); + result.ShortName = GetShortName(entry.Name); + result.FileName = entry.Name; + return result; + } + default: + { + throw new UnsupportedInformationLevelException(); + } + } + } + + internal static QueryInformation FromFileSystemEntry(FileSystemEntry entry, QueryInformationLevel informationLevel) + { + switch (informationLevel) + { + case QueryInformationLevel.SMB_INFO_STANDARD: + { + QueryInfoStandard result = new QueryInfoStandard(); + result.CreationDateTime = entry.CreationTime; + result.LastAccessDateTime = entry.LastAccessTime; + result.LastWriteDateTime = entry.LastWriteTime; + result.FileDataSize = (uint)Math.Min(entry.Size, UInt32.MaxValue); + result.AllocationSize = (uint)Math.Min(GetAllocationSize(entry.Size), UInt32.MaxValue); + return result; + } + case QueryInformationLevel.SMB_INFO_QUERY_EA_SIZE: + { + QueryEASize result = new QueryEASize(); + result.CreationDateTime = entry.CreationTime; + result.LastAccessDateTime = entry.LastAccessTime; + result.LastWriteDateTime = entry.LastWriteTime; + result.FileDataSize = (uint)Math.Min(entry.Size, UInt32.MaxValue); + result.AllocationSize = (uint)Math.Min(GetAllocationSize(entry.Size), UInt32.MaxValue); + result.Attributes = GetFileAttributes(entry); + result.EASize = 0; + return result; + } + case QueryInformationLevel.SMB_INFO_QUERY_EAS_FROM_LIST: + { + throw new NotImplementedException(); + } + case QueryInformationLevel.SMB_INFO_QUERY_ALL_EAS: + { + throw new NotImplementedException(); + } + case QueryInformationLevel.SMB_INFO_IS_NAME_VALID: + { + throw new NotImplementedException(); + } + case QueryInformationLevel.SMB_QUERY_FILE_BASIC_INFO: + { + QueryFileBasicInfo result = new QueryFileBasicInfo(); + result.CreationDateTime = entry.CreationTime; + result.LastAccessDateTime = entry.LastAccessTime; + result.LastWriteDateTime = entry.LastWriteTime; + result.LastChangeTime = entry.LastWriteTime; + result.ExtFileAttributes = GetExtendedFileAttributes(entry); + return result; + } + case QueryInformationLevel.SMB_QUERY_FILE_STANDARD_INFO: + { + QueryFileStandardInfo result = new QueryFileStandardInfo(); + result.AllocationSize = GetAllocationSize(entry.Size); + result.EndOfFile = entry.Size; + result.Directory = entry.IsDirectory; + return result; + } + case QueryInformationLevel.SMB_QUERY_FILE_EA_INFO: + { + QueryFileExtendedAttributeInfo result = new QueryFileExtendedAttributeInfo(); + result.EASize = 0; + return result; + } + case QueryInformationLevel.SMB_QUERY_FILE_NAME_INFO: + { + QueryFileNameInfo result = new QueryFileNameInfo(); + result.FileName = entry.Name; + return result; + } + case QueryInformationLevel.SMB_QUERY_FILE_ALL_INFO: + { + QueryFileAllInfo result = new QueryFileAllInfo(); + result.CreationDateTime = entry.CreationTime; + result.LastAccessDateTime = entry.LastAccessTime; + result.LastWriteDateTime = entry.LastWriteTime; + result.ExtFileAttributes = GetExtendedFileAttributes(entry); + result.LastChangeTime = entry.LastWriteTime; + result.AllocationSize = GetAllocationSize(entry.Size); + result.EndOfFile = entry.Size; + result.DeletePending = false; // We delete immediately + result.Directory = entry.IsDirectory; + result.EASize = 0; + result.FileName = entry.Name; + return result; + } + case QueryInformationLevel.SMB_QUERY_FILE_ALT_NAME_INFO: + { + QueryFileAltNameInfo result = new QueryFileAltNameInfo(); + result.FileName = GetShortName(entry.Name); + return result; + } + case QueryInformationLevel.SMB_QUERY_FILE_STREAM_INFO: + { + QueryFileStreamInfo result = new QueryFileStreamInfo(); + result.StreamSize = entry.Size; + result.StreamAllocationSize = GetAllocationSize(entry.Size); + result.StreamName = "::$DATA"; + return result; + } + case QueryInformationLevel.SMB_QUERY_FILE_COMPRESSION_INFO: + { + QueryFileCompressionInfo result = new QueryFileCompressionInfo(); + result.CompressionFormat = CompressionFormat.COMPRESSION_FORMAT_NONE; + return result; + } + default: + { + throw new UnsupportedInformationLevelException(); + } + } + } + + internal static QueryFSInformation GetFSInformation(QueryFSInformationLevel informationLevel, IFileSystem fileSystem) + { + switch (informationLevel) + { + case QueryFSInformationLevel.SMB_INFO_ALLOCATION: + { + QueryFSInfoAllocation result = new QueryFSInfoAllocation(); + result.FileSystemID = 0; + result.SectorUnit = ClusterSize / BytesPerSector; + result.UnitsTotal = (uint)Math.Min(fileSystem.Size / ClusterSize, UInt32.MaxValue); + result.UnitsAvailable = (uint)Math.Min(fileSystem.FreeSpace / ClusterSize, UInt32.MaxValue); + result.Sector = BytesPerSector; + return result; + } + case QueryFSInformationLevel.SMB_INFO_VOLUME: + { + QueryFSInfoVolume result = new QueryFSInfoVolume(); + result.VolumeLabel = String.Empty; + result.VolumeSerialNumber = 0; + return result; + } + case QueryFSInformationLevel.SMB_QUERY_FS_VOLUME_INFO: + { + QueryFSVolumeInfo result = new QueryFSVolumeInfo(); + result.VolumeCreationTime = DateTime.Now; + return result; + } + case QueryFSInformationLevel.SMB_QUERY_FS_SIZE_INFO: + { + QueryFSSizeInfo result = new QueryFSSizeInfo(); + result.TotalAllocationUnits = (ulong)(fileSystem.Size / ClusterSize); + result.TotalFreeAllocationUnits = (ulong)(fileSystem.FreeSpace / ClusterSize); + result.BytesPerSector = BytesPerSector; + result.SectorsPerAllocationUnit = ClusterSize / BytesPerSector; + return result; + } + case QueryFSInformationLevel.SMB_QUERY_FS_DEVICE_INFO: + { + QueryFSDeviceInfo result = new QueryFSDeviceInfo(); + result.DeviceCharacteristics = DeviceCharacteristics.FILE_DEVICE_IS_MOUNTED; + result.DeviceType = DeviceType.FILE_DEVICE_DISK; + return result; + } + case QueryFSInformationLevel.SMB_QUERY_FS_ATTRIBUTE_INFO: + { + QueryFSAttibuteInfo result = new QueryFSAttibuteInfo(); + result.FileSystemAttributes = FileSystemAttributes.FILE_UNICODE_ON_DISK; + result.MaxFileNameLengthInBytes = 255; + result.FileSystemName = fileSystem.Name; + return result; + } + default: + { + throw new UnsupportedInformationLevelException(); + } + } + } + + /// + /// Will return a virtual allocation size, assuming 4096 bytes per cluster + /// + public static ulong GetAllocationSize(ulong size) + { + return (ulong)Math.Ceiling((double)size / ClusterSize) * ClusterSize; + } + + private static string GetShortName(string filename) + { + string fileNameWithoutExt = System.IO.Path.GetFileNameWithoutExtension(filename); + string extension = System.IO.Path.GetExtension(filename); + if (fileNameWithoutExt.Length > 8 || extension.Length > 4) + { + if (fileNameWithoutExt.Length > 8) + { + fileNameWithoutExt = fileNameWithoutExt.Substring(0, 8); + } + + if (extension.Length > 4) + { + extension = extension.Substring(0, 4); + } + + return fileNameWithoutExt + extension; + } + else + { + return filename; + } + } + + + public static FileAttributes GetFileAttributes(FileSystemEntry entry) + { + FileAttributes attributes = FileAttributes.Normal; + if (entry.IsHidden) + { + attributes |= FileAttributes.Hidden; + } + if (entry.IsReadonly) + { + attributes |= FileAttributes.ReadOnly; + } + if (entry.IsArchived) + { + attributes |= FileAttributes.Archive; + } + if (entry.IsDirectory) + { + attributes |= FileAttributes.Directory; + } + + return attributes; + } + + public static ExtendedFileAttributes GetExtendedFileAttributes(FileSystemEntry entry) + { + ExtendedFileAttributes attributes = (ExtendedFileAttributes)0; + if (entry.IsHidden) + { + attributes |= ExtendedFileAttributes.Hidden; + } + if (entry.IsReadonly) + { + attributes |= ExtendedFileAttributes.Readonly; + } + if (entry.IsArchived) + { + attributes |= ExtendedFileAttributes.Archive; + } + if (entry.IsDirectory) + { + attributes |= ExtendedFileAttributes.Directory; + } + + if (attributes == (ExtendedFileAttributes)0) + { + attributes = ExtendedFileAttributes.Normal; + } + + return attributes; + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/NTCreateHelper.cs b/SMBLibrary/Server/ResponseHelpers/NTCreateHelper.cs new file mode 100644 index 0000000..a2fecf6 --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/NTCreateHelper.cs @@ -0,0 +1,422 @@ +/* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SMBLibrary.Services; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server +{ + public class NTCreateHelper + { + internal static SMBCommand GetNTCreateResponse(SMBHeader header, NTCreateAndXRequest request, object share, StateObject state) + { + bool isExtended = (request.Flags & NTCreateFlags.NT_CREATE_REQUEST_EXTENDED_RESPONSE) > 0; + string path = request.FileName; + if (share is NamedPipeShare) + { + RemoteService service = ((NamedPipeShare)share).GetService(path); + if (service != null) + { + ushort fileID = state.AddOpenedFile(path); + if (isExtended) + { + return CreateResponseExtendedForNamedPipe(fileID); + } + else + { + return CreateResponseForNamedPipe(fileID); + } + } + + header.Status = NTStatus.STATUS_OBJECT_PATH_NOT_FOUND; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + else // FileSystemShare + { + FileSystemShare fileSystemShare = (FileSystemShare)share; + string userName = state.GetConnectedUserName(header.UID); + bool hasWriteAccess = fileSystemShare.HasWriteAccess(userName); + IFileSystem fileSystem = fileSystemShare.FileSystem; + + bool forceDirectory = (request.CreateOptions & CreateOptions.FILE_DIRECTORY_FILE) > 0; + bool forceFile = (request.CreateOptions & CreateOptions.FILE_NON_DIRECTORY_FILE) > 0; + + if (forceDirectory & (request.CreateDisposition != CreateDisposition.FILE_CREATE && + request.CreateDisposition != CreateDisposition.FILE_OPEN && + request.CreateDisposition != CreateDisposition.FILE_OPEN_IF)) + { + header.Status = NTStatus.STATUS_INVALID_PARAMETER; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + + // Windows will try to access named streams (alternate data streams) regardless of the FILE_NAMED_STREAMS flag, we need to prevent this behaviour. + if (path.Contains(":")) + { + // Windows Server 2003 will return STATUS_OBJECT_NAME_NOT_FOUND + header.Status = NTStatus.STATUS_NO_SUCH_FILE; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + + FileSystemEntry entry = fileSystem.GetEntry(path); + if (request.CreateDisposition == CreateDisposition.FILE_OPEN) + { + if (entry == null) + { + header.Status = NTStatus.STATUS_OBJECT_PATH_NOT_FOUND; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + + if (entry.IsDirectory && forceFile) + { + header.Status = NTStatus.STATUS_FILE_IS_A_DIRECTORY; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + + if (!entry.IsDirectory && forceDirectory) + { + // Not sure if that's the correct response + header.Status = NTStatus.STATUS_OBJECT_NAME_COLLISION; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + } + else if (request.CreateDisposition == CreateDisposition.FILE_CREATE) + { + if (entry != null) + { + // File already exists, fail the request + header.Status = NTStatus.STATUS_OBJECT_NAME_COLLISION; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + + if (!hasWriteAccess) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + + try + { + if (forceDirectory) + { + entry = fileSystem.CreateDirectory(path); + } + else + { + entry = fileSystem.CreateFile(path); + } + } + catch (IOException ex) + { + ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex); + if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION) + { + header.Status = NTStatus.STATUS_SHARING_VIOLATION; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + else + { + header.Status = NTStatus.STATUS_DATA_ERROR; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + } + else if (request.CreateDisposition == CreateDisposition.FILE_OPEN_IF || + request.CreateDisposition == CreateDisposition.FILE_OVERWRITE || + request.CreateDisposition == CreateDisposition.FILE_OVERWRITE_IF || + request.CreateDisposition == CreateDisposition.FILE_SUPERSEDE) + { + entry = fileSystem.GetEntry(path); + if (entry == null) + { + if (request.CreateDisposition == CreateDisposition.FILE_OVERWRITE) + { + header.Status = NTStatus.STATUS_OBJECT_PATH_NOT_FOUND; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + + if (!hasWriteAccess) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + + try + { + if (forceDirectory) + { + entry = fileSystem.CreateDirectory(path); + } + else + { + entry = fileSystem.CreateFile(path); + } + } + catch (IOException ex) + { + ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex); + if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION) + { + header.Status = NTStatus.STATUS_SHARING_VIOLATION; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + else + { + header.Status = NTStatus.STATUS_DATA_ERROR; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + } + else + { + if (request.CreateDisposition == CreateDisposition.FILE_OVERWRITE || + request.CreateDisposition == CreateDisposition.FILE_OVERWRITE_IF || + request.CreateDisposition == CreateDisposition.FILE_SUPERSEDE) + { + if (!hasWriteAccess) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + + // Truncate the file + try + { + Stream temp = fileSystem.OpenFile(path, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite); + temp.Close(); + } + catch (IOException ex) + { + ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex); + if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION) + { + header.Status = NTStatus.STATUS_SHARING_VIOLATION; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + else + { + header.Status = NTStatus.STATUS_DATA_ERROR; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + } + } + } + else + { + throw new InvalidRequestException(); + } + FileAccess fileAccess = ToFileAccess(request.DesiredAccess); + FileShare fileShare = ToFileShare(request.ShareAccess); + + if (!hasWriteAccess && (fileAccess == FileAccess.Write || fileAccess == FileAccess.ReadWrite)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX); + } + + Stream stream; + if ((request.CreateOptions & CreateOptions.FILE_OPEN_REPARSE_POINT) > 0 || fileAccess == (FileAccess)0 || entry.IsDirectory) + { + // FILE_OPEN_REPARSE_POINT is a hint that the caller does not intend to actually read the file + stream = null; + } + else + { + bool buffered = (request.CreateOptions & CreateOptions.FILE_SEQUENTIAL_ONLY) > 0 && (request.CreateOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) == 0; + System.Diagnostics.Debug.Print("[{0}] Opening {1}, Access={2}, Share={3}, Buffered={4}", DateTime.Now.ToString("HH:mm:ss:ffff"), path, fileAccess, fileShare, buffered); + stream = fileSystem.OpenFile(path, FileMode.Open, fileAccess, fileShare); + if (buffered) + { + stream = new PrefetchedStream(stream); + } + } + + ushort fileID = state.AddOpenedFile(path, stream); + if (isExtended) + { + NTCreateAndXResponseExtended response = CreateResponseExtendedFromFileSystemEntry(entry, fileID); + if ((request.Flags & NTCreateFlags.NT_CREATE_REQUEST_OPBATCH) > 0) + { + response.OpLockLevel = OpLockLevel.BatchOpLockGranted; + } + return response; + } + else + { + NTCreateAndXResponse response = CreateResponseFromFileSystemEntry(entry, fileID); + if ((request.Flags & NTCreateFlags.NT_CREATE_REQUEST_OPBATCH) > 0) + { + response.OpLockLevel = OpLockLevel.BatchOpLockGranted; + } + return response; + } + } + } + + public static FileAccess ToFileAccess(DesiredAccess desiredAccess) + { + if ((desiredAccess & DesiredAccess.GENERIC_ALL) > 0 || + ((desiredAccess & DesiredAccess.FILE_READ_DATA) > 0 && (desiredAccess & DesiredAccess.FILE_WRITE_DATA) > 0) || + ((desiredAccess & DesiredAccess.FILE_READ_DATA) > 0 && (desiredAccess & DesiredAccess.FILE_APPEND_DATA) > 0)) + { + return FileAccess.ReadWrite; + } + else if ((desiredAccess & DesiredAccess.GENERIC_WRITE) > 0 || + (desiredAccess & DesiredAccess.FILE_WRITE_DATA) > 0 || + (desiredAccess & DesiredAccess.FILE_APPEND_DATA) > 0) + { + return FileAccess.Write; + } + else if ((desiredAccess & DesiredAccess.FILE_READ_DATA) > 0) + { + return FileAccess.Read; + } + else + { + return (FileAccess)0; + } + } + + public static FileShare ToFileShare(ShareAccess shareAccess) + { + if ((shareAccess & ShareAccess.FILE_SHARE_READ) > 0 && (shareAccess & ShareAccess.FILE_SHARE_WRITE) > 0) + { + return FileShare.ReadWrite; + } + else if ((shareAccess & ShareAccess.FILE_SHARE_WRITE) > 0) + { + return FileShare.Write; + } + else if ((shareAccess & ShareAccess.FILE_SHARE_READ) > 0) + { + return FileShare.Read; + } + else if ((shareAccess & ShareAccess.FILE_SHARE_DELETE) > 0) + { + return FileShare.Delete; + } + else + { + return FileShare.None; + } + } + + private static NTCreateAndXResponse CreateResponseForNamedPipe(ushort fileID) + { + NTCreateAndXResponse response = new NTCreateAndXResponse(); + response.FID = fileID; + response.CreateDisposition = CreateDisposition.FILE_OPEN; + response.ExtFileAttributes = ExtendedFileAttributes.Normal; + response.ResourceType = ResourceType.FileTypeMessageModePipe; + response.NMPipeStatus.ICount = 255; + response.NMPipeStatus.ReadMode = ReadMode.MessageMode; + response.NMPipeStatus.NamedPipeType = NamedPipeType.MessageNodePipe; + return response; + } + + private static NTCreateAndXResponseExtended CreateResponseExtendedForNamedPipe(ushort fileID) + { + NTCreateAndXResponseExtended response = new NTCreateAndXResponseExtended(); + response.FID = fileID; + response.CreateDisposition = CreateDisposition.FILE_OPEN; + response.ExtFileAttributes = ExtendedFileAttributes.Normal; + response.ResourceType = ResourceType.FileTypeMessageModePipe; + NamedPipeStatus status = new NamedPipeStatus(); + status.ICount = 255; + status.ReadMode = ReadMode.MessageMode; + status.NamedPipeType = NamedPipeType.MessageNodePipe; + response.NMPipeStatus = status; + response.MaximalAccessRights.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | FileAccessMask.FILE_APPEND_DATA | + FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA | + FileAccessMask.FILE_EXECUTE | + FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES | + FileAccessMask.DELETE | FileAccessMask.READ_CONTROL | FileAccessMask.WRITE_DAC | FileAccessMask.WRITE_OWNER | FileAccessMask.SYNCHRONIZE; + response.GuestMaximalAccessRights.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | + FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA | + FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES | + FileAccessMask.READ_CONTROL | FileAccessMask.SYNCHRONIZE; + return response; + } + + private static NTCreateAndXResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ushort fileID) + { + NTCreateAndXResponse response = new NTCreateAndXResponse(); + if (entry.IsDirectory) + { + response.ExtFileAttributes = ExtendedFileAttributes.Directory; + response.Directory = true; + } + else + { + response.ExtFileAttributes = ExtendedFileAttributes.Normal; + } + response.FID = fileID; + response.CreateDisposition = CreateDisposition.FILE_OPEN; + response.AllocationSize = InfoHelper.GetAllocationSize(entry.Size); + response.EndOfFile = entry.Size; + response.CreateTime = entry.CreationTime; + response.LastAccessTime = entry.LastAccessTime; + response.LastWriteTime = entry.LastWriteTime; + response.LastChangeTime = entry.LastWriteTime; + response.ResourceType = ResourceType.FileTypeDisk; + return response; + } + + private static NTCreateAndXResponseExtended CreateResponseExtendedFromFileSystemEntry(FileSystemEntry entry, ushort fileID) + { + NTCreateAndXResponseExtended response = new NTCreateAndXResponseExtended(); + if (entry.IsDirectory) + { + response.ExtFileAttributes = ExtendedFileAttributes.Directory; + response.Directory = true; + } + else + { + response.ExtFileAttributes = ExtendedFileAttributes.Normal; + } + response.FID = fileID; + response.CreateTime = entry.CreationTime; + response.LastAccessTime = entry.LastAccessTime; + response.LastWriteTime = entry.LastWriteTime; + response.LastChangeTime = entry.LastWriteTime; + response.CreateDisposition = CreateDisposition.FILE_OPEN; + response.AllocationSize = InfoHelper.GetAllocationSize(entry.Size); + response.EndOfFile = entry.Size; + response.ResourceType = ResourceType.FileTypeDisk; + response.FileStatus = FileStatus.NO_EAS | FileStatus.NO_SUBSTREAMS | FileStatus.NO_REPARSETAG; + response.MaximalAccessRights.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | FileAccessMask.FILE_APPEND_DATA | + FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA | + FileAccessMask.FILE_EXECUTE | + FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES | + FileAccessMask.DELETE | FileAccessMask.READ_CONTROL | FileAccessMask.WRITE_DAC | FileAccessMask.WRITE_OWNER | FileAccessMask.SYNCHRONIZE; + response.GuestMaximalAccessRights.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | + FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA | + FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES | + FileAccessMask.READ_CONTROL | FileAccessMask.SYNCHRONIZE; + return response; + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/NTTransactHelper.cs b/SMBLibrary/Server/ResponseHelpers/NTTransactHelper.cs new file mode 100644 index 0000000..c3624d1 --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/NTTransactHelper.cs @@ -0,0 +1,151 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server +{ + public class NTTransactHelper + { + /// + /// The client MUST send as many secondary requests as are needed to complete the transfer of the transaction request. + /// + internal static SMBCommand GetNTTransactResponse(SMBHeader header, NTTransactRequest request, object share, StateObject state, List sendQueue) + { + if (request.TransParameters.Length < request.TotalParameterCount || + request.TransData.Length < request.TotalDataCount) + { + ProcessStateObject processState = state.ObtainProcessState(header.PID); + // A secondary transaction request is pending + processState.SubcommandID = (ushort)request.Function; + processState.TransactionSetup = request.Setup; + processState.TransactionParameters = new byte[request.TotalParameterCount]; + processState.TransactionData = new byte[request.TotalDataCount]; + ByteWriter.WriteBytes(processState.TransactionParameters, 0, request.TransParameters); + ByteWriter.WriteBytes(processState.TransactionData, 0, request.TransData); + processState.TransactionParametersReceived += request.TransParameters.Length; + processState.TransactionDataReceived += request.TransData.Length; + return new NTTransactInterimResponse(); + } + else + { + // We have a complete command + return GetCompleteNTTransactResponse(header, request.Function, request.Setup, request.TransParameters, request.TransData, share, state, sendQueue); + } + } + + /// + /// There are no secondary response messages. + /// The client MUST send as many secondary requests as are needed to complete the transfer of the transaction request. + /// + internal static SMBCommand GetNTTransactResponse(SMBHeader header, NTTransactSecondaryRequest request, object share, StateObject state, List sendQueue) + { + ProcessStateObject processState = state.GetProcessState(header.PID); + if (processState == null) + { + throw new InvalidRequestException(); + } + ByteWriter.WriteBytes(processState.TransactionParameters, (int)request.ParameterDisplacement, request.TransParameters); + ByteWriter.WriteBytes(processState.TransactionData, (int)request.DataDisplacement, request.TransData); + processState.TransactionParametersReceived += request.TransParameters.Length; + processState.TransactionDataReceived += request.TransData.Length; + + if (processState.TransactionParametersReceived < processState.TransactionParameters.Length || + processState.TransactionDataReceived < processState.TransactionData.Length) + { + return null; + } + else + { + // We have a complete command + return GetCompleteNTTransactResponse(header, (NTTransactSubcommandName)processState.SubcommandID, processState.TransactionSetup, processState.TransactionParameters, processState.TransactionData, share, state, sendQueue); + } + } + + internal static SMBCommand GetCompleteNTTransactResponse(SMBHeader header, NTTransactSubcommandName subcommandName, byte[] requestSetup, byte[] requestParameters, byte[] requestData, object share, StateObject state, List sendQueue) + { + NTTransactSubcommand subcommand = NTTransactSubcommand.GetSubcommandRequest(subcommandName, requestSetup, requestParameters, requestData, header.UnicodeFlag); + NTTransactSubcommand subcommandResponse = null; + + if (subcommand is NTTransactCreateRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is NTTransactIOCTLRequest) + { + subcommandResponse = GetSubcommandResponse(header, (NTTransactIOCTLRequest)subcommand); + } + else if (subcommand is NTTransactSetSecurityDescriptor) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is NTTransactNotifyChangeRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is NTTransactQuerySecurityDescriptorRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + } + + if (header.Status != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(CommandName.SMB_COM_NT_TRANSACT); + } + + byte[] responseSetup = subcommandResponse.GetSetup(); + byte[] responseParameters = subcommandResponse.GetParameters(header.UnicodeFlag); + byte[] responseData = subcommandResponse.GetData(); + NTTransactResponse response = new NTTransactResponse(); + PrepareResponse(response, responseSetup, responseParameters, responseData, state.MaxBufferSize, sendQueue); + return response; + } + + private static NTTransactIOCTLResponse GetSubcommandResponse(SMBHeader header, NTTransactIOCTLRequest subcommand) + { + const uint FSCTL_CREATE_OR_GET_OBJECT_ID = 0x900C0; + + NTTransactIOCTLResponse response = new NTTransactIOCTLResponse(); + if (subcommand.IsFsctl) + { + if (subcommand.FunctionCode == FSCTL_CREATE_OR_GET_OBJECT_ID) + { + ObjectIDBufferType1 objectID = new ObjectIDBufferType1(); + objectID.ObjectId = Guid.NewGuid(); + response.Data = objectID.GetBytes(); + return response; + } + } + + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + return null; + } + + private static void PrepareResponse(NTTransactResponse response, byte[] responseSetup, byte[] responseParameters, byte[] responseData, int maxBufferSize, List sendQueue) + { + if (NTTransactResponse.CalculateMessageSize(responseSetup.Length, responseParameters.Length, responseData.Length) <= maxBufferSize) + { + response.Setup = responseSetup; + response.TotalParameterCount = (ushort)responseParameters.Length; + response.TotalDataCount = (ushort)responseData.Length; + response.TransParameters = responseParameters; + response.TransData = responseData; + } + else + { + throw new NotImplementedException(); + } + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/NegotiateHelper.cs b/SMBLibrary/Server/ResponseHelpers/NegotiateHelper.cs new file mode 100644 index 0000000..79dbd17 --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/NegotiateHelper.cs @@ -0,0 +1,171 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.Authentication; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server +{ + /// + /// Negotiate and Session Setup helper + /// + public class NegotiateHelper + { + internal static NegotiateResponseNTLM GetNegotiateResponse(SMBHeader header, NegotiateRequest request, byte[] serverChallenge) + { + NegotiateResponseNTLM response = new NegotiateResponseNTLM(); + + response.DialectIndex = (ushort)request.Dialects.IndexOf(SMBServer.NTLanManagerDialect); + response.SecurityMode = SecurityMode.UserSecurityMode | SecurityMode.EncryptPasswords; + response.MaxMpxCount = 50; + response.MaxNumberVcs = 1; + response.MaxBufferSize = 16644; + response.MaxRawSize = 65536; + response.Capabilities = ServerCapabilities.Unicode | + ServerCapabilities.LargeFiles | + ServerCapabilities.NTSMB | + ServerCapabilities.NTStatusCode | + ServerCapabilities.NTFind | + ServerCapabilities.LargeRead | + ServerCapabilities.LargeWrite; + response.SystemTime = DateTime.UtcNow; + response.ServerTimeZone = (short)-TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).TotalMinutes; + response.Challenge = serverChallenge; + response.DomainName = String.Empty; + response.ServerName = String.Empty; + + return response; + } + + internal static NegotiateResponseNTLMExtended GetNegotiateResponseExtended(NegotiateRequest request, Guid serverGuid) + { + NegotiateResponseNTLMExtended response = new NegotiateResponseNTLMExtended(); + response.DialectIndex = (ushort)request.Dialects.IndexOf(SMBServer.NTLanManagerDialect); + response.SecurityMode = SecurityMode.UserSecurityMode | SecurityMode.EncryptPasswords; + response.MaxMpxCount = 50; + response.MaxNumberVcs = 1; + response.MaxBufferSize = 16644; + response.MaxRawSize = 65536; + response.Capabilities = ServerCapabilities.Unicode | + ServerCapabilities.LargeFiles | + ServerCapabilities.NTSMB | + ServerCapabilities.NTStatusCode | + ServerCapabilities.NTFind | + ServerCapabilities.LargeRead | + ServerCapabilities.LargeWrite | + ServerCapabilities.ExtendedSecurity; + response.SystemTime = DateTime.UtcNow; + response.ServerTimeZone = (short)-TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).TotalMinutes; + response.ServerGuid = serverGuid; + + return response; + } + + internal static SMBCommand GetSessionSetupResponse(SMBHeader header, SessionSetupAndXRequest request, INTLMAuthenticationProvider users, StateObject state) + { + SessionSetupAndXResponse response = new SessionSetupAndXResponse(); + // The PrimaryDomain field in the request is used to determine with domain controller should authenticate the user credentials, + // However, the domain controller itself does not use this field. + // See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa378749%28v=vs.85%29.aspx + User user; + try + { + user = users.Authenticate(request.AccountName, request.OEMPassword, request.UnicodePassword); + } + catch(EmptyPasswordNotAllowedException) + { + header.Status = NTStatus.STATUS_ACCOUNT_RESTRICTION; + return new ErrorResponse(CommandName.SMB_COM_SESSION_SETUP_ANDX); + } + + if (user != null) + { + response.PrimaryDomain = request.PrimaryDomain; + header.UID = state.AddConnectedUser(user.AccountName); + } + else if (users.EnableGuestLogin) + { + response.Action = SessionSetupAction.SetupGuest; + response.PrimaryDomain = request.PrimaryDomain; + header.UID = state.AddConnectedUser("Guest"); + } + else + { + header.Status = NTStatus.STATUS_LOGON_FAILURE; + return new ErrorResponse(CommandName.SMB_COM_SESSION_SETUP_ANDX); + } + if ((request.Capabilities & ServerCapabilities.LargeRead) > 0) + { + state.LargeRead = true; + } + if ((request.Capabilities & ServerCapabilities.LargeWrite) > 0) + { + state.LargeWrite = true; + } + response.NativeOS = String.Empty; // "Windows Server 2003 3790 Service Pack 2" + response.NativeLanMan = String.Empty; // "Windows Server 2003 5.2" + + return response; + } + + internal static SMBCommand GetSessionSetupResponseExtended(SMBHeader header, SessionSetupAndXRequestExtended request, INTLMAuthenticationProvider users, StateObject state) + { + SessionSetupAndXResponseExtended response = new SessionSetupAndXResponseExtended(); + + bool isValid = AuthenticationMessageUtils.IsSignatureValid(request.SecurityBlob); + if (!isValid) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + return new ErrorResponse(CommandName.SMB_COM_SESSION_SETUP_ANDX); + } + + MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(request.SecurityBlob); + if (messageType == MessageTypeName.Negotiate) + { + byte[] challengeMessageBytes = users.GetChallengeMessageBytes(request.SecurityBlob); + response.SecurityBlob = challengeMessageBytes; + header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED; + } + else // MessageTypeName.Authenticate + { + User user; + try + { + user = users.Authenticate(request.SecurityBlob); + } + catch(EmptyPasswordNotAllowedException) + { + header.Status = NTStatus.STATUS_ACCOUNT_RESTRICTION; + return new ErrorResponse(CommandName.SMB_COM_SESSION_SETUP_ANDX); + } + + + if (user != null) + { + header.UID = state.AddConnectedUser(user.AccountName); + } + else if (users.EnableGuestLogin) + { + response.Action = SessionSetupAction.SetupGuest; + header.UID = state.AddConnectedUser("Guest"); + } + else + { + header.Status = NTStatus.STATUS_LOGON_FAILURE; + return new ErrorResponse(CommandName.SMB_COM_SESSION_SETUP_ANDX); + } + } + response.NativeOS = String.Empty; // "Windows Server 2003 3790 Service Pack 2" + response.NativeLanMan = String.Empty; // "Windows Server 2003 5.2" + + return response; + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/OpenAndXHelper.cs b/SMBLibrary/Server/ResponseHelpers/OpenAndXHelper.cs new file mode 100644 index 0000000..75684fd --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/OpenAndXHelper.cs @@ -0,0 +1,250 @@ +/* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SMBLibrary.Services; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server +{ + public class OpenAndXHelper + { + internal static SMBCommand GetOpenAndXResponse(SMBHeader header, OpenAndXRequest request, object share, StateObject state) + { + bool isExtended = (request.Flags & OpenFlags.SMB_OPEN_EXTENDED_RESPONSE) > 0; + string path = request.FileName; + if (share is NamedPipeShare) + { + RemoteService service = ((NamedPipeShare)share).GetService(path); + if (service != null) + { + ushort fileID = state.AddOpenedFile(path); + if (isExtended) + { + return CreateResponseExtendedForNamedPipe(fileID); + } + else + { + return CreateResponseForNamedPipe(fileID); + } + } + + header.Status = NTStatus.STATUS_OBJECT_PATH_NOT_FOUND; + return new ErrorResponse(CommandName.SMB_COM_OPEN_ANDX); + } + else // FileSystemShare + { + FileSystemShare fileSystemShare = (FileSystemShare)share; + string userName = state.GetConnectedUserName(header.UID); + bool hasWriteAccess = fileSystemShare.HasWriteAccess(userName); + IFileSystem fileSystem = fileSystemShare.FileSystem; + + OpenResult openResult; + FileSystemEntry entry = fileSystem.GetEntry(path); + if (entry != null) + { + if (!hasWriteAccess && request.AccessMode.AccessMode == AccessMode.Write || request.AccessMode.AccessMode == AccessMode.ReadWrite) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_OPEN_ANDX); + } + + if (request.OpenMode.FileExistsOpts == FileExistsOpts.ReturnError) + { + header.Status = NTStatus.STATUS_OBJECT_NAME_COLLISION; + return new ErrorResponse(CommandName.SMB_COM_OPEN_ANDX); + } + else if (request.OpenMode.FileExistsOpts == FileExistsOpts.TruncateToZero) + { + try + { + Stream temp = fileSystem.OpenFile(path, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite); + temp.Close(); + } + catch (IOException ex) + { + ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex); + if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION) + { + header.Status = NTStatus.STATUS_SHARING_VIOLATION; + return new ErrorResponse(CommandName.SMB_COM_OPEN_ANDX); + } + else + { + header.Status = NTStatus.STATUS_DATA_ERROR; + return new ErrorResponse(CommandName.SMB_COM_OPEN_ANDX); + } + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_OPEN_ANDX); + } + openResult = OpenResult.FileExistedAndWasTruncated; + } + else // FileExistsOpts.Append + { + openResult = OpenResult.FileExistedAndWasOpened; + } + } + else + { + if (request.OpenMode.CreateFile == CreateFile.ReturnErrorIfNotExist) + { + header.Status = NTStatus.STATUS_NO_SUCH_FILE; + return new ErrorResponse(CommandName.SMB_COM_OPEN_ANDX); + } + + if ((request.FileAttrs & SMB1.FileAttributes.Directory) > 0) + { + entry = fileSystem.CreateDirectory(path); + } + else + { + entry = fileSystem.CreateFile(path); + } + openResult = OpenResult.NotExistedAndWasCreated; + } + + FileAccess fileAccess = ToFileAccess(request.AccessMode.AccessMode); + FileShare fileShare = ToFileShare(request.AccessMode.SharingMode); + Stream stream = null; + if (!entry.IsDirectory) + { + bool buffered = (request.AccessMode.CachedMode == CachedMode.CachingAllowed && request.AccessMode.WriteThroughMode == WriteThroughMode.Disabled); + System.Diagnostics.Debug.Print("[{0}] Opening {1}, Access={2}, Share={3}, Buffered={4}", DateTime.Now.ToString("HH:mm:ss:ffff"), path, fileAccess, fileShare, buffered); + stream = fileSystem.OpenFile(path, FileMode.Open, fileAccess, fileShare); + if (buffered) + { + stream = new PrefetchedStream(stream); + } + } + ushort fileID = state.AddOpenedFile(path, stream); + if (isExtended) + { + return CreateResponseExtendedFromFileSystemEntry(entry, fileID, openResult); + } + else + { + return CreateResponseFromFileSystemEntry(entry, fileID, openResult); + } + } + } + + private static FileAccess ToFileAccess(AccessMode accessMode) + { + if (accessMode == AccessMode.Write) + { + return FileAccess.Write; + } + else if (accessMode == AccessMode.ReadWrite) + { + return FileAccess.ReadWrite; + } + else + { + return FileAccess.Read; + } + } + + private static FileShare ToFileShare(SharingMode sharingMode) + { + if (sharingMode == SharingMode.DenyReadWriteExecute) + { + return FileShare.None; + } + else if (sharingMode == SharingMode.DenyWrite) + { + return FileShare.Read; + } + else if (sharingMode == SharingMode.DenyReadExecute) + { + return FileShare.Write; + } + else + { + return FileShare.ReadWrite; + } + } + + private static OpenAndXResponse CreateResponseForNamedPipe(ushort fileID) + { + OpenAndXResponse response = new OpenAndXResponse(); + response.FID = fileID; + response.AccessRights = AccessRights.SMB_DA_ACCESS_READ_WRITE; + response.ResourceType = ResourceType.FileTypeMessageModePipe; + response.NMPipeStatus.ICount = 255; + response.NMPipeStatus.ReadMode = ReadMode.MessageMode; + response.NMPipeStatus.NamedPipeType = NamedPipeType.MessageNodePipe; + return response; + } + + private static OpenAndXResponseExtended CreateResponseExtendedForNamedPipe(ushort fileID) + { + OpenAndXResponseExtended response = new OpenAndXResponseExtended(); + response.FID = fileID; + response.AccessRights = AccessRights.SMB_DA_ACCESS_READ_WRITE; + response.ResourceType = ResourceType.FileTypeMessageModePipe; + response.NMPipeStatus.ICount = 255; + response.NMPipeStatus.ReadMode = ReadMode.MessageMode; + response.NMPipeStatus.NamedPipeType = NamedPipeType.MessageNodePipe; + return response; + } + + private static OpenAndXResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ushort fileID, OpenResult openResult) + { + OpenAndXResponse response = new OpenAndXResponse(); + if (entry.IsDirectory) + { + response.FileAttrs = SMBLibrary.SMB1.FileAttributes.Directory; + } + else + { + response.FileAttrs = SMBLibrary.SMB1.FileAttributes.Normal; + } + response.FID = fileID; + response.LastWriteTime = entry.LastWriteTime; + response.FileDataSize = (uint)Math.Min(UInt32.MaxValue, entry.Size); + response.AccessRights = AccessRights.SMB_DA_ACCESS_READ; + response.ResourceType = ResourceType.FileTypeDisk; + response.OpenResults.OpenResult = openResult; + return response; + } + + private static OpenAndXResponseExtended CreateResponseExtendedFromFileSystemEntry(FileSystemEntry entry, ushort fileID, OpenResult openResult) + { + OpenAndXResponseExtended response = new OpenAndXResponseExtended(); + if (entry.IsDirectory) + { + response.FileAttrs = SMBLibrary.SMB1.FileAttributes.Directory; + } + else + { + response.FileAttrs = SMBLibrary.SMB1.FileAttributes.Normal; + } + response.FID = fileID; + response.LastWriteTime = entry.LastWriteTime; + response.FileDataSize = (uint)Math.Min(UInt32.MaxValue, entry.Size); + response.AccessRights = AccessRights.SMB_DA_ACCESS_READ; + response.ResourceType = ResourceType.FileTypeDisk; + response.OpenResults.OpenResult = openResult; + response.MaximalAccessRights.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | FileAccessMask.FILE_APPEND_DATA | + FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA | + FileAccessMask.FILE_EXECUTE | + FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES | + FileAccessMask.DELETE | FileAccessMask.READ_CONTROL | FileAccessMask.WRITE_DAC | FileAccessMask.WRITE_OWNER | FileAccessMask.SYNCHRONIZE; + response.GuestMaximalAccessRights.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | + FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA | + FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES | + FileAccessMask.READ_CONTROL | FileAccessMask.SYNCHRONIZE; + return response; + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/ReadWriteResponseHelper.cs b/SMBLibrary/Server/ResponseHelpers/ReadWriteResponseHelper.cs new file mode 100644 index 0000000..9dc5892 --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/ReadWriteResponseHelper.cs @@ -0,0 +1,241 @@ +/* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SMBLibrary.RPC; +using SMBLibrary.SMB1; +using SMBLibrary.Services; +using Utilities; + +namespace SMBLibrary.Server +{ + public class ReadWriteResponseHelper + { + internal static SMBCommand GetReadResponse(SMBHeader header, ReadRequest request, object share, StateObject state) + { + byte[] data = PerformRead(header, share, request.FID, request.ReadOffsetInBytes, request.CountOfBytesToRead, state); + if (header.Status != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(CommandName.SMB_COM_READ); + } + + ReadResponse response = new ReadResponse(); + response.Bytes = data; + response.CountOfBytesReturned = (ushort)data.Length; + return response; + } + + internal static SMBCommand GetReadResponse(SMBHeader header, ReadAndXRequest request, object share, StateObject state) + { + uint maxCount = request.MaxCount; + if ((share is FileSystemShare) && state.LargeRead) + { + maxCount = request.MaxCountLarge; + } + byte[] data = PerformRead(header, share, request.FID, request.Offset, maxCount, state); + if (header.Status != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(CommandName.SMB_COM_READ_ANDX); + } + + ReadAndXResponse response = new ReadAndXResponse(); + if (share is FileSystemShare) + { + // If the client reads from a disk file, this field MUST be set to -1 (0xFFFF) + response.Available = 0xFFFF; + } + response.Data = data; + return response; + } + + public static byte[] PerformRead(SMBHeader header, object share, ushort FID, ulong offset, uint maxCount, StateObject state) + { + if (offset > Int64.MaxValue || maxCount > Int32.MaxValue) + { + throw new NotImplementedException("Underlying filesystem does not support unsigned offset / read count"); + } + return PerformRead(header, share, FID, (long)offset, (int)maxCount, state); + } + + public static byte[] PerformRead(SMBHeader header, object share, ushort FID, long offset, int maxCount, StateObject state) + { + OpenedFileObject openedFile = state.GetOpenedFileObject(FID); + if (openedFile == null) + { + header.Status = NTStatus.STATUS_INVALID_HANDLE; + return null; + } + string openedFilePath = openedFile.Path; + + if (share is NamedPipeShare) + { + return state.RetrieveNamedPipeReply(FID); + } + else // FileSystemShare + { + FileSystemShare fileSystemShare = (FileSystemShare)share; + IFileSystem fileSystem = fileSystemShare.FileSystem; + Stream stream = openedFile.Stream; + + if (stream == null) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return null; + } + + int bytesRead; + byte[] data; + try + { + stream.Seek(offset, SeekOrigin.Begin); + data = new byte[maxCount]; + bytesRead = stream.Read(data, 0, maxCount); + } + catch (IOException ex) + { + ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex); + if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION) + { + // Returning STATUS_SHARING_VIOLATION is undocumented but apparently valid + header.Status = NTStatus.STATUS_SHARING_VIOLATION; + return null; + } + else + { + header.Status = NTStatus.STATUS_DATA_ERROR; + return null; + } + } + catch (ArgumentOutOfRangeException) + { + header.Status = NTStatus.STATUS_DATA_ERROR; + return null; + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return null; + } + + if (bytesRead < maxCount) + { + // EOF, we must trim the response data array + data = ByteReader.ReadBytes(data, 0, bytesRead); + } + return data; + } + } + + internal static SMBCommand GetWriteResponse(SMBHeader header, WriteRequest request, object share, StateObject state) + { + ushort bytesWritten = (ushort)PerformWrite(header, share, request.FID, request.WriteOffsetInBytes, request.Data, state); + if (header.Status != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(CommandName.SMB_COM_WRITE_ANDX); + } + WriteResponse response = new WriteResponse(); + response.CountOfBytesWritten = bytesWritten; + + return response; + } + + internal static SMBCommand GetWriteResponse(SMBHeader header, WriteAndXRequest request, object share, StateObject state) + { + uint bytesWritten = PerformWrite(header, share, request.FID, request.Offset, request.Data, state); + if (header.Status != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(CommandName.SMB_COM_WRITE_ANDX); + } + WriteAndXResponse response = new WriteAndXResponse(); + response.Count = bytesWritten; + + if (share is FileSystemShare) + { + // If the client wrote to a disk file, this field MUST be set to 0xFFFF. + response.Available = 0xFFFF; + } + return response; + } + + public static uint PerformWrite(SMBHeader header, object share, ushort FID, ulong offset, byte[] data, StateObject state) + { + OpenedFileObject openedFile = state.GetOpenedFileObject(FID); + if (openedFile == null) + { + header.Status = NTStatus.STATUS_INVALID_HANDLE; + return 0; + } + string openedFilePath = openedFile.Path; + + if (share is NamedPipeShare) + { + RemoteService service = ((NamedPipeShare)share).GetService(openedFilePath); + if (service != null) + { + RPCPDU rpcRequest = RPCPDU.GetPDU(data); + RPCPDU rpcReply = RemoteServiceHelper.GetRPCReply(rpcRequest, service); + byte[] replyData = rpcReply.GetBytes(); + state.StoreNamedPipeReply(FID, replyData); + return (uint)data.Length; + } + + // This code should not execute unless the SMB request (sequence) is invalid + header.Status = NTStatus.STATUS_INVALID_SMB; + return 0; + } + else // FileSystemShare + { + Stream stream = openedFile.Stream; + if (stream == null) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return 0; + } + + try + { + stream.Seek((long)offset, SeekOrigin.Begin); + stream.Write(data, 0, data.Length); + return (uint)data.Length; + } + catch (IOException ex) + { + ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex); + if (errorCode == (ushort)Win32Error.ERROR_DISK_FULL) + { + header.Status = NTStatus.STATUS_DISK_FULL; + return 0; + } + else if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION) + { + // Returning STATUS_SHARING_VIOLATION is undocumented but apparently valid + header.Status = NTStatus.STATUS_SHARING_VIOLATION; + return 0; + } + else + { + header.Status = NTStatus.STATUS_DATA_ERROR; + return 0; + } + } + catch (ArgumentOutOfRangeException) + { + header.Status = NTStatus.STATUS_DATA_ERROR; + return 0; + } + catch (UnauthorizedAccessException) + { + // The user may have tried to write to a readonly file + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return 0; + } + } + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/ServerPathUtils.cs b/SMBLibrary/Server/ResponseHelpers/ServerPathUtils.cs new file mode 100644 index 0000000..b9260f3 --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/ServerPathUtils.cs @@ -0,0 +1,55 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.Server +{ + public class ServerPathUtils + { + /// UNC path, e.g. '\\192.168.1.1\Shared' + /// e.g. \Shared + public static string GetRelativeServerPath(string path) + { + if (path.StartsWith(@"\\")) + { + int index = path.IndexOf('\\', 2); + if (index > 0) + { + return path.Substring(index); + } + else + { + return String.Empty; + } + } + return path; + } + + /// UNC path, e.g. '\\192.168.1.1\Shared\*' + /// e.g. \* + public static string GetRelativeSharePath(string path) + { + if (path.StartsWith(@"\\")) + { + int firstIndex = path.IndexOf('\\', 2); + int index = path.IndexOf('\\', firstIndex + 1); + if (index > 0) + { + return path.Substring(index); + } + else + { + return path; + } + } + return path; + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/ServerResponseHelper.cs b/SMBLibrary/Server/ResponseHelpers/ServerResponseHelper.cs new file mode 100644 index 0000000..5a57bde --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/ServerResponseHelper.cs @@ -0,0 +1,53 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server +{ + public partial class ServerResponseHelper + { + internal static SMBCommand GetCloseResponse(SMBHeader header, CloseRequest request, StateObject state) + { + string openedFilePath = state.GetOpenedFilePath(request.FID); + if (openedFilePath == null) + { + header.Status = NTStatus.STATUS_SMB_BAD_FID; + return new ErrorResponse(CommandName.SMB_COM_CLOSE); + } + + state.RemoveOpenedFile(request.FID); + CloseResponse response = new CloseResponse(); + return response; + } + + internal static SMBCommand GetFindClose2Request(SMBHeader header, FindClose2Request request, StateObject state) + { + state.ReleaseSearchHandle(request.SearchHandle); + return new FindClose2Response(); + } + + internal static EchoResponse GetEchoResponse(EchoRequest request, List sendQueue) + { + EchoResponse response = new EchoResponse(); + response.SequenceNumber = 0; + response.SMBData = request.SMBData; + for (int index = 1; index < request.EchoCount; index++) + { + EchoResponse echo = new EchoResponse(); + echo.SequenceNumber = (ushort)index; + echo.SMBData = request.SMBData; + sendQueue.Add(echo); + } + return response; + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/Transaction2SubcommandHelper.cs b/SMBLibrary/Server/ResponseHelpers/Transaction2SubcommandHelper.cs new file mode 100644 index 0000000..85ef242 --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/Transaction2SubcommandHelper.cs @@ -0,0 +1,425 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server +{ + public class Transaction2SubcommandHelper + { + // Windows servers will return "." and ".." when enumerating directory files, Windows clients do not require it. + // It seems that Ubuntu 10.04.4 and 13.10 expect at least one entry in the response (so empty directory listing cause a problem when omitting both). + public const bool IncludeCurrentDirectoryInResults = true; + public const bool IncludeParentDirectoryInResults = true; + + internal static Transaction2FindFirst2Response GetSubcommandResponse(SMBHeader header, Transaction2FindFirst2Request subcommand, FileSystemShare share, StateObject state) + { + IFileSystem fileSystem = share.FileSystem; + string path = subcommand.FileName; + // '\Directory' - Get the directory info + // '\Directory\*' - List the directory files + // '\Directory\s*' - List the directory files starting with s (cmd.exe will use this syntax when entering 's' and hitting tab for autocomplete) + // '\Directory\<.inf' (Update driver will use this syntax) + // '\Directory\exefile"*' (cmd.exe will use this syntax when entering an exe without its extension, explorer will use this opening a directory from the run menu) + bool isDirectoryEnumeration = false; + string searchPattern = String.Empty; + if (path.Contains("*") || path.Contains("<")) + { + isDirectoryEnumeration = true; + int separatorIndex = path.LastIndexOf('\\'); + searchPattern = path.Substring(separatorIndex + 1); + path = path.Substring(0, separatorIndex + 1); + } + bool exactNameWithoutExtension = searchPattern.Contains("\""); + + if (state.OpenSearches.Count > StateObject.MaxSearches) + { + header.Status = NTStatus.STATUS_OS2_NO_MORE_SIDS; + return null; + } + + FileSystemEntry entry = fileSystem.GetEntry(path); + if (entry == null) + { + header.Status = NTStatus.STATUS_NO_SUCH_FILE; + return null; + } + + List entries; + if (isDirectoryEnumeration) + { + try + { + entries = fileSystem.ListEntriesInDirectory(path); + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return null; + } + + if (searchPattern != String.Empty) + { + entries = GetFiltered(entries, searchPattern); + } + + if (!exactNameWithoutExtension) + { + if (IncludeParentDirectoryInResults) + { + entries.Insert(0, fileSystem.GetEntry(FileSystem.GetParentDirectory(path))); + entries[0].Name = ".."; + } + if (IncludeCurrentDirectoryInResults) + { + entries.Insert(0, fileSystem.GetEntry(path)); + entries[0].Name = "."; + } + } + + // If no matching entries are found, the server SHOULD fail the request with STATUS_NO_SUCH_FILE. + if (entries.Count == 0) + { + header.Status = NTStatus.STATUS_NO_SUCH_FILE; + return null; + } + } + else + { + entries = new List(); + entries.Add(entry); + } + + bool returnResumeKeys = (subcommand.Flags & FindFlags.SMB_FIND_RETURN_RESUME_KEYS) > 0; + int entriesToReturn = Math.Min(subcommand.SearchCount, entries.Count); + // We ignore SearchAttributes + Transaction2FindFirst2Response response = new Transaction2FindFirst2Response(); + for (int index = 0; index < entriesToReturn; index++) + { + FindInformationEntry infoEntry = InfoHelper.FromFileSystemEntry(entries[index], subcommand.InformationLevel, header.UnicodeFlag, returnResumeKeys); + response.FindInfoList.Add(infoEntry); + if (response.FindInfoList.GetLength(header.UnicodeFlag) > state.GetMaxDataCount(header.PID)) + { + response.FindInfoList.RemoveAt(response.FindInfoList.Count - 1); + break; + } + } + int returnCount = response.FindInfoList.Count; + response.EndOfSearch = (returnCount == entries.Count) && (entries.Count <= subcommand.SearchCount); + bool closeAtEndOfSearch = (subcommand.Flags & FindFlags.SMB_FIND_CLOSE_AT_EOS) > 0; + bool closeAfterRequest = (subcommand.Flags & FindFlags.SMB_FIND_CLOSE_AFTER_REQUEST) > 0; + // If [..] the search fit within a single response and SMB_FIND_CLOSE_AT_EOS is set in the Flags field, + // or if SMB_FIND_CLOSE_AFTER_REQUEST is set in the request, + // the server SHOULD return a SID field value of zero. + // This indicates that the search has been closed and is no longer active on the server. + if ((response.EndOfSearch && closeAtEndOfSearch) || closeAfterRequest) + { + response.SID = 0; + } + else + { + response.SID = state.AllocateSearchHandle(); + entries.RemoveRange(0, returnCount); + state.OpenSearches.Add(response.SID, entries); + } + return response; + } + + // [MS-FSA] 2.1.4.4 + // The FileName is string compared with Expression using the following wildcard rules: + // * (asterisk) Matches zero or more characters. + // ? (question mark) Matches a single character. + // DOS_DOT (" quotation mark) Matches either a period or zero characters beyond the name string. + // DOS_QM (> greater than) Matches any single character or, upon encountering a period or end of name string, advances the expression to the end of the set of contiguous DOS_QMs. + // DOS_STAR (< less than) Matches zero or more characters until encountering and matching the final . in the name. + internal static List GetFiltered(List entries, string searchPattern) + { + if (searchPattern == String.Empty || searchPattern == "*") + { + return entries; + } + + List result = new List(); + if (searchPattern.EndsWith("*") && searchPattern.Length > 1) + { + string fileNameStart = searchPattern.Substring(0, searchPattern.Length - 1); + bool exactNameWithoutExtensionMatch = false; + if (fileNameStart.EndsWith("\"")) + { + exactNameWithoutExtensionMatch = true; + fileNameStart = fileNameStart.Substring(0, fileNameStart.Length - 1); + } + + foreach (FileSystemEntry entry in entries) + { + if (!exactNameWithoutExtensionMatch) + { + if (entry.Name.StartsWith(fileNameStart, StringComparison.InvariantCultureIgnoreCase)) + { + result.Add(entry); + } + } + else + { + if (entry.Name.StartsWith(fileNameStart + ".", StringComparison.InvariantCultureIgnoreCase) || + entry.Name.Equals(fileNameStart, StringComparison.InvariantCultureIgnoreCase)) + { + result.Add(entry); + } + } + } + } + else if (searchPattern.StartsWith("<")) + { + string fileNameEnd = searchPattern.Substring(1); + foreach (FileSystemEntry entry in entries) + { + if (entry.Name.EndsWith(fileNameEnd, StringComparison.InvariantCultureIgnoreCase)) + { + result.Add(entry); + } + } + } + return result; + } + + internal static Transaction2FindNext2Response GetSubcommandResponse(SMBHeader header, Transaction2FindNext2Request subcommand, FileSystemShare share, StateObject state) + { + Transaction2FindNext2Response response = new Transaction2FindNext2Response(); + if (!state.OpenSearches.ContainsKey(subcommand.SID)) + { + header.Status = NTStatus.STATUS_INVALID_HANDLE; + return null; + } + + bool returnResumeKeys = (subcommand.Flags & FindFlags.SMB_FIND_RETURN_RESUME_KEYS) > 0; + List entries = state.OpenSearches[subcommand.SID]; + for (int index = 0; index < entries.Count; index++) + { + FindInformationEntry infoEntry = InfoHelper.FromFileSystemEntry(entries[index], subcommand.InformationLevel, header.UnicodeFlag, returnResumeKeys); + response.FindInfoList.Add(infoEntry); + if (response.FindInfoList.GetLength(header.UnicodeFlag) > state.GetMaxDataCount(header.PID)) + { + response.FindInfoList.RemoveAt(response.FindInfoList.Count - 1); + break; + } + } + int returnCount = response.FindInfoList.Count; + entries.RemoveRange(0, returnCount); + state.OpenSearches[subcommand.SID] = entries; + response.SearchCount = (ushort)returnCount; + response.EndOfSearch = (returnCount == entries.Count) && (entries.Count <= subcommand.SearchCount); + if (response.EndOfSearch) + { + state.ReleaseSearchHandle(subcommand.SID); + } + return response; + } + + internal static Transaction2QueryFSInformationResponse GetSubcommandResponse(SMBHeader header, Transaction2QueryFSInformationRequest subcommand, FileSystemShare share) + { + Transaction2QueryFSInformationResponse response = new Transaction2QueryFSInformationResponse(); + response.QueryFSInfo = InfoHelper.GetFSInformation(subcommand.InformationLevel, share.FileSystem); + return response; + } + + internal static Transaction2QueryPathInformationResponse GetSubcommandResponse(SMBHeader header, Transaction2QueryPathInformationRequest subcommand, FileSystemShare share) + { + IFileSystem fileSystem = share.FileSystem; + string path = subcommand.FileName; + FileSystemEntry entry = fileSystem.GetEntry(path); + if (entry == null) + { + // Windows Server 2003 will return STATUS_OBJECT_NAME_NOT_FOUND + // Returning STATUS_NO_SUCH_FILE caused an issue when executing ImageX.exe from WinPE 3.0 (32-bit) + header.Status = NTStatus.STATUS_OBJECT_NAME_NOT_FOUND; + return null; + } + Transaction2QueryPathInformationResponse response = new Transaction2QueryPathInformationResponse(); + response.QueryInfo = InfoHelper.FromFileSystemEntry(entry, subcommand.InformationLevel); + + return response; + } + + internal static Transaction2QueryFileInformationResponse GetSubcommandResponse(SMBHeader header, Transaction2QueryFileInformationRequest subcommand, FileSystemShare share, StateObject state) + { + IFileSystem fileSystem = share.FileSystem; + string openedFilePath = state.GetOpenedFilePath(subcommand.FID); + if (openedFilePath == null) + { + header.Status = NTStatus.STATUS_INVALID_HANDLE; + return null; + } + + FileSystemEntry entry = fileSystem.GetEntry(openedFilePath); + if (entry == null) + { + header.Status = NTStatus.STATUS_NO_SUCH_FILE; + return null; + } + Transaction2QueryFileInformationResponse response = new Transaction2QueryFileInformationResponse(); + response.QueryInfo = InfoHelper.FromFileSystemEntry(entry, subcommand.InformationLevel); + + return response; + } + + internal static Transaction2SetFileInformationResponse GetSubcommandResponse(SMBHeader header, Transaction2SetFileInformationRequest subcommand, FileSystemShare share, StateObject state) + { + string openedFilePath = state.GetOpenedFilePath(subcommand.FID); + if (openedFilePath == null) + { + header.Status = NTStatus.STATUS_INVALID_HANDLE; + return null; + } + + Transaction2SetFileInformationResponse response = new Transaction2SetFileInformationResponse(); + switch (subcommand.InformationLevel) + { + case SetInformationLevel.SMB_INFO_STANDARD: + { + return response; + } + case SetInformationLevel.SMB_INFO_SET_EAS: + { + throw new NotImplementedException(); + } + case SetInformationLevel.SMB_SET_FILE_BASIC_INFO: + { + string userName = state.GetConnectedUserName(header.UID); + if (!share.HasWriteAccess(userName)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return null; + } + + SetFileBasicInfo info = (SetFileBasicInfo)subcommand.SetInfo; + bool isHidden = (info.ExtFileAttributes & ExtendedFileAttributes.Hidden) > 0; + bool isReadonly = (info.ExtFileAttributes & ExtendedFileAttributes.Readonly) > 0; + bool isArchived = (info.ExtFileAttributes & ExtendedFileAttributes.Archive) > 0; + try + { + share.FileSystem.SetAttributes(openedFilePath, isHidden, isReadonly, isArchived); + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return null; + } + + DateTime? creationTime = null; + DateTime? lastWriteDT = null; + DateTime? lastAccessTime = null; + if (info.CreationTime != SMBHelper.FileTimeNotSpecified) + { + creationTime = info.CreationTime; + } + if (info.LastWriteTime != SMBHelper.FileTimeNotSpecified) + { + lastWriteDT = info.LastWriteTime; + } + if (info.LastAccessTime != SMBHelper.FileTimeNotSpecified) + { + lastAccessTime = info.LastAccessTime; + } + + try + { + share.FileSystem.SetDates(openedFilePath, creationTime, lastWriteDT, lastAccessTime); + } + catch (IOException ex) + { + ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex); + if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION) + { + // Returning STATUS_SHARING_VIOLATION is undocumented but apparently valid + header.Status = NTStatus.STATUS_SHARING_VIOLATION; + return null; + } + else + { + header.Status = NTStatus.STATUS_DATA_ERROR; + return null; + } + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return null; + } + return response; + } + case SetInformationLevel.SMB_SET_FILE_DISPOSITION_INFO: + { + if (((SetFileDispositionInfo)subcommand.SetInfo).DeletePending) + { + // We're supposed to delete the file on close, but it's too late to report errors at this late stage + string userName = state.GetConnectedUserName(header.UID); + if (!share.HasWriteAccess(userName)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return null; + } + + try + { + share.FileSystem.Delete(openedFilePath); + } + catch (IOException) + { + header.Status = NTStatus.STATUS_FILE_LOCK_CONFLICT; + return null; + } + catch (UnauthorizedAccessException) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return null; + } + } + return response; + } + case SetInformationLevel.SMB_SET_FILE_ALLOCATION_INFO: + { + // This subcommand is used to set the file length in bytes. + // Note: the input will NOT be a multiple of the cluster size / bytes per sector. + ulong allocationSize = ((SetFileAllocationInfo)subcommand.SetInfo).AllocationSize; + try + { + Stream stream = share.FileSystem.OpenFile(openedFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); + stream.SetLength((long)allocationSize); + stream.Close(); + } + catch + { + } + return response; + } + case SetInformationLevel.SMB_SET_FILE_END_OF_FILE_INFO: + { + ulong endOfFile = ((SetFileEndOfFileInfo)subcommand.SetInfo).EndOfFile; + try + { + Stream stream = share.FileSystem.OpenFile(openedFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); + stream.SetLength((long)endOfFile); + stream.Close(); + } + catch + { + } + return response; + } + default: + { + throw new InvalidRequestException(); + } + } + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/TransactionHelper.cs b/SMBLibrary/Server/ResponseHelpers/TransactionHelper.cs new file mode 100644 index 0000000..2844735 --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/TransactionHelper.cs @@ -0,0 +1,287 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.SMB1; +using SMBLibrary.RPC; +using SMBLibrary.Services; +using Utilities; + +namespace SMBLibrary.Server +{ + public class TransactionHelper + { + /// + /// There are no interim response messages. + /// The client MUST send as many secondary requests as are needed to complete the transfer of the transaction request. + /// The server MUST respond to the transaction request as a whole. + /// + internal static SMBCommand GetTransactionResponse(SMBHeader header, TransactionRequest request, object share, StateObject state, List sendQueue) + { + ProcessStateObject processState = state.ObtainProcessState(header.PID); + processState.MaxDataCount = request.MaxDataCount; + + if (request.TransParameters.Length < request.TotalParameterCount || + request.TransData.Length < request.TotalDataCount) + { + // A secondary transaction request is pending + processState.TransactionSetup = request.Setup; + processState.TransactionParameters = new byte[request.TotalParameterCount]; + processState.TransactionData = new byte[request.TotalDataCount]; + ByteWriter.WriteBytes(processState.TransactionParameters, 0, request.TransParameters); + ByteWriter.WriteBytes(processState.TransactionData, 0, request.TransData); + processState.TransactionParametersReceived += request.TransParameters.Length; + processState.TransactionDataReceived += request.TransData.Length; + return null; + } + else + { + // We have a complete command + if (request is Transaction2Request) + { + return GetCompleteTransaction2Response(header, request.Setup, request.TransParameters, request.TransData, share, state, sendQueue); + } + else + { + return GetCompleteTransactionResponse(header, request.Setup, request.TransParameters, request.TransData, share, state, sendQueue); + } + } + } + + /// + /// There are no secondary response messages. + /// The client MUST send as many secondary requests as are needed to complete the transfer of the transaction request. + /// The server MUST respond to the transaction request as a whole. + /// + internal static SMBCommand GetTransactionResponse(SMBHeader header, TransactionSecondaryRequest request, object share, StateObject state, List sendQueue) + { + ProcessStateObject processState = state.GetProcessState(header.PID); + if (processState == null) + { + throw new InvalidRequestException(); + } + ByteWriter.WriteBytes(processState.TransactionParameters, request.ParameterDisplacement, request.TransParameters); + ByteWriter.WriteBytes(processState.TransactionData, request.DataDisplacement, request.TransData); + processState.TransactionParametersReceived += request.TransParameters.Length; + processState.TransactionDataReceived += request.TransData.Length; + + if (processState.TransactionParametersReceived < processState.TransactionParameters.Length || + processState.TransactionDataReceived < processState.TransactionData.Length) + { + return null; + } + else + { + // We have a complete command + if (request is Transaction2SecondaryRequest) + { + return GetCompleteTransaction2Response(header, processState.TransactionSetup, processState.TransactionParameters, processState.TransactionData, share, state, sendQueue); + } + else + { + return GetCompleteTransactionResponse(header, processState.TransactionSetup, processState.TransactionParameters, processState.TransactionData, share, state, sendQueue); + } + } + } + + internal static SMBCommand GetCompleteTransactionResponse(SMBHeader header, byte[] requestSetup, byte[] requestParameters, byte[] requestData, object share, StateObject state, List sendQueue) + { + TransactionSubcommand subcommand = TransactionSubcommand.GetSubcommandRequest(requestSetup, requestParameters, requestData, header.UnicodeFlag); + TransactionSubcommand subcommandResponse = null; + + if (subcommand is TransactionSetNamedPipeStateRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is TransactionRawReadNamedPipeRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is TransactionQueryNamedPipeStateRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is TransactionQueryNamedPipeInfoRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is TransactionPeekNamedPipeRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is TransactionTransactNamedPipeRequest) + { + if (!(share is NamedPipeShare)) + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + return new ErrorResponse(CommandName.SMB_COM_TRANSACTION); + } + + subcommandResponse = TransactionSubcommandHelper.GetSubcommandResponse(header, (TransactionTransactNamedPipeRequest)subcommand, (NamedPipeShare)share, state); + } + else if (subcommand is TransactionRawWriteNamedPipeRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is TransactionReadNamedPipeRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is TransactionWriteNamedPipeRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is TransactionWaitNamedPipeRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is TransactionCallNamedPipeRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + } + + if (header.Status != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(CommandName.SMB_COM_TRANSACTION); + } + + byte[] responseSetup = subcommandResponse.GetSetup(); + byte[] responseParameters = subcommandResponse.GetParameters(header.UnicodeFlag); + byte[] responseData = subcommandResponse.GetData(); + TransactionResponse response = new TransactionResponse(); + PrepareResponse(response, responseSetup, responseParameters, responseData, state.MaxBufferSize, sendQueue); + return response; + } + + internal static SMBCommand GetCompleteTransaction2Response(SMBHeader header, byte[] requestSetup, byte[] requestParameters, byte[] requestData, object share, StateObject state, List sendQueue) + { + Transaction2Subcommand subcommand = Transaction2Subcommand.GetSubcommandRequest(requestSetup, requestParameters, requestData, header.UnicodeFlag); + Transaction2Subcommand subcommandResponse = null; + + if (!(share is FileSystemShare)) + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + return new ErrorResponse(CommandName.SMB_COM_TRANSACTION2); + } + + FileSystemShare fileSystemShare = (FileSystemShare)share; + + if (subcommand is Transaction2FindFirst2Request) + { + subcommandResponse = Transaction2SubcommandHelper.GetSubcommandResponse(header, (Transaction2FindFirst2Request)subcommand, fileSystemShare, state); + } + else if (subcommand is Transaction2FindNext2Request) + { + subcommandResponse = Transaction2SubcommandHelper.GetSubcommandResponse(header, (Transaction2FindNext2Request)subcommand, fileSystemShare, state); + } + else if (subcommand is Transaction2QueryFSInformationRequest) + { + subcommandResponse = Transaction2SubcommandHelper.GetSubcommandResponse(header, (Transaction2QueryFSInformationRequest)subcommand, fileSystemShare); + } + else if (subcommand is Transaction2QueryPathInformationRequest) + { + subcommandResponse = Transaction2SubcommandHelper.GetSubcommandResponse(header, (Transaction2QueryPathInformationRequest)subcommand, fileSystemShare); + } + else if (subcommand is Transaction2SetPathInformationRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is Transaction2QueryFileInformationRequest) + { + subcommandResponse = Transaction2SubcommandHelper.GetSubcommandResponse(header, (Transaction2QueryFileInformationRequest)subcommand, fileSystemShare, state); + } + else if (subcommand is Transaction2SetFileInformationRequest) + { + subcommandResponse = Transaction2SubcommandHelper.GetSubcommandResponse(header, (Transaction2SetFileInformationRequest)subcommand, fileSystemShare, state); + } + else if (subcommand is Transaction2CreateDirectoryRequest) + { + header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + } + else if (subcommand is Transaction2GetDfsReferralRequest) + { + header.Status = NTStatus.STATUS_NO_SUCH_DEVICE; + } + else + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + } + + if (header.Status != NTStatus.STATUS_SUCCESS) + { + return new ErrorResponse(CommandName.SMB_COM_TRANSACTION2); + } + + byte[] responseSetup = subcommandResponse.GetSetup(); + byte[] responseParameters = subcommandResponse.GetParameters(header.UnicodeFlag); + byte[] responseData = subcommandResponse.GetData(header.UnicodeFlag); + Transaction2Response response = new Transaction2Response(); + PrepareResponse(response, responseSetup, responseParameters, responseData, state.MaxBufferSize, sendQueue); + return response; + } + + internal static void PrepareResponse(TransactionResponse response, byte[] responseSetup, byte[] responseParameters, byte[] responseData, int maxBufferSize, List sendQueue) + { + int responseSize = TransactionResponse.CalculateMessageSize(responseSetup.Length, responseParameters.Length, responseData.Length); + if (responseSize <= maxBufferSize) + { + response.Setup = responseSetup; + response.TotalParameterCount = (ushort)responseParameters.Length; + response.TotalDataCount = (ushort)responseData.Length; + response.TransParameters = responseParameters; + response.TransData = responseData; + } + else + { + int currentDataLength = maxBufferSize - (responseSize - responseData.Length); + byte[] buffer = new byte[currentDataLength]; + Array.Copy(responseData, 0, buffer, 0, currentDataLength); + response.Setup = responseSetup; + response.TotalParameterCount = (ushort)responseParameters.Length; + response.TotalDataCount = (ushort)responseData.Length; + response.TransParameters = responseParameters; + response.TransData = buffer; + + int dataBytesLeftToSend = responseData.Length - currentDataLength; + while (dataBytesLeftToSend > 0) + { + TransactionResponse additionalResponse; + if (response is Transaction2Response) + { + additionalResponse = new Transaction2Response(); + } + else + { + additionalResponse = new TransactionResponse(); + } + currentDataLength = dataBytesLeftToSend; + responseSize = TransactionResponse.CalculateMessageSize(0, 0, dataBytesLeftToSend); + if (responseSize > maxBufferSize) + { + currentDataLength = maxBufferSize - (responseSize - dataBytesLeftToSend); + } + buffer = new byte[currentDataLength]; + int dataBytesSent = responseData.Length - dataBytesLeftToSend; + Array.Copy(responseData, dataBytesSent, buffer, 0, currentDataLength); + additionalResponse.TotalParameterCount = (ushort)responseParameters.Length; + additionalResponse.TotalDataCount = (ushort)responseData.Length; + additionalResponse.TransData = buffer; + additionalResponse.ParameterDisplacement = (ushort)response.TransParameters.Length; + additionalResponse.DataDisplacement = (ushort)dataBytesSent; + sendQueue.Add(additionalResponse); + + dataBytesLeftToSend -= currentDataLength; + } + } + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/TransactionSubcommandHelper.cs b/SMBLibrary/Server/ResponseHelpers/TransactionSubcommandHelper.cs new file mode 100644 index 0000000..53088f7 --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/TransactionSubcommandHelper.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using SMBLibrary.SMB1; +using SMBLibrary.Services; +using Utilities; + +namespace SMBLibrary.Server +{ + public class TransactionSubcommandHelper + { + internal static TransactionTransactNamedPipeResponse GetSubcommandResponse(SMBHeader header, TransactionTransactNamedPipeRequest subcommand, NamedPipeShare share, StateObject state) + { + string openedFilePath = state.GetOpenedFilePath(subcommand.FID); + if (openedFilePath == null) + { + header.Status = NTStatus.STATUS_INVALID_HANDLE; + return null; + } + + TransactionTransactNamedPipeResponse response = new TransactionTransactNamedPipeResponse(); + RemoteService service = share.GetService(openedFilePath); + if (service != null) + { + RPCPDU rpcRequest = RPCPDU.GetPDU(subcommand.WriteData); + RPCPDU rpcReply = RemoteServiceHelper.GetRPCReply(rpcRequest, service); + response.ReadData = rpcReply.GetBytes(); + return response; + } + + // This code should not execute unless the request sequence is invalid + header.Status = NTStatus.STATUS_INVALID_SMB; + return null; + } + } +} diff --git a/SMBLibrary/Server/ResponseHelpers/TreeConnectHelper.cs b/SMBLibrary/Server/ResponseHelpers/TreeConnectHelper.cs new file mode 100644 index 0000000..322b69d --- /dev/null +++ b/SMBLibrary/Server/ResponseHelpers/TreeConnectHelper.cs @@ -0,0 +1,104 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.SMB1; +using Utilities; + +namespace SMBLibrary.Server +{ + public class TreeConnectHelper + { + internal static SMBCommand GetTreeConnectResponse(SMBHeader header, TreeConnectAndXRequest request, StateObject state, ShareCollection shares) + { + bool isExtended = (request.Flags & TreeConnectFlags.ExtendedResponse) > 0; + string relativePath = ServerPathUtils.GetRelativeServerPath(request.Path); + if (String.Equals(relativePath, "\\IPC$", StringComparison.InvariantCultureIgnoreCase)) + { + header.TID = state.AddConnectedTree(relativePath); + if (isExtended) + { + return CreateTreeConnectResponseExtended(ServiceName.NamedPipe); + } + else + { + return CreateTreeConnectResponse(ServiceName.NamedPipe); + } + } + else + { + FileSystemShare share = shares.GetShareFromRelativePath(relativePath); + if (share == null) + { + header.Status = NTStatus.STATUS_OBJECT_PATH_NOT_FOUND; + return new ErrorResponse(CommandName.SMB_COM_TREE_CONNECT_ANDX); + } + else + { + string userName = state.GetConnectedUserName(header.UID); + if (!share.HasReadAccess(userName)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_TREE_CONNECT_ANDX); + } + else + { + header.TID = state.AddConnectedTree(relativePath); + if (isExtended) + { + return CreateTreeConnectResponseExtended(ServiceName.DiskShare); + } + else + { + return CreateTreeConnectResponse(ServiceName.DiskShare); + } + } + } + } + } + + private static TreeConnectAndXResponse CreateTreeConnectResponse(ServiceName serviceName) + { + TreeConnectAndXResponse response = new TreeConnectAndXResponse(); + response.OptionalSupport = OptionalSupportFlags.SMB_SUPPORT_SEARCH_BITS; + response.NativeFileSystem = String.Empty; + response.Service = serviceName; + return response; + } + + private static TreeConnectAndXResponseExtended CreateTreeConnectResponseExtended(ServiceName serviceName) + { + TreeConnectAndXResponseExtended response = new TreeConnectAndXResponseExtended(); + response.OptionalSupport = OptionalSupportFlags.SMB_SUPPORT_SEARCH_BITS; + response.MaximalShareAccessRights.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | FileAccessMask.FILE_APPEND_DATA | + FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA | + FileAccessMask.FILE_EXECUTE | + FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES | + FileAccessMask.DELETE | FileAccessMask.READ_CONTROL | FileAccessMask.WRITE_DAC | FileAccessMask.WRITE_OWNER | FileAccessMask.SYNCHRONIZE; + response.GuestMaximalShareAccessRights.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | + FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA | + FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES | + FileAccessMask.READ_CONTROL | FileAccessMask.SYNCHRONIZE; + response.NativeFileSystem = String.Empty; + response.Service = serviceName; + return response; + } + + internal static SMBCommand GetTreeDisconnectResponse(SMBHeader header, TreeDisconnectRequest request, StateObject state) + { + if (!state.IsTreeConnected(header.TID)) + { + header.Status = NTStatus.STATUS_SMB_BAD_TID; + return new ErrorResponse(CommandName.SMB_COM_TREE_DISCONNECT); + } + + state.RemoveConnectedTree(header.TID); + return new TreeDisconnectResponse(); + } + } +} diff --git a/SMBLibrary/Server/SMBServer.cs b/SMBLibrary/Server/SMBServer.cs new file mode 100644 index 0000000..4483331 --- /dev/null +++ b/SMBLibrary/Server/SMBServer.cs @@ -0,0 +1,638 @@ +/* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Text; +using SMBLibrary.NetBios; +using SMBLibrary.SMB1; +using SMBLibrary.Services; +using Utilities; + +namespace SMBLibrary.Server +{ + public class SMBServer + { + public const int NetBiosOverTCPPort = 139; + public const int DirectTCPPort = 445; + public const string NTLanManagerDialect = "NT LM 0.12"; + public const bool EnableExtendedSecurity = true; + + private ShareCollection m_shares; // e.g. Shared folders + private INTLMAuthenticationProvider m_users; + private NamedPipeShare m_services; // Named pipes + private IPAddress m_serverAddress; + private SMBTransportType m_transport; + + private Socket m_listenerSocket; + private bool m_listening; + private Guid m_serverGuid; + + public SMBServer(ShareCollection shares, INTLMAuthenticationProvider users, IPAddress serverAddress, SMBTransportType transport) + { + m_shares = shares; + m_users = users; + m_serverAddress = serverAddress; + m_serverGuid = Guid.NewGuid(); + m_transport = transport; + + m_services = new NamedPipeShare(shares.ListShares()); + } + + public void Start() + { + if (!m_listening) + { + m_listening = true; + + m_listenerSocket = new Socket(m_serverAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + int port = (m_transport == SMBTransportType.DirectTCPTransport ? DirectTCPPort : NetBiosOverTCPPort); + m_listenerSocket.Bind(new IPEndPoint(m_serverAddress, port)); + m_listenerSocket.Listen((int)SocketOptionName.MaxConnections); + m_listenerSocket.BeginAccept(ConnectRequestCallback, m_listenerSocket); + } + } + + public void Stop() + { + m_listening = false; + SocketUtils.ReleaseSocket(m_listenerSocket); + } + + // This method Accepts new connections + private void ConnectRequestCallback(IAsyncResult ar) + { + System.Diagnostics.Debug.Print("[{0}] New connection request", DateTime.Now.ToString("HH:mm:ss:ffff")); + Socket listenerSocket = (Socket)ar.AsyncState; + + Socket clientSocket; + try + { + clientSocket = listenerSocket.EndAccept(ar); + } + catch (ObjectDisposedException) + { + return; + } + catch (SocketException ex) + { + const int WSAECONNRESET = 10054; + // Client may have closed the connection before we start to process the connection request. + // When we get this error, we have to continue to accept other requests. + // See http://stackoverflow.com/questions/7704417/socket-endaccept-error-10054 + if (ex.ErrorCode == WSAECONNRESET) + { + m_listenerSocket.BeginAccept(ConnectRequestCallback, m_listenerSocket); + } + System.Diagnostics.Debug.Print("[{0}] Connection request error {1}", DateTime.Now.ToString("HH:mm:ss:ffff"), ex.ErrorCode); + return; + } + + StateObject state = new StateObject(); + state.ReceiveBuffer = new byte[StateObject.ReceiveBufferSize]; + // Disable the Nagle Algorithm for this tcp socket: + clientSocket.NoDelay = true; + state.ClientSocket = clientSocket; + try + { + clientSocket.BeginReceive(state.ReceiveBuffer, 0, StateObject.ReceiveBufferSize, 0, ReceiveCallback, state); + } + catch (ObjectDisposedException) + { + } + catch (SocketException) + { + } + m_listenerSocket.BeginAccept(ConnectRequestCallback, m_listenerSocket); + } + + private void ReceiveCallback(IAsyncResult result) + { + StateObject state = (StateObject)result.AsyncState; + Socket clientSocket = state.ClientSocket; + + if (!m_listening) + { + clientSocket.Close(); + return; + } + + byte[] receiveBuffer = state.ReceiveBuffer; + + int bytesReceived; + + try + { + bytesReceived = clientSocket.EndReceive(result); + } + catch (ObjectDisposedException) + { + return; + } + catch (SocketException) + { + return; + } + + if (bytesReceived == 0) + { + // The other side has closed the connection + System.Diagnostics.Debug.Print("[{0}] The other side closed the connection", DateTime.Now.ToString("HH:mm:ss:ffff")); + clientSocket.Close(); + return; + } + + byte[] currentBuffer = new byte[bytesReceived]; + Array.Copy(receiveBuffer, currentBuffer, bytesReceived); + + ProcessCurrentBuffer(currentBuffer, state); + + if (clientSocket.Connected) + { + try + { + clientSocket.BeginReceive(state.ReceiveBuffer, 0, StateObject.ReceiveBufferSize, 0, ReceiveCallback, state); + } + catch (ObjectDisposedException) + { + } + catch (SocketException) + { + } + } + } + + public void ProcessCurrentBuffer(byte[] currentBuffer, StateObject state) + { + Socket clientSocket = state.ClientSocket; + + if (state.ConnectionBuffer.Length == 0) + { + state.ConnectionBuffer = currentBuffer; + } + else + { + byte[] oldConnectionBuffer = state.ConnectionBuffer; + state.ConnectionBuffer = new byte[oldConnectionBuffer.Length + currentBuffer.Length]; + Array.Copy(oldConnectionBuffer, state.ConnectionBuffer, oldConnectionBuffer.Length); + Array.Copy(currentBuffer, 0, state.ConnectionBuffer, oldConnectionBuffer.Length, currentBuffer.Length); + } + + // we now have all SMB message bytes received so far in state.ConnectionBuffer + int bytesLeftInBuffer = state.ConnectionBuffer.Length; + + + while (bytesLeftInBuffer >= 4) + { + // The packet is either Direct TCP transport packet (which is an NBT Session Message + // Packet) or an NBT packet. + int bufferOffset = state.ConnectionBuffer.Length - bytesLeftInBuffer; + byte flags = ByteReader.ReadByte(state.ConnectionBuffer, bufferOffset + 1); + int trailerLength = (flags & 0x01) << 16 | BigEndianConverter.ToUInt16(state.ConnectionBuffer, bufferOffset + 2); + int packetLength = 4 + trailerLength; + + if (flags > 0x01) + { + System.Diagnostics.Debug.Print("[{0}] Invalid NBT flags", DateTime.Now.ToString("HH:mm:ss:ffff")); + state.ClientSocket.Close(); + return; + } + + if (packetLength > bytesLeftInBuffer) + { + break; + } + else + { + byte[] packetBytes = new byte[packetLength]; + Array.Copy(state.ConnectionBuffer, bufferOffset, packetBytes, 0, packetLength); + ProcessPacket(packetBytes, state); + bytesLeftInBuffer -= packetLength; + if (!clientSocket.Connected) + { + // Do not continue to process the buffer if the other side closed the connection + return; + } + } + } + + if (bytesLeftInBuffer > 0) + { + byte[] newReceiveBuffer = new byte[bytesLeftInBuffer]; + Array.Copy(state.ConnectionBuffer, state.ConnectionBuffer.Length - bytesLeftInBuffer, newReceiveBuffer, 0, bytesLeftInBuffer); + state.ConnectionBuffer = newReceiveBuffer; + } + else + { + state.ConnectionBuffer = new byte[0]; + } + } + + public void ProcessPacket(byte[] packetBytes, StateObject state) + { + SessionPacket packet = null; +#if DEBUG + packet = SessionPacket.GetSessionPacket(packetBytes); +#else + try + { + packet = SessionPacket.GetSessionPacket(packetBytes); + } + catch (Exception) + { + state.ClientSocket.Close(); + return; + } +#endif + if (packet is SessionRequestPacket && m_transport == SMBTransportType.NetBiosOverTCP) + { + PositiveSessionResponsePacket response = new PositiveSessionResponsePacket(); + TrySendPacket(state, response); + } + else if (packet is SessionKeepAlivePacket && m_transport == SMBTransportType.NetBiosOverTCP) + { + // [RFC 1001] NetBIOS session keep alives do not require a response from the NetBIOS peer + } + else if (packet is SessionMessagePacket) + { + SMBMessage message = null; +#if DEBUG + message = SMBMessage.GetSMBMessage(packet.Trailer); + System.Diagnostics.Debug.Print("[{0}] Message Received: {1} Commands, First Command: {2}, Packet length: {3}", DateTime.Now.ToString("HH:mm:ss:ffff"), message.Commands.Count, message.Commands[0].CommandName.ToString(), packet.Length); +#else + try + { + message = SMBMessage.GetSMBMessage(packet.Trailer); + } + catch (Exception) + { + state.ClientSocket.Close(); + return; + } +#endif + ProcessMessage(message, state); + } + else + { + System.Diagnostics.Debug.Print("[{0}] Invalid NetBIOS packet", DateTime.Now.ToString("HH:mm:ss:ffff")); + state.ClientSocket.Close(); + return; + } + } + + public void ProcessMessage(SMBMessage message, StateObject state) + { + SMBMessage reply = new SMBMessage(); + PrepareResponseHeader(reply, message); + List sendQueue = new List(); + + foreach (SMBCommand command in message.Commands) + { + SMBCommand response = ProcessCommand(reply.Header, command, state, sendQueue); + if (response != null) + { + reply.Commands.Add(response); + } + if (reply.Header.Status != NTStatus.STATUS_SUCCESS) + { + break; + } + } + + if (reply.Commands.Count > 0) + { + TrySendMessage(state, reply); + + foreach (SMBCommand command in sendQueue) + { + SMBMessage secondaryReply = new SMBMessage(); + secondaryReply.Header = reply.Header; + secondaryReply.Commands.Add(command); + TrySendMessage(state, secondaryReply); + } + } + } + + /// + /// May return null + /// + public SMBCommand ProcessCommand(SMBHeader header, SMBCommand command, StateObject state, List sendQueue) + { + if (command is NegotiateRequest) + { + NegotiateRequest request = (NegotiateRequest)command; + if (request.Dialects.Contains(SMBServer.NTLanManagerDialect)) + { + if (EnableExtendedSecurity && header.ExtendedSecurityFlag) + { + return NegotiateHelper.GetNegotiateResponseExtended(request, m_serverGuid); + } + else + { + byte[] serverChallenge = m_users.GenerateServerChallenge(); + return NegotiateHelper.GetNegotiateResponse(header, request, serverChallenge); + } + } + else + { + return new NegotiateResponseNotSupported(); + } + } + else if (command is SessionSetupAndXRequest) + { + SessionSetupAndXRequest request = (SessionSetupAndXRequest)command; + state.MaxBufferSize = request.MaxBufferSize; + return NegotiateHelper.GetSessionSetupResponse(header, request, m_users, state); + } + else if (command is SessionSetupAndXRequestExtended) + { + SessionSetupAndXRequestExtended request = (SessionSetupAndXRequestExtended)command; + state.MaxBufferSize = request.MaxBufferSize; + return NegotiateHelper.GetSessionSetupResponseExtended(header, request, m_users, state); + } + else if (command is EchoRequest) + { + return ServerResponseHelper.GetEchoResponse((EchoRequest)command, sendQueue); + } + else if (state.IsAuthenticated(header.UID)) + { + if (command is TreeConnectAndXRequest) + { + TreeConnectAndXRequest request = (TreeConnectAndXRequest)command; + return TreeConnectHelper.GetTreeConnectResponse(header, request, state, m_shares); + } + else if (command is LogoffAndXRequest) + { + return new LogoffAndXResponse(); + } + else if (state.IsTreeConnected(header.TID)) + { + string rootPath = state.GetConnectedTreePath(header.TID); + object share; + if (state.IsIPC(header.TID)) + { + share = m_services; + } + else + { + share = m_shares.GetShareFromRelativePath(rootPath); + } + + if (command is CreateDirectoryRequest) + { + if (!(share is FileSystemShare)) + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + return new ErrorResponse(command.CommandName); + } + CreateDirectoryRequest request = (CreateDirectoryRequest)command; + return FileSystemResponseHelper.GetCreateDirectoryResponse(header, request, (FileSystemShare)share, state); + } + else if (command is DeleteDirectoryRequest) + { + if (!(share is FileSystemShare)) + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + return new ErrorResponse(command.CommandName); + } + DeleteDirectoryRequest request = (DeleteDirectoryRequest)command; + return FileSystemResponseHelper.GetDeleteDirectoryResponse(header, request, (FileSystemShare)share, state); + } + else if (command is CloseRequest) + { + CloseRequest request = (CloseRequest)command; + return ServerResponseHelper.GetCloseResponse(header, request, state); + } + else if (command is FlushRequest) + { + return new FlushResponse(); + } + else if (command is DeleteRequest) + { + if (!(share is FileSystemShare)) + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + return new ErrorResponse(command.CommandName); + } + DeleteRequest request = (DeleteRequest)command; + return FileSystemResponseHelper.GetDeleteResponse(header, request, (FileSystemShare)share, state); + } + else if (command is RenameRequest) + { + if (!(share is FileSystemShare)) + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + return new ErrorResponse(command.CommandName); + } + RenameRequest request = (RenameRequest)command; + return FileSystemResponseHelper.GetRenameResponse(header, request, (FileSystemShare)share, state); + } + else if (command is QueryInformationRequest) + { + if (!(share is FileSystemShare)) + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + return new ErrorResponse(command.CommandName); + } + QueryInformationRequest request = (QueryInformationRequest)command; + return FileSystemResponseHelper.GetQueryInformationResponse(header, request, (FileSystemShare)share); + } + else if (command is SetInformationRequest) + { + if (!(share is FileSystemShare)) + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + return new ErrorResponse(command.CommandName); + } + SetInformationRequest request = (SetInformationRequest)command; + return FileSystemResponseHelper.GetSetInformationResponse(header, request, (FileSystemShare)share, state); + } + else if (command is ReadRequest) + { + ReadRequest request = (ReadRequest)command; + return ReadWriteResponseHelper.GetReadResponse(header, request, share, state); + } + else if (command is WriteRequest) + { + string userName = state.GetConnectedUserName(header.UID); + if (share is FileSystemShare && !((FileSystemShare)share).HasWriteAccess(userName)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(command.CommandName); + } + WriteRequest request = (WriteRequest)command; + return ReadWriteResponseHelper.GetWriteResponse(header, request, share, state); + } + else if (command is CheckDirectoryRequest) + { + if (!(share is FileSystemShare)) + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + return new ErrorResponse(command.CommandName); + } + CheckDirectoryRequest request = (CheckDirectoryRequest)command; + return FileSystemResponseHelper.GetCheckDirectoryResponse(header, request, (FileSystemShare)share); + } + else if (command is WriteRawRequest) + { + // [MS-CIFS] 3.3.5.26 - Receiving an SMB_COM_WRITE_RAW Request: + // the server MUST verify that the Server.Capabilities include CAP_RAW_MODE, + // If an error is detected [..] the Write Raw operation MUST fail and + // the server MUST return a Final Server Response [..] with the Count field set to zero. + return new WriteRawFinalResponse(); + } + else if (command is SetInformation2Request) + { + if (!(share is FileSystemShare)) + { + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + return new ErrorResponse(command.CommandName); + } + SetInformation2Request request = (SetInformation2Request)command; + return FileSystemResponseHelper.GetSetInformation2Response(header, request, (FileSystemShare)share, state); + } + else if (command is LockingAndXRequest) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(CommandName.SMB_COM_LOCKING_ANDX); + } + else if (command is OpenAndXRequest) + { + OpenAndXRequest request = (OpenAndXRequest)command; + return OpenAndXHelper.GetOpenAndXResponse(header, request, share, state); + } + else if (command is ReadAndXRequest) + { + ReadAndXRequest request = (ReadAndXRequest)command; + return ReadWriteResponseHelper.GetReadResponse(header, request, share, state); + } + else if (command is WriteAndXRequest) + { + string userName = state.GetConnectedUserName(header.UID); + if (share is FileSystemShare && !((FileSystemShare)share).HasWriteAccess(userName)) + { + header.Status = NTStatus.STATUS_ACCESS_DENIED; + return new ErrorResponse(command.CommandName); + } + WriteAndXRequest request = (WriteAndXRequest)command; + return ReadWriteResponseHelper.GetWriteResponse(header, request, share, state); + } + else if (command is FindClose2Request) + { + return ServerResponseHelper.GetFindClose2Request(header, (FindClose2Request)command, state); + } + else if (command is TreeDisconnectRequest) + { + TreeDisconnectRequest request = (TreeDisconnectRequest)command; + return TreeConnectHelper.GetTreeDisconnectResponse(header, request, state); + } + else if (command is TransactionRequest) // Both TransactionRequest and Transaction2Request + { + TransactionRequest request = (TransactionRequest)command; + try + { + return TransactionHelper.GetTransactionResponse(header, request, share, state, sendQueue); + } + catch (UnsupportedInformationLevelException) + { + header.Status = NTStatus.STATUS_INVALID_PARAMETER; + return new ErrorResponse(command.CommandName); + } + } + else if (command is TransactionSecondaryRequest) // Both TransactionSecondaryRequest and Transaction2SecondaryRequest + { + TransactionSecondaryRequest request = (TransactionSecondaryRequest)command; + try + { + return TransactionHelper.GetTransactionResponse(header, request, share, state, sendQueue); + } + catch (UnsupportedInformationLevelException) + { + header.Status = NTStatus.STATUS_INVALID_PARAMETER; + return new ErrorResponse(command.CommandName); + } + } + else if (command is NTTransactRequest) + { + NTTransactRequest request = (NTTransactRequest)command; + return NTTransactHelper.GetNTTransactResponse(header, request, share, state, sendQueue); + } + else if (command is NTTransactSecondaryRequest) + { + NTTransactSecondaryRequest request = (NTTransactSecondaryRequest)command; + return NTTransactHelper.GetNTTransactResponse(header, request, share, state, sendQueue); + } + else if (command is NTCreateAndXRequest) + { + NTCreateAndXRequest request = (NTCreateAndXRequest)command; + return NTCreateHelper.GetNTCreateResponse(header, request, share, state); + } + } + else + { + header.Status = NTStatus.STATUS_SMB_BAD_TID; + return new ErrorResponse(command.CommandName); + } + } + + header.Status = NTStatus.STATUS_SMB_BAD_COMMAND; + return new ErrorResponse(command.CommandName); + } + + public static void TrySendMessage(StateObject state, SMBMessage reply) + { + SessionMessagePacket packet = new SessionMessagePacket(); + packet.Trailer = reply.GetBytes(); + TrySendPacket(state, packet); + System.Diagnostics.Debug.Print("[{0}] Reply sent: {1} Commands, First Command: {2}, Packet length: {3}", DateTime.Now.ToString("HH:mm:ss:ffff"), reply.Commands.Count, reply.Commands[0].CommandName.ToString(), packet.Length); + } + + public static void TrySendPacket(StateObject state, SessionPacket response) + { + Socket clientSocket = state.ClientSocket; + try + { + clientSocket.Send(response.GetBytes()); + } + catch (SocketException) + { + } + catch (ObjectDisposedException) + { + } + } + + private static void PrepareResponseHeader(SMBMessage response, SMBMessage request) + { + response.Header.Status = NTStatus.STATUS_SUCCESS; + response.Header.Flags = HeaderFlags.CaseInsensitive | HeaderFlags.CanonicalizedPaths | HeaderFlags.Reply; + response.Header.Flags2 = HeaderFlags2.NTStatusCode; + if ((request.Header.Flags2 & HeaderFlags2.LongNamesAllowed) > 0) + { + response.Header.Flags2 |= HeaderFlags2.LongNamesAllowed | HeaderFlags2.LongNameUsed; + } + if ((request.Header.Flags2 & HeaderFlags2.ExtendedAttributes) > 0) + { + response.Header.Flags2 |= HeaderFlags2.ExtendedAttributes; + } + if ((request.Header.Flags2 & HeaderFlags2.ExtendedSecurity) > 0) + { + response.Header.Flags2 |= HeaderFlags2.ExtendedSecurity; + } + if ((request.Header.Flags2 & HeaderFlags2.Unicode) > 0) + { + response.Header.Flags2 |= HeaderFlags2.Unicode; + } + response.Header.MID = request.Header.MID; + response.Header.PID = request.Header.PID; + response.Header.UID = request.Header.UID; + response.Header.TID = request.Header.TID; + } + } +} diff --git a/SMBLibrary/Server/ShareCollection.cs b/SMBLibrary/Server/ShareCollection.cs new file mode 100644 index 0000000..8577388 --- /dev/null +++ b/SMBLibrary/Server/ShareCollection.cs @@ -0,0 +1,78 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.Server +{ + public class ShareCollection : List + { + public void Add(string shareName, List readAccess, List writeAccess, IFileSystem fileSystem) + { + FileSystemShare share = new FileSystemShare(); + share.Name = shareName; + share.ReadAccess = readAccess; + share.WriteAccess = writeAccess; + share.FileSystem = fileSystem; + this.Add(share); + } + public bool Contains(string shareName, StringComparison comparisonType) + { + return (this.IndexOf(shareName, comparisonType) != -1); + } + + public int IndexOf(string shareName, StringComparison comparisonType) + { + for (int index = 0; index < this.Count; index++) + { + if (this[index].Name.Equals(shareName, comparisonType)) + { + return index; + } + } + + return -1; + } + + public List ListShares() + { + List result = new List(); + foreach (FileSystemShare share in this) + { + result.Add(share.Name); + } + return result; + } + + /// e.g. \Shared + public FileSystemShare GetShareFromRelativePath(string relativePath) + { + if (relativePath.StartsWith(@"\")) + { + relativePath = relativePath.Substring(1); + } + + int indexOfSeparator = relativePath.IndexOf(@"\"); + if (indexOfSeparator >= 0) + { + relativePath = relativePath.Substring(0, indexOfSeparator); + } + + int index = IndexOf(relativePath, StringComparison.InvariantCultureIgnoreCase); + if (index >= 0) + { + return this[index]; + } + else + { + return null; + } + } + } +} diff --git a/SMBLibrary/Server/StateObject.cs b/SMBLibrary/Server/StateObject.cs new file mode 100644 index 0000000..3b45481 --- /dev/null +++ b/SMBLibrary/Server/StateObject.cs @@ -0,0 +1,289 @@ +/* Copyright (C) 2014-2016 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Sockets; +using System.Text; +using Utilities; + +namespace SMBLibrary.Server +{ + public class StateObject + { + public Socket ClientSocket = null; + public const int ReceiveBufferSize = 65536; + public byte[] ReceiveBuffer = new byte[ReceiveBufferSize]; // immediate receive buffer + public byte[] ConnectionBuffer = new byte[0]; // we append the receive buffer here until we have a complete Message + + public int MaxBufferSize; + public bool LargeRead; + public bool LargeWrite; + + // Key is UID + private Dictionary m_connectedUsers = new Dictionary(); + private ushort m_nextUID = 1; + + // Key is TID + private Dictionary m_connectedTrees = new Dictionary(); + private ushort m_nextTID = 1; + + // Key is FID + private Dictionary m_openedFiles = new Dictionary(); + private ushort m_nextFID = 1; + // Key is FID + private Dictionary m_namedPipeResponse = new Dictionary(); + + // Key is PID + public Dictionary ProcessStateList = new Dictionary(); + public const int MaxSearches = 2048; // Windows servers initialize Server.MaxSearches to 2048. + public Dictionary> OpenSearches = new Dictionary>(); + private ushort m_nextSearchHandle = 1; + + /// + /// An open UID MUST be unique within an SMB connection. + /// The value of 0xFFFE SHOULD NOT be used as a valid UID. All other possible values for a UID, excluding zero (0x0000), are valid. + /// + private ushort AllocateUserID() + { + while (m_connectedUsers.ContainsKey(m_nextUID) || m_nextUID == 0 || m_nextUID == 0xFFFE || m_nextUID == 0xFFFF) + { + m_nextUID++; + } + ushort userID = m_nextUID; + m_nextUID++; + return userID; + } + + public ushort AddConnectedUser(string userName) + { + ushort userID = AllocateUserID(); + m_connectedUsers.Add(userID, userName); + return userID; + } + + public string GetConnectedUserName(ushort userID) + { + if (m_connectedUsers.ContainsKey(userID)) + { + return m_connectedUsers[userID]; + } + else + { + return null; + } + } + + public bool IsAuthenticated(ushort userID) + { + return m_connectedUsers.ContainsKey(userID); + } + + public void RemoveConnectedUser(ushort userID) + { + m_connectedUsers.Remove(userID); + } + + /// + /// An open TID MUST be unique within an SMB connection. + /// The value 0xFFFF MUST NOT be used as a valid TID. All other possible values for TID, including zero (0x0000), are valid. + /// + private ushort AllocateTreeID() + { + while (m_connectedTrees.ContainsKey(m_nextTID) || m_nextTID == 0 || m_nextTID == 0xFFFF) + { + m_nextTID++; + } + ushort treeID = m_nextTID; + m_nextTID++; + return treeID; + } + + public ushort AddConnectedTree(string relativePath) + { + ushort treeID = AllocateTreeID(); + m_connectedTrees.Add(treeID, relativePath); + return treeID; + } + + public string GetConnectedTreePath(ushort treeID) + { + if (m_connectedTrees.ContainsKey(treeID)) + { + return m_connectedTrees[treeID]; + } + else + { + return null; + } + } + + public void RemoveConnectedTree(ushort treeID) + { + m_connectedTrees.Remove(treeID); + } + + public bool IsTreeConnected(ushort treeID) + { + return m_connectedTrees.ContainsKey(treeID); + } + + public bool IsIPC(ushort treeID) + { + string relativePath = GetConnectedTreePath(treeID); + return String.Equals(relativePath, "\\IPC$", StringComparison.InvariantCultureIgnoreCase); + } + + public ProcessStateObject GetProcessState(uint processID) + { + if (ProcessStateList.ContainsKey(processID)) + { + return ProcessStateList[processID]; + } + else + { + return null; + } + } + + /// + /// Get or Create process state + /// + public ProcessStateObject ObtainProcessState(uint processID) + { + if (ProcessStateList.ContainsKey(processID)) + { + return ProcessStateList[processID]; + } + else + { + ProcessStateObject processState = new ProcessStateObject(); + ProcessStateList[processID] = processState; + return processState; + } + } + + /// + /// The value 0xFFFF MUST NOT be used as a valid FID. All other possible values for FID, including zero (0x0000) are valid. + /// + /// + private ushort AllocateFileID() + { + while (m_openedFiles.ContainsKey(m_nextFID) || m_nextFID == 0 || m_nextFID == 0xFFFF) + { + m_nextFID++; + } + ushort fileID = m_nextFID; + m_nextFID++; + return fileID; + } + + /// Should include the path relative to the file system + /// FileID + public ushort AddOpenedFile(string relativePath) + { + return AddOpenedFile(relativePath, null); + } + + public ushort AddOpenedFile(string relativePath, Stream stream) + { + ushort fileID = AllocateFileID(); + m_openedFiles.Add(fileID, new OpenedFileObject(relativePath, stream)); + return fileID; + } + + public string GetOpenedFilePath(ushort fileID) + { + if (m_openedFiles.ContainsKey(fileID)) + { + return m_openedFiles[fileID].Path; + } + else + { + return null; + } + } + + public OpenedFileObject GetOpenedFileObject(ushort fileID) + { + if (m_openedFiles.ContainsKey(fileID)) + { + return m_openedFiles[fileID]; + } + else + { + return null; + } + } + + public bool IsFileOpen(ushort fileID) + { + return m_openedFiles.ContainsKey(fileID); + } + + public void RemoveOpenedFile(ushort fileID) + { + Stream stream = m_openedFiles[fileID].Stream; + if (stream != null) + { + stream.Close(); + } + m_openedFiles.Remove(fileID); + } + + public void StoreNamedPipeReply(ushort fileID, byte[] response) + { + m_namedPipeResponse.Add(fileID, response); + } + + public byte[] RetrieveNamedPipeReply(ushort fileID) + { + if (m_namedPipeResponse.ContainsKey(fileID)) + { + byte[] result = m_namedPipeResponse[fileID]; + m_namedPipeResponse.Remove(fileID); + return result; + } + else + { + return new byte[0]; + } + } + + public uint? GetMaxDataCount(uint processID) + { + ProcessStateObject processState = GetProcessState(processID); + if (processState != null) + { + return processState.MaxDataCount; + } + else + { + return null; + } + } + + public ushort AllocateSearchHandle() + { + while (OpenSearches.ContainsKey(m_nextSearchHandle) || m_nextSearchHandle == 0 || m_nextSearchHandle == 0xFFFF) + { + m_nextSearchHandle++; + } + ushort searchHandle = m_nextSearchHandle; + m_nextSearchHandle++; + return searchHandle; + } + + public void ReleaseSearchHandle(ushort searchHandle) + { + if (OpenSearches.ContainsKey(searchHandle)) + { + OpenSearches.Remove(searchHandle); + } + } + } +} diff --git a/SMBLibrary/Server/User.cs b/SMBLibrary/Server/User.cs new file mode 100644 index 0000000..caff1c2 --- /dev/null +++ b/SMBLibrary/Server/User.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.Server +{ + public class User + { + public string AccountName; + public string Password; + + public User(string accountName, string password) + { + AccountName = accountName; + Password = password; + } + } +} diff --git a/SMBLibrary/Server/UserCollection.cs b/SMBLibrary/Server/UserCollection.cs new file mode 100644 index 0000000..4ab94d9 --- /dev/null +++ b/SMBLibrary/Server/UserCollection.cs @@ -0,0 +1,42 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.Server +{ + public class UserCollection : List + { + public void Add(string accountName, string password) + { + Add(new User(accountName, password)); + } + + public int IndexOf(string accountName) + { + for (int index = 0; index < this.Count; index++) + { + if (string.Equals(this[index].AccountName, accountName, StringComparison.InvariantCultureIgnoreCase)) + { + return index; + } + } + return -1; + } + + public List ListUsers() + { + List result = new List(); + foreach (User user in this) + { + result.Add(user.AccountName); + } + return result; + } + } +} diff --git a/SMBLibrary/Services/Enums/PlatformName.cs b/SMBLibrary/Services/Enums/PlatformName.cs new file mode 100644 index 0000000..ce0a811 --- /dev/null +++ b/SMBLibrary/Services/Enums/PlatformName.cs @@ -0,0 +1,15 @@ + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] 2.2.2.6 + /// + public enum PlatformName : uint + { + DOS = 300, // PLATFORM_ID_DOS + OS2 = 400, // PLATFORM_ID_OS2 + NT = 500, // PLATFORM_ID_NT + OSF = 600, // PLATFORM_ID_OSF + VMS = 700, // PLATFORM_ID_VMS + } +} diff --git a/SMBLibrary/Services/RemoteService.cs b/SMBLibrary/Services/RemoteService.cs new file mode 100644 index 0000000..d54efbb --- /dev/null +++ b/SMBLibrary/Services/RemoteService.cs @@ -0,0 +1,29 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Utilities; + +namespace SMBLibrary.Services +{ + public abstract class RemoteService + { + public abstract byte[] GetResponseBytes(ushort opNum, byte[] requestBytes); + + public abstract Guid InterfaceGuid + { + get; + } + + public abstract string PipeName + { + get; + } + } +} diff --git a/SMBLibrary/Services/RemoteServiceHelper.cs b/SMBLibrary/Services/RemoteServiceHelper.cs new file mode 100644 index 0000000..063fdc5 --- /dev/null +++ b/SMBLibrary/Services/RemoteServiceHelper.cs @@ -0,0 +1,135 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + public class RemoteServiceHelper + { + // v1 - DCE 1.1: Remote Procedure Call + // v2 - [MS-RPCE] 2.2.4.12 NDR Transfer Syntax Identifier + private static readonly Guid NDRTransferSyntaxIdentifier = new Guid("8A885D04-1CEB-11C9-9FE8-08002B104860"); + // v1 - [MS-RPCE] 3.3.1.5.3 - Bind Time Feature Negotiation + // Windows will reject this: + //private static readonly Guid BindTimeFeatureIdentifier1 = new Guid("6CB71C2C-9812-4540-0100-000000000000"); + // Windows will return NegotiationResult.NegotiateAck: + private static readonly Guid BindTimeFeatureIdentifier3 = new Guid("6CB71C2C-9812-4540-0300-000000000000"); + private static uint m_associationGroupID = 1; + + public static RPCPDU GetRPCReply(RPCPDU pdu, RemoteService service) + { + if (pdu is BindPDU) + { + return GetRPCBindResponse((BindPDU)pdu, service); + } + else if (pdu is RequestPDU) + { + return GetRPCResponse((RequestPDU)pdu, service); + } + else + { + throw new NotImplementedException(); + } + } + + private static BindAckPDU GetRPCBindResponse(BindPDU bindPDU, RemoteService service) + { + BindAckPDU bindAckPDU = new BindAckPDU(); + PrepareReply(bindAckPDU, bindPDU); + // See DCE 1.1: Remote Procedure Call - 12.6.3.6 + // The client should set the assoc_group_id field either to 0 (zero), to indicate a new association group, + // or to the known value. When the server receives a value of 0, this indicates that the client + // has requested a new association group, and it assigns a server unique value to the group. + if (bindPDU.AssociationGroupID == 0) + { + bindAckPDU.AssociationGroupID = m_associationGroupID; + m_associationGroupID++; + if (m_associationGroupID == 0) + { + m_associationGroupID++; + } + } + else + { + bindAckPDU.AssociationGroupID = bindPDU.AssociationGroupID; + } + bindAckPDU.SecondaryAddress = @"\PIPE" + service.PipeName; + bindAckPDU.MaxReceiveFragmentSize = bindPDU.MaxReceiveFragmentSize; + bindAckPDU.MaxTransmitFragmentSize = bindPDU.MaxTransmitFragmentSize; + foreach (ContextElement element in bindPDU.ContextList) + { + ResultElement resultElement = new ResultElement(); + if (element.AbstractSyntax.InterfaceUUID.Equals(service.InterfaceGuid)) + { + int index = IndexOfSupportedTransferSyntax(element.TransferSyntaxList); + if (index >= 0) + { + resultElement.Result = NegotiationResult.Acceptance; + resultElement.TransferSyntax = element.TransferSyntaxList[index]; + } + else if (element.TransferSyntaxList.Contains(new SyntaxID(BindTimeFeatureIdentifier3, 1))) + { + // [MS-RPCE] 3.3.1.5.3 + // If the server supports bind time feature negotiation, it MUST reply with the result + // field in the p_result_t structure of the bind_ack PDU equal to negotiate_ack. + resultElement.Result = NegotiationResult.NegotiateAck; + resultElement.Reason = RejectionReason.AbstractSyntaxNotSupported; + } + else + { + resultElement.Result = NegotiationResult.ProviderRejection; + resultElement.Reason = RejectionReason.ProposedTransferSyntaxesNotSupported; + } + } + else + { + resultElement.Result = NegotiationResult.ProviderRejection; + resultElement.Reason = RejectionReason.AbstractSyntaxNotSupported; + } + bindAckPDU.ResultList.Add(resultElement); + } + + return bindAckPDU; + } + + private static int IndexOfSupportedTransferSyntax(List syntaxList) + { + List supportedTransferSyntaxes = new List(); + supportedTransferSyntaxes.Add(new SyntaxID(NDRTransferSyntaxIdentifier, 1)); + // [MS-RPCE] Version 2.0 data representation protocol: + supportedTransferSyntaxes.Add(new SyntaxID(NDRTransferSyntaxIdentifier, 2)); + + for(int index = 0; index < syntaxList.Count; index++) + { + if (supportedTransferSyntaxes.Contains(syntaxList[index])) + { + return index; + } + } + return -1; + } + + private static ResponsePDU GetRPCResponse(RequestPDU requestPDU, RemoteService service) + { + ResponsePDU responsePDU = new ResponsePDU(); + PrepareReply(responsePDU, requestPDU); + responsePDU.Data = service.GetResponseBytes(requestPDU.OpNum, requestPDU.Data); + return responsePDU; + } + + private static void PrepareReply(RPCPDU reply, RPCPDU request) + { + reply.DataRepresentation = request.DataRepresentation; + reply.CallID = request.CallID; + reply.Flags = PacketFlags.FirstFragment | PacketFlags.LastFragment; + } + } +} diff --git a/SMBLibrary/Services/ServerService/EnumStructures/ShareTypeExtended.cs b/SMBLibrary/Services/ServerService/EnumStructures/ShareTypeExtended.cs new file mode 100644 index 0000000..96981b9 --- /dev/null +++ b/SMBLibrary/Services/ServerService/EnumStructures/ShareTypeExtended.cs @@ -0,0 +1,76 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using SMBLibrary.RPC; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] 2.2.2.4 Share Types + /// + public enum ShareType : uint + { + DiskDrive = 0x00000000, // STYPE_DISKTREE + PrintQueue = 0x00000001, // STYPE_PRINTQ + CommunicationDevice = 0x00000002, // STYPE_DEVICE + IPC = 0x00000003, // STYPE_IPC + ClusterShare = 0x02000000, // STYPE_CLUSTER_FS + ScaleOutClusterShare = 0x04000000, // STYPE_CLUSTER_SOFS + DfsShareInCluster = 0x08000000, // STYPE_CLUSTER_DFS + } + + public struct ShareTypeExtended // uint + { + public ShareType ShareType; + public bool IsSpecial; + public bool IsTemporary; + + public ShareTypeExtended(ShareType shareType) + { + ShareType = shareType; + IsSpecial = false; + IsTemporary = false; + } + + public ShareTypeExtended(ShareType shareType, bool isSpecial, bool isTemporary) + { + ShareType = shareType; + IsSpecial = isSpecial; + IsTemporary = isTemporary; + } + + public ShareTypeExtended(NDRParser parser) : this(parser.ReadUInt32()) + { + } + + public ShareTypeExtended(uint shareTypeExtended) + { + ShareType = (ShareType)(shareTypeExtended & 0x0FFFFFFF); + IsSpecial = (shareTypeExtended & 0x80000000) > 0; + IsTemporary = (shareTypeExtended & 0x40000000) > 0; + } + + public void Write(NDRWriter writer) + { + writer.WriteUInt32(ToUInt32()); + } + + public uint ToUInt32() + { + uint shareTypeExtended = (uint)ShareType; + if (IsSpecial) + { + shareTypeExtended |= 0x80000000; + } + if (IsTemporary) + { + shareTypeExtended |= 0x40000000; + } + return shareTypeExtended; + } + } +} diff --git a/SMBLibrary/Services/ServerService/Enums/Permissions.cs b/SMBLibrary/Services/ServerService/Enums/Permissions.cs new file mode 100644 index 0000000..58cbf03 --- /dev/null +++ b/SMBLibrary/Services/ServerService/Enums/Permissions.cs @@ -0,0 +1,16 @@ +using System; + +namespace SMBLibrary.Services +{ + [Flags] + public enum Permissions : uint + { + PERM_FILE_READ = 0x00000001, + PERM_FILE_WRITE = 0x00000002, + PERM_FILE_CREATE = 0x00000004, + ACCESS_EXEC = 0x00000008, + ACCESS_DELETE = 0x00000010, + ACCESS_ATRIB = 0x00000020, + ACCESS_PERM = 0x00000040, + } +} diff --git a/SMBLibrary/Services/ServerService/Enums/ServerType.cs b/SMBLibrary/Services/ServerService/Enums/ServerType.cs new file mode 100644 index 0000000..fe16790 --- /dev/null +++ b/SMBLibrary/Services/ServerService/Enums/ServerType.cs @@ -0,0 +1,41 @@ +using System; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] 2.2.2.7 Software Type Flags + /// + [Flags] + public enum ServerType : uint + { + Workstation = 0x00000001, // SV_TYPE_WORKSTATION + Server = 0x00000002, // SV_TYPE_SERVER + SqlServer = 0x00000004, // SV_TYPE_SQLSERVER + DomainController = 0x00000008, // SV_TYPE_DOMAIN_CTRL + BackupDomainController = 0x00000010, // SV_TYPE_DOMAIN_BAKCTRL + NetworkTimeSource = 0x00000020, // SV_TYPE_TIME_SOURCE + AppleFileProtocolServer = 0x00000040, // SV_TYPE_AFP + NovellServer = 0x00000080, // SV_TYPE_NOVELL + DomainMember = 0x00000100, // SV_TYPE_DOMAIN_MEMBER + PrintQueueServer = 0x00000200, // SV_TYPE_PRINTQ_SERVER + DialInServer = 0x00000400, // SV_TYPE_DIALIN_SERVER + XenixServer = 0x00000800, // SV_TYPE_XENIX_SERVER + WindowsNT = 0x00001000, // SV_TYPE_NT + WindowsForWorkgroupServer = 0x00002000, // SV_TYPE_WFW + FileAndPrintForNetware = 0x00004000, // SV_TYPE_SERVER_MFPN + ServerNT = 0x00008000, // SV_TYPE_SERVER_NT + PotentialBrowser = 0x00010000, // SV_TYPE_POTENTIAL_BROWSER + BackupBrowser = 0x00020000,// SV_TYPE_BACKUP_BROWSER + MasterBrowser = 0x00040000,// SV_TYPE_MASTER_BROWSER + DomainMaster = 0x00080000,// SV_TYPE_DOMAIN_MASTER + Windows = 0x00400000, // SV_TYPE_WINDOWS + DfsServer = 0x00800000, // Not in the official documents + TerminalServer = 0x02000000, // SV_TYPE_TERMINALSERVER + ClusterVirtualServer = 0x04000000, // SV_TYPE_CLUSTER_NT + NTCluster = 0x10000000, // SV_TYPE_CLUSTER_NT + LocalListOnly = 0x40000000, // SV_TYPE_LOCAL_LIST_ONLY + PrimaryDomain = 0x80000000,// SV_TYPE_DOMAIN_ENUM + + All = 0xFFFFFFFF, // SV_TYPE_ALL + } +} diff --git a/SMBLibrary/Services/ServerService/NetrServerGetInfoRequest.cs b/SMBLibrary/Services/ServerService/NetrServerGetInfoRequest.cs new file mode 100644 index 0000000..127e37b --- /dev/null +++ b/SMBLibrary/Services/ServerService/NetrServerGetInfoRequest.cs @@ -0,0 +1,39 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// NetrServerGetInfo Request (opnum 21) + /// + public class NetrServerGetInfoRequest + { + public string ServerName; + public uint Level; + + public NetrServerGetInfoRequest(byte[] buffer) + { + NDRParser parser = new NDRParser(buffer); + ServerName = parser.ReadTopLevelUnicodeStringPointer(); + Level = parser.ReadUInt32(); + } + + public byte[] GetBytes() + { + NDRWriter writer = new NDRWriter(); + writer.WriteTopLevelUnicodeStringPointer(ServerName); + writer.WriteUInt32(Level); + + return writer.GetBytes(); + } + } +} diff --git a/SMBLibrary/Services/ServerService/NetrServerGetInfoResponse.cs b/SMBLibrary/Services/ServerService/NetrServerGetInfoResponse.cs new file mode 100644 index 0000000..b5cc519 --- /dev/null +++ b/SMBLibrary/Services/ServerService/NetrServerGetInfoResponse.cs @@ -0,0 +1,44 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// NetrServerGetInfo Response (opnum 21) + /// + public class NetrServerGetInfoResponse + { + public ServerInfo InfoStruct; + public Win32Error Result; + + public NetrServerGetInfoResponse() + { + } + + public NetrServerGetInfoResponse(byte[] buffer) + { + NDRParser parser = new NDRParser(buffer); + InfoStruct = new ServerInfo(parser); + // 14.4 - If an operation returns a result, the representation of the result appears after all parameters in + Result = (Win32Error)parser.ReadUInt32(); + } + + public byte[] GetBytes() + { + NDRWriter writer = new NDRWriter(); + writer.WriteStructure(InfoStruct); + writer.WriteUInt32((uint)Result); + + return writer.GetBytes(); + } + } +} diff --git a/SMBLibrary/Services/ServerService/NetrShareEnumRequest.cs b/SMBLibrary/Services/ServerService/NetrShareEnumRequest.cs new file mode 100644 index 0000000..27eb734 --- /dev/null +++ b/SMBLibrary/Services/ServerService/NetrShareEnumRequest.cs @@ -0,0 +1,49 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// NetrShareEnum Request (opnum 15) + /// + public class NetrShareEnumRequest + { + public string ServerName; + public ShareEnum InfoStruct; + public uint PreferedMaximumLength; // Preferred maximum length, in bytes, of the returned data + public uint ResumeHandle; + + public NetrShareEnumRequest() + { + } + + public NetrShareEnumRequest(byte[] buffer) + { + NDRParser parser = new NDRParser(buffer); + ServerName = parser.ReadTopLevelUnicodeStringPointer(); + InfoStruct = new ShareEnum(parser); + PreferedMaximumLength = parser.ReadUInt32(); + ResumeHandle = parser.ReadUInt32(); + } + + public byte[] GetBytes() + { + NDRWriter writer = new NDRWriter(); + writer.WriteTopLevelUnicodeStringPointer(ServerName); + writer.WriteStructure(InfoStruct); + writer.WriteUInt32(PreferedMaximumLength); + writer.WriteUInt32(ResumeHandle); + + return writer.GetBytes(); + } + } +} diff --git a/SMBLibrary/Services/ServerService/NetrShareEnumResponse.cs b/SMBLibrary/Services/ServerService/NetrShareEnumResponse.cs new file mode 100644 index 0000000..1d925ba --- /dev/null +++ b/SMBLibrary/Services/ServerService/NetrShareEnumResponse.cs @@ -0,0 +1,49 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// NetrShareEnum Response (opnum 15) + /// + public class NetrShareEnumResponse + { + public ShareEnum InfoStruct; + public uint TotalEntries; // The total number of entries that could have been enumerated if the buffer had been big enough to hold all the entries + public uint ResumeHandle; + public Win32Error Result; + + public NetrShareEnumResponse() + { + } + + public NetrShareEnumResponse(byte[] buffer) + { + NDRParser parser = new NDRParser(buffer); + InfoStruct = new ShareEnum(parser); + TotalEntries = parser.ReadUInt32(); + ResumeHandle = parser.ReadUInt32(); + Result = (Win32Error)parser.ReadUInt32(); + } + + public byte[] GetBytes() + { + NDRWriter writer = new NDRWriter(); + writer.WriteStructure(InfoStruct); + writer.WriteUInt32(TotalEntries); + writer.WriteUInt32(ResumeHandle); + writer.WriteUInt32((uint)Result); + + return writer.GetBytes(); + } + } +} diff --git a/SMBLibrary/Services/ServerService/NetrShareGetInfoRequest.cs b/SMBLibrary/Services/ServerService/NetrShareGetInfoRequest.cs new file mode 100644 index 0000000..1233f29 --- /dev/null +++ b/SMBLibrary/Services/ServerService/NetrShareGetInfoRequest.cs @@ -0,0 +1,42 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// NetrShareGetInfo Request (opnum 16) + /// + public class NetrShareGetInfoRequest + { + public string ServerName; + public string NetName; // Share name + public uint Level; + + public NetrShareGetInfoRequest(byte[] buffer) + { + NDRParser parser = new NDRParser(buffer); + ServerName = parser.ReadTopLevelUnicodeStringPointer(); + NetName = parser.ReadUnicodeString(); + Level = parser.ReadUInt32(); + } + + public byte[] GetBytes() + { + NDRWriter writer = new NDRWriter(); + writer.WriteTopLevelUnicodeStringPointer(ServerName); + writer.WriteUnicodeString(NetName); + writer.WriteUInt32(Level); + + return writer.GetBytes(); + } + } +} diff --git a/SMBLibrary/Services/ServerService/NetrShareGetInfoResponse.cs b/SMBLibrary/Services/ServerService/NetrShareGetInfoResponse.cs new file mode 100644 index 0000000..03f5fc2 --- /dev/null +++ b/SMBLibrary/Services/ServerService/NetrShareGetInfoResponse.cs @@ -0,0 +1,43 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// NetrShareGetInfo Response (opnum 16) + /// + public class NetrShareGetInfoResponse + { + public ShareInfo InfoStruct; + public Win32Error Result; + + public NetrShareGetInfoResponse() + { + } + + public NetrShareGetInfoResponse(byte[] buffer) + { + NDRParser parser = new NDRParser(buffer); + InfoStruct = new ShareInfo(parser); + Result = (Win32Error)parser.ReadUInt32(); + } + + public byte[] GetBytes() + { + NDRWriter writer = new NDRWriter(); + writer.WriteStructure(InfoStruct); + writer.WriteUInt32((uint)Result); + + return writer.GetBytes(); + } + } +} diff --git a/SMBLibrary/Services/ServerService/ServerService.cs b/SMBLibrary/Services/ServerService/ServerService.cs new file mode 100644 index 0000000..a1b03f6 --- /dev/null +++ b/SMBLibrary/Services/ServerService/ServerService.cs @@ -0,0 +1,194 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] + /// + public class ServerService : RemoteService + { + public const int MaxPreferredLength = -1; // MAX_PREFERRED_LENGTH + + private PlatformName m_platformID; + private string m_serverName; + private uint m_verMajor; + private uint m_verMinor; + private ServerType m_serverType; + + private List m_shares; + + public ServerService(string serverName, List shares) + { + m_platformID = PlatformName.NT; + m_serverName = serverName; + m_verMajor = 5; + m_verMinor = 2; + m_serverType = ServerType.Workstation | ServerType.Server | ServerType.WindowsNT | ServerType.ServerNT | ServerType.MasterBrowser; + + m_shares = shares; + } + + + public override byte[] GetResponseBytes(ushort opNum, byte[] requestBytes) + { + switch (opNum) + { + case 15: + { + NetrShareEnumRequest request = new NetrShareEnumRequest(requestBytes); + NetrShareEnumResponse response = GetNetrShareEnumResponse(request); + return response.GetBytes(); + } + case 16: + { + NetrShareGetInfoRequest request = new NetrShareGetInfoRequest(requestBytes); + NetrShareGetInfoResponse response = GetNetrShareGetInfoResponse(request); + return response.GetBytes(); + } + case 21: + { + NetrServerGetInfoRequest request = new NetrServerGetInfoRequest(requestBytes); + NetrServerGetInfoResponse response = GetNetrWkstaGetInfoResponse(request); + return response.GetBytes(); + } + default: + throw new NotImplementedException(); + } + } + + public NetrShareEnumResponse GetNetrShareEnumResponse(NetrShareEnumRequest request) + { + NetrShareEnumResponse response = new NetrShareEnumResponse(); + if (request.InfoStruct.Level == 0) + { + // We ignore request.PreferedMaximumLength + ShareInfo0Container info = new ShareInfo0Container(); + foreach (string shareName in m_shares) + { + info.Add(new ShareInfo0Entry(shareName)); + } + response.InfoStruct = new ShareEnum(info); + response.TotalEntries = (uint)m_shares.Count; + } + else if (request.InfoStruct.Level == 1) + { + // We ignore request.PreferedMaximumLength + ShareInfo1Container info = new ShareInfo1Container(); + foreach (string shareName in m_shares) + { + info.Add(new ShareInfo1Entry(shareName, new ShareTypeExtended(ShareType.DiskDrive))); + } + response.InfoStruct = new ShareEnum(info); + response.TotalEntries = (uint)m_shares.Count; + } + else + { + throw new NotImplementedException(); + } + response.Result = Win32Error.ERROR_SUCCESS; + return response; + } + + public NetrShareGetInfoResponse GetNetrShareGetInfoResponse(NetrShareGetInfoRequest request) + { + int shareIndex = IndexOfShare(request.NetName); + + NetrShareGetInfoResponse response = new NetrShareGetInfoResponse(); + if (shareIndex == -1) + { + response.InfoStruct = new ShareInfo(request.Level); + response.Result = Win32Error.NERR_NetNameNotFound; + return response; + } + + if (request.Level == 0) + { + ShareInfo0Entry info = new ShareInfo0Entry(m_shares[shareIndex]); + response.InfoStruct = new ShareInfo(info); + } + else if (request.Level == 1) + { + ShareInfo1Entry info = new ShareInfo1Entry(m_shares[shareIndex], new ShareTypeExtended(ShareType.DiskDrive)); + response.InfoStruct = new ShareInfo(info); + } + else if (request.Level == 2) + { + ShareInfo2Entry info = new ShareInfo2Entry(m_shares[shareIndex], new ShareTypeExtended(ShareType.DiskDrive)); + response.InfoStruct = new ShareInfo(info); + } + else + { + throw new NotImplementedException(); + } + response.Result = Win32Error.ERROR_SUCCESS; + return response; + } + + public NetrServerGetInfoResponse GetNetrWkstaGetInfoResponse(NetrServerGetInfoRequest request) + { + NetrServerGetInfoResponse response = new NetrServerGetInfoResponse(); + if (request.Level == 100) + { + ServerInfo100 info = new ServerInfo100(); + info.PlatformID = m_platformID; + info.ServerName.Value = m_serverName; + response.InfoStruct = new ServerInfo(info); + } + else if (request.Level == 101) + { + ServerInfo101 info = new ServerInfo101(); + info.PlatformID = m_platformID; + info.ServerName.Value = m_serverName; + info.VerMajor = m_verMajor; + info.VerMinor = m_verMinor; + info.Type = m_serverType; + info.Comment.Value = String.Empty; + response.InfoStruct = new ServerInfo(info); + } + else + { + throw new NotImplementedException(); + } + response.Result = Win32Error.ERROR_SUCCESS; + return response; + } + + private int IndexOfShare(string shareName) + { + for (int index = 0; index < m_shares.Count; index++) + { + if (m_shares[index].Equals(shareName, StringComparison.InvariantCultureIgnoreCase)) + { + return index; + } + } + + return -1; + } + + public override Guid InterfaceGuid + { + get + { + return new Guid("4B324FC8-1670-01D3-1278-5A47BF6EE188"); + } + } + + public override string PipeName + { + get + { + return @"\srvsvc"; + } + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfo.cs b/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfo.cs new file mode 100644 index 0000000..cb2473b --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfo.cs @@ -0,0 +1,69 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] SERVER_INFO Union + /// + public class ServerInfo : INDRStructure + { + public uint Level; + public ServerInfoLevel Info; + + public ServerInfo() + { + } + + public ServerInfo(ServerInfoLevel info) + { + Level = info.Level; + Info = info; + } + + public ServerInfo(NDRParser parser) + { + Read(parser); + } + + public void Read(NDRParser parser) + { + parser.BeginStructure(); // SERVER_INFO Union + Level = parser.ReadUInt32(); + switch (Level) + { + case 100: + ServerInfo100 info100 = null; + parser.ReadEmbeddedStructureFullPointer(ref info100); + Info = info100; + break; + case 101: + ServerInfo101 info101 = null; + parser.ReadEmbeddedStructureFullPointer(ref info101); + Info = info101; + break; + default: + throw new NotImplementedException(); + } + ; + parser.EndStructure(); // SERVER_INFO Union + } + + public void Write(NDRWriter writer) + { + writer.BeginStructure(); // SERVER_INFO Union + writer.WriteUInt32(Level); + writer.WriteEmbeddedStructureFullPointer(Info); + writer.EndStructure(); // SERVER_INFO Union + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfo100.cs b/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfo100.cs new file mode 100644 index 0000000..403fa9a --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfo100.cs @@ -0,0 +1,60 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] SERVER_INFO_100 + /// + public class ServerInfo100 : ServerInfoLevel + { + public PlatformName PlatformID; + public NDRUnicodeString ServerName; + + public ServerInfo100() + { + ServerName = new NDRUnicodeString(); + } + + public ServerInfo100(NDRParser parser) + { + Read(parser); + } + + public override void Read(NDRParser parser) + { + // If an array, structure, or union embeds a pointer, the representation of the referent of the + // pointer is deferred to a position in the octet stream that follows the representation of the + // embedding construction + parser.BeginStructure(); + PlatformID = (PlatformName)parser.ReadUInt32(); + parser.ReadEmbeddedStructureFullPointer(ref ServerName); + parser.EndStructure(); + } + + public override void Write(NDRWriter writer) + { + writer.BeginStructure(); + writer.WriteUInt32((uint)PlatformID); + writer.WriteEmbeddedStructureFullPointer(ServerName); + writer.EndStructure(); + } + + public override uint Level + { + get + { + return 100; + } + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfo101.cs b/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfo101.cs new file mode 100644 index 0000000..2a994df --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfo101.cs @@ -0,0 +1,82 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] SERVER_INFO_101 + /// + public class ServerInfo101 : ServerInfoLevel + { + public PlatformName PlatformID; + public NDRUnicodeString ServerName; + public uint VerMajor; + public uint VerMinor; + public ServerType Type; + public NDRUnicodeString Comment; + + public ServerInfo101() + { + ServerName = new NDRUnicodeString(); + Comment = new NDRUnicodeString(); + } + + public ServerInfo101(NDRParser parser) + { + Read(parser); + } + + public override void Read(NDRParser parser) + { + // If an array, structure, or union embeds a pointer, the representation of the referent of the + // pointer is deferred to a position in the octet stream that follows the representation of the + // embedding construction + parser.BeginStructure(); + PlatformID = (PlatformName)parser.ReadUInt32(); + parser.ReadEmbeddedStructureFullPointer(ref ServerName); + VerMajor = parser.ReadUInt32(); + VerMinor = parser.ReadUInt32(); + Type = (ServerType)parser.ReadUInt32(); + parser.ReadEmbeddedStructureFullPointer(ref Comment); + parser.EndStructure(); + } + + public override void Write(NDRWriter writer) + { + writer.BeginStructure(); + writer.WriteUInt32((uint)PlatformID); + writer.WriteEmbeddedStructureFullPointer(ServerName); + writer.WriteUInt32(VerMajor); + writer.WriteUInt32(VerMinor); + writer.WriteUInt32((uint)Type); + writer.WriteEmbeddedStructureFullPointer(Comment); + writer.EndStructure(); + } + + /* + public static ServerInfo101 ReadServerInfo101Pointer(NDRParser parser) + { + uint referentID = parser.ReadUInt32(); // ServerInfoLevel pointer + ServerInfo101 info = new ServerInfo101(parser); + parser.AddReferentInstance(referentID, info); + return info; + }*/ + + public override uint Level + { + get + { + return 101; + } + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfoLevel.cs b/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfoLevel.cs new file mode 100644 index 0000000..0bd5d76 --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ServerInfo/ServerInfoLevel.cs @@ -0,0 +1,26 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + public abstract class ServerInfoLevel : INDRStructure + { + public abstract void Read(NDRParser parser); + + public abstract void Write(NDRWriter writer); + + public abstract uint Level + { + get; + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ShareInfo/IShareInfoContainer.cs b/SMBLibrary/Services/ServerService/Structures/ShareInfo/IShareInfoContainer.cs new file mode 100644 index 0000000..bd026e6 --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ShareInfo/IShareInfoContainer.cs @@ -0,0 +1,22 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + public interface IShareInfoContainer : INDRStructure + { + uint Level + { + get; + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ShareInfo/IShareInfoEntry.cs b/SMBLibrary/Services/ServerService/Structures/ShareInfo/IShareInfoEntry.cs new file mode 100644 index 0000000..0f4c8d2 --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ShareInfo/IShareInfoEntry.cs @@ -0,0 +1,22 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + public interface IShareInfoEntry : INDRStructure + { + uint Level + { + get; + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareEnum.cs b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareEnum.cs new file mode 100644 index 0000000..169df0d --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareEnum.cs @@ -0,0 +1,82 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] SHARE_ENUM_STRUCT and the embedded SHARE_ENUM_UNION + /// + public class ShareEnum : INDRStructure + { + public uint Level; + public IShareInfoContainer Info; + + public ShareEnum() + { + } + + public ShareEnum(IShareInfoContainer info) + { + Level = info.Level; + Info = info; + } + + public ShareEnum(NDRParser parser) + { + Read(parser); + } + + public void Read(NDRParser parser) + { + parser.BeginStructure(); // SHARE_ENUM_STRUCT + Level = parser.ReadUInt32(); + parser.BeginStructure(); // SHARE_ENUM_UNION + // 14.3.8 - For a non-encapsulated union, the discriminant is marshalled into the transmitted data stream twice. + // once as the field or parameter, which is referenced by the switch_is construct, in the procedure argument list; + // and once as the first part of the union representation. + uint level = parser.ReadUInt32(); + switch (level) + { + case 0: + ShareInfo0Container info0 = null; + parser.ReadEmbeddedStructureFullPointer(ref info0); + Info = info0; + break; + case 1: + ShareInfo1Container info1 = null; + parser.ReadEmbeddedStructureFullPointer(ref info1); + Info = info1; + break; + default: + throw new NotImplementedException(); + } + parser.EndStructure(); // SHARE_ENUM_UNION + parser.EndStructure(); // SHARE_ENUM_STRUCT + } + + public void Write(NDRWriter writer) + { + if (Info != null && Level != Info.Level) + { + throw new ArgumentException("Invalid SHARE_ENUM_STRUCT Level"); + } + + writer.BeginStructure(); // SHARE_ENUM_STRUCT + writer.WriteUInt32(Level); + writer.BeginStructure(); // SHARE_ENUM_UNION + writer.WriteUInt32(Info.Level); + writer.WriteEmbeddedStructureFullPointer(Info); + writer.EndStructure(); // SHARE_ENUM_UNION + writer.EndStructure(); // SHARE_ENUM_STRUCT + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo.cs b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo.cs new file mode 100644 index 0000000..5fd0269 --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo.cs @@ -0,0 +1,74 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] SHARE_INFO Union + /// + public class ShareInfo : INDRStructure + { + public uint Level; + public IShareInfoEntry Info; + + public ShareInfo() + { + } + + public ShareInfo(uint level) + { + Level = level; + } + + public ShareInfo(IShareInfoEntry info) + { + Level = info.Level; + Info = info; + } + + public ShareInfo(NDRParser parser) + { + Read(parser); + } + + public void Read(NDRParser parser) + { + parser.BeginStructure(); // SHARE_INFO Union + Level = parser.ReadUInt32(); + switch (Level) + { + case 100: + ShareInfo0Entry info0 = null; + parser.ReadEmbeddedStructureFullPointer(ref info0); + Info = info0; + break; + case 101: + ShareInfo1Entry info1 = null; + parser.ReadEmbeddedStructureFullPointer(ref info1); + Info = info1; + break; + default: + throw new NotImplementedException(); + } + ; + parser.EndStructure(); // SHARE_INFO Union + } + + public void Write(NDRWriter writer) + { + writer.BeginStructure(); // SHARE_INFO Union + writer.WriteUInt32(Level); + writer.WriteEmbeddedStructureFullPointer(Info); + writer.EndStructure(); // SHARE_INFO Union + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo0Container.cs b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo0Container.cs new file mode 100644 index 0000000..c74cf39 --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo0Container.cs @@ -0,0 +1,75 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] SHARE_INFO_0_CONTAINER + /// + public class ShareInfo0Container : IShareInfoContainer + { + public NDRConformantArray Entries = new NDRConformantArray(); + + public ShareInfo0Container() + { + } + + public ShareInfo0Container(NDRParser parser) + { + Read(parser); + } + + public void Read(NDRParser parser) + { + parser.BeginStructure(); + uint count = parser.ReadUInt32(); + parser.ReadEmbeddedStructureFullPointer>(ref Entries); + parser.EndStructure(); + } + + public void Write(NDRWriter writer) + { + writer.BeginStructure(); + writer.WriteUInt32((uint)this.Count); + writer.WriteEmbeddedStructureFullPointer(Entries); + writer.EndStructure(); + } + + public uint Level + { + get + { + return 0; + } + } + + public int Count + { + get + { + if (Entries != null) + { + return Entries.Count; + } + else + { + return 0; + } + } + } + + public void Add(ShareInfo0Entry entry) + { + Entries.Add(entry); + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo0Entry.cs b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo0Entry.cs new file mode 100644 index 0000000..29e77f8 --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo0Entry.cs @@ -0,0 +1,58 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] SHARE_INFO_0 + /// + public class ShareInfo0Entry : IShareInfoEntry + { + public NDRUnicodeString NetName; + + public ShareInfo0Entry() + { + } + + public ShareInfo0Entry(string shareName) + { + NetName = new NDRUnicodeString(shareName); + } + + public ShareInfo0Entry(NDRParser parser) + { + Read(parser); + } + + public void Read(NDRParser parser) + { + parser.BeginStructure(); + parser.ReadEmbeddedStructureFullPointer(ref NetName); + parser.EndStructure(); + } + + public void Write(NDRWriter writer) + { + writer.BeginStructure(); + writer.WriteEmbeddedStructureFullPointer(NetName); + writer.EndStructure(); + } + + public uint Level + { + get + { + return 0; + } + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo1Container.cs b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo1Container.cs new file mode 100644 index 0000000..491ac85 --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo1Container.cs @@ -0,0 +1,75 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] SHARE_INFO_1_CONTAINER + /// + public class ShareInfo1Container : IShareInfoContainer + { + public NDRConformantArray Entries = new NDRConformantArray(); + + public ShareInfo1Container() + { + } + + public ShareInfo1Container(NDRParser parser) + { + Read(parser); + } + + public void Read(NDRParser parser) + { + parser.BeginStructure(); + uint count = parser.ReadUInt32(); + parser.ReadEmbeddedStructureFullPointer>(ref Entries); + parser.EndStructure(); + } + + public void Write(NDRWriter writer) + { + writer.BeginStructure(); + writer.WriteUInt32((uint)this.Count); + writer.WriteEmbeddedStructureFullPointer(Entries); + writer.EndStructure(); + } + + public uint Level + { + get + { + return 1; + } + } + + public int Count + { + get + { + if (Entries != null) + { + return Entries.Count; + } + else + { + return 0; + } + } + } + + public void Add(ShareInfo1Entry entry) + { + Entries.Add(entry); + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo1Entry.cs b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo1Entry.cs new file mode 100644 index 0000000..6eb0495 --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo1Entry.cs @@ -0,0 +1,66 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] SHARE_INFO_1 + /// + public class ShareInfo1Entry : IShareInfoEntry + { + public NDRUnicodeString NetName; + public ShareTypeExtended ShareType; + public NDRUnicodeString Remark; + + public ShareInfo1Entry() + { + } + + public ShareInfo1Entry(string shareName, ShareTypeExtended shareType) + { + NetName = new NDRUnicodeString(shareName); + ShareType = shareType; + Remark = new NDRUnicodeString(String.Empty); + } + + public ShareInfo1Entry(NDRParser parser) + { + Read(parser); + } + + public void Read(NDRParser parser) + { + parser.BeginStructure(); + parser.ReadEmbeddedStructureFullPointer(ref NetName); + ShareType = new ShareTypeExtended(parser); + parser.ReadEmbeddedStructureFullPointer(ref Remark); + parser.EndStructure(); + } + + public void Write(NDRWriter writer) + { + writer.BeginStructure(); + writer.WriteEmbeddedStructureFullPointer(NetName); + ShareType.Write(writer); + writer.WriteEmbeddedStructureFullPointer(Remark); + writer.EndStructure(); + } + + public uint Level + { + get + { + return 1; + } + } + } +} diff --git a/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo2Entry.cs b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo2Entry.cs new file mode 100644 index 0000000..722167c --- /dev/null +++ b/SMBLibrary/Services/ServerService/Structures/ShareInfo/ShareInfo2Entry.cs @@ -0,0 +1,87 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + /// + /// [MS-SRVS] SHARE_INFO_2 + /// + public class ShareInfo2Entry : IShareInfoEntry + { + public const uint UnlimitedConnections = 0xFFFFFFFF; + + public NDRUnicodeString NetName; + public ShareTypeExtended ShareType; + public NDRUnicodeString Remark; + public Permissions Permissions; // Windows will leave this field empty (0) + public uint MaxUses; // Maximum number of concurrent connections that the shared resource can accommodate. + public uint CurrentUses; // Number of current connections to the resource. + public NDRUnicodeString Path; // Windows will set this field to the on-disk path (.e.g 'D:\Shared') + public NDRUnicodeString Password; // Windows will set it to null + + public ShareInfo2Entry() + { + } + + public ShareInfo2Entry(string shareName, ShareTypeExtended shareType) + { + NetName = new NDRUnicodeString(shareName); + ShareType = shareType; + Remark = new NDRUnicodeString(String.Empty); + + MaxUses = UnlimitedConnections; + Path = new NDRUnicodeString(String.Empty); + Password = null; + } + + public ShareInfo2Entry(NDRParser parser) + { + Read(parser); + } + + public void Read(NDRParser parser) + { + parser.BeginStructure(); + parser.ReadEmbeddedStructureFullPointer(ref NetName); + ShareType = new ShareTypeExtended(parser); + parser.ReadEmbeddedStructureFullPointer(ref Remark); + Permissions = (Permissions)parser.ReadUInt32(); + MaxUses = parser.ReadUInt32(); + CurrentUses = parser.ReadUInt32(); + parser.ReadEmbeddedStructureFullPointer(ref Path); + parser.ReadEmbeddedStructureFullPointer(ref Password); + parser.EndStructure(); + } + + public void Write(NDRWriter writer) + { + writer.BeginStructure(); + writer.WriteEmbeddedStructureFullPointer(NetName); + ShareType.Write(writer); + writer.WriteEmbeddedStructureFullPointer(Remark); + writer.WriteUInt32((uint)Permissions); + writer.WriteUInt32(MaxUses); + writer.WriteUInt32(CurrentUses); + writer.WriteEmbeddedStructureFullPointer(Path); + writer.WriteEmbeddedStructureFullPointer(Password); + writer.EndStructure(); + } + + public uint Level + { + get + { + return 2; + } + } + } +} diff --git a/SMBLibrary/Services/WorkstationService/NetrWkstaGetInfoRequest.cs b/SMBLibrary/Services/WorkstationService/NetrWkstaGetInfoRequest.cs new file mode 100644 index 0000000..ea883a6 --- /dev/null +++ b/SMBLibrary/Services/WorkstationService/NetrWkstaGetInfoRequest.cs @@ -0,0 +1,45 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Utilities; +using SMBLibrary.RPC; + +namespace SMBLibrary.Services +{ + /// + /// NetrWkstaGetInfo Request (opnum 0) + /// + public class NetrWkstaGetInfoRequest + { + public string ServerName; + public uint Level; + + public NetrWkstaGetInfoRequest() + { + + } + + public NetrWkstaGetInfoRequest(byte[] buffer) + { + NDRParser parser = new NDRParser(buffer); + ServerName = parser.ReadTopLevelUnicodeStringPointer(); + Level = parser.ReadUInt32(); + } + + public byte[] GetBytes() + { + NDRWriter writer = new NDRWriter(); + writer.WriteTopLevelUnicodeStringPointer(ServerName); + writer.WriteUInt32(Level); + + return writer.GetBytes(); + } + } +} diff --git a/SMBLibrary/Services/WorkstationService/NetrWkstaGetInfoResponse.cs b/SMBLibrary/Services/WorkstationService/NetrWkstaGetInfoResponse.cs new file mode 100644 index 0000000..8eb8df5 --- /dev/null +++ b/SMBLibrary/Services/WorkstationService/NetrWkstaGetInfoResponse.cs @@ -0,0 +1,40 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; + +namespace SMBLibrary.Services +{ + public class NetrWkstaGetInfoResponse + { + public WorkstationInfo WkstaInfo; + public Win32Error Result; + + public NetrWkstaGetInfoResponse() + { + } + + public NetrWkstaGetInfoResponse(byte[] buffer) + { + NDRParser parser = new NDRParser(buffer); + WkstaInfo = new WorkstationInfo(parser); + // 14.4 - If an operation returns a result, the representation of the result appears after all parameters in + Result = (Win32Error)parser.ReadUInt32(); + } + + public byte[] GetBytes() + { + NDRWriter writer = new NDRWriter(); + writer.WriteStructure(WkstaInfo); + writer.WriteUInt32((uint)Result); + + return writer.GetBytes(); + } + } +} diff --git a/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfo.cs b/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfo.cs new file mode 100644 index 0000000..921cd9a --- /dev/null +++ b/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfo.cs @@ -0,0 +1,72 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; + +namespace SMBLibrary.Services +{ + /// + /// [MS-WKST] WKSTA_INFO Union + /// + public class WorkstationInfo : INDRStructure + { + public uint Level; + public WorkstationInfoLevel Info; + + public WorkstationInfo() + { + } + + public WorkstationInfo(WorkstationInfoLevel info) + { + Level = info.Level; + Info = info; + } + + public WorkstationInfo(NDRParser parser) + { + Read(parser); + } + + public void Read(NDRParser parser) + { + parser.BeginStructure(); + Level = parser.ReadUInt32(); + switch (Level) + { + case 100: + WorkstationInfo100 info100 = null; + parser.ReadEmbeddedStructureFullPointer(ref info100); + Info = info100; + break; + case 101: + WorkstationInfo101 info101 = null; + parser.ReadEmbeddedStructureFullPointer(ref info101); + Info = info101; + break; + default: + throw new NotImplementedException(); + } + parser.EndStructure(); + } + + public void Write(NDRWriter writer) + { + if (Info != null && Level != Info.Level) + { + throw new ArgumentException("Invalid WKSTA_INFO Level"); + } + + writer.BeginStructure(); + writer.WriteUInt32(Level); + writer.WriteEmbeddedStructureFullPointer(Info); + writer.EndStructure(); + } + } +} diff --git a/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfo100.cs b/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfo100.cs new file mode 100644 index 0000000..f0a83c0 --- /dev/null +++ b/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfo100.cs @@ -0,0 +1,70 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using SMBLibrary.RPC; + +namespace SMBLibrary.Services +{ + /// + /// [MS-WKST] WKSTA_INFO_100 + /// + public class WorkstationInfo100 : WorkstationInfoLevel + { + public uint PlatformID; + public NDRUnicodeString ComputerName; + public NDRUnicodeString LanGroup; + public uint VerMajor; + public uint VerMinor; + + public WorkstationInfo100() + { + ComputerName = new NDRUnicodeString(); + LanGroup = new NDRUnicodeString(); + } + + public WorkstationInfo100(NDRParser parser) + { + Read(parser); + } + + public override void Read(NDRParser parser) + { + // If an array, structure, or union embeds a pointer, the representation of the referent of the + // pointer is deferred to a position in the octet stream that follows the representation of the + // embedding construction + parser.BeginStructure(); + PlatformID = parser.ReadUInt32(); + parser.ReadEmbeddedStructureFullPointer(ref ComputerName); + parser.ReadEmbeddedStructureFullPointer(ref LanGroup); + VerMajor = parser.ReadUInt32(); + VerMinor = parser.ReadUInt32(); + parser.EndStructure(); + } + + public override void Write(NDRWriter writer) + { + writer.BeginStructure(); + writer.WriteUInt32(PlatformID); + writer.WriteEmbeddedStructureFullPointer(ComputerName); + writer.WriteEmbeddedStructureFullPointer(LanGroup); + writer.WriteUInt32(VerMajor); + writer.WriteUInt32(VerMinor); + writer.EndStructure(); + } + + public override uint Level + { + get + { + return 100; + } + } + } +} diff --git a/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfo101.cs b/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfo101.cs new file mode 100644 index 0000000..7716cf3 --- /dev/null +++ b/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfo101.cs @@ -0,0 +1,70 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; + +namespace SMBLibrary.Services +{ + /// + /// [MS-WKST] WKSTA_INFO_101 + /// + public class WorkstationInfo101 : WorkstationInfoLevel + { + public uint PlatformID; + public NDRUnicodeString ComputerName; + public NDRUnicodeString LanGroup; + public uint VerMajor; + public uint VerMinor; + public NDRUnicodeString LanRoot; + + public WorkstationInfo101() + { + ComputerName = new NDRUnicodeString(); + LanGroup = new NDRUnicodeString(); + LanRoot = new NDRUnicodeString(); + } + + public WorkstationInfo101(NDRParser parser) + { + Read(parser); + } + + public override void Read(NDRParser parser) + { + parser.BeginStructure(); + PlatformID = parser.ReadUInt32(); + parser.ReadEmbeddedStructureFullPointer(ref ComputerName); + parser.ReadEmbeddedStructureFullPointer(ref LanGroup); + VerMajor = parser.ReadUInt32(); + VerMinor = parser.ReadUInt32(); + parser.ReadEmbeddedStructureFullPointer(ref LanRoot); + parser.EndStructure(); + } + + public override void Write(NDRWriter writer) + { + writer.BeginStructure(); + writer.WriteUInt32(PlatformID); + writer.WriteEmbeddedStructureFullPointer(ComputerName); + writer.WriteEmbeddedStructureFullPointer(LanGroup); + writer.WriteUInt32(VerMajor); + writer.WriteUInt32(VerMinor); + writer.WriteEmbeddedStructureFullPointer(LanRoot); + writer.EndStructure(); + } + + public override uint Level + { + get + { + return 101; + } + } + } +} diff --git a/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfoLevel.cs b/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfoLevel.cs new file mode 100644 index 0000000..811cc0b --- /dev/null +++ b/SMBLibrary/Services/WorkstationService/Structures/WorkstationInfoLevel.cs @@ -0,0 +1,26 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using Utilities; + +namespace SMBLibrary.Services +{ + public abstract class WorkstationInfoLevel : INDRStructure + { + public abstract void Read(NDRParser parser); + + public abstract void Write(NDRWriter writer); + + public abstract uint Level + { + get; + } + } +} diff --git a/SMBLibrary/Services/WorkstationService/WorkstationService.cs b/SMBLibrary/Services/WorkstationService/WorkstationService.cs new file mode 100644 index 0000000..ae6c739 --- /dev/null +++ b/SMBLibrary/Services/WorkstationService/WorkstationService.cs @@ -0,0 +1,94 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SMBLibrary.Services +{ + /// + /// [MS-WKST] + /// + public class WorkstationService : RemoteService + { + private uint m_platformID; + private string m_computerName; + private string m_lanGroup; + private uint m_verMajor; + private uint m_verMinor; + + public WorkstationService(string computerName, string lanGroup) + { + m_platformID = (uint)PlatformName.NT; + m_computerName = computerName; + m_lanGroup = lanGroup; + m_verMajor = 5; + m_verMinor = 2; + } + + public override byte[] GetResponseBytes(ushort opNum, byte[] requestBytes) + { + switch (opNum) + { + case 0: + NetrWkstaGetInfoRequest request = new NetrWkstaGetInfoRequest(requestBytes); + NetrWkstaGetInfoResponse response = GetNetrWkstaGetInfoResponse(request); + return response.GetBytes(); + default: + throw new NotImplementedException(); + } + } + + public NetrWkstaGetInfoResponse GetNetrWkstaGetInfoResponse(NetrWkstaGetInfoRequest request) + { + NetrWkstaGetInfoResponse response = new NetrWkstaGetInfoResponse(); + if (request.Level == 100) + { + WorkstationInfo100 info = new WorkstationInfo100(); + info.PlatformID = m_platformID; + info.ComputerName.Value = m_computerName; + info.LanGroup.Value = m_lanGroup; + info.VerMajor = m_verMajor; + info.VerMinor = m_verMinor; + response.WkstaInfo = new WorkstationInfo(info); + } + else if (request.Level == 101) + { + WorkstationInfo101 info = new WorkstationInfo101(); + info.PlatformID = m_platformID; + info.ComputerName.Value = m_computerName; + info.LanGroup.Value = m_lanGroup; + info.VerMajor = m_verMajor; + info.VerMinor = m_verMinor; + info.LanRoot.Value = m_lanGroup; + response.WkstaInfo = new WorkstationInfo(info); + } + else + { + throw new NotImplementedException(); + } + response.Result = Win32Error.ERROR_SUCCESS; + return response; + } + + public override Guid InterfaceGuid + { + get + { + return new Guid("6BFFD098-A112-3610-9833-46C3F87E345A"); + } + } + + public override string PipeName + { + get + { + return @"\wkssvc"; + } + } + } +} diff --git a/SMBLibrary/Structures/ACE/ACE.cs b/SMBLibrary/Structures/ACE/ACE.cs new file mode 100644 index 0000000..3dd32ab --- /dev/null +++ b/SMBLibrary/Structures/ACE/ACE.cs @@ -0,0 +1,36 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary +{ + /// + /// [MS-DTYP] ACE (Access Control Entry( + /// + public abstract class ACE + { + public abstract int Length + { + get; + } + + public static ACE GetAce(byte[] buffer, int offset) + { + AceType aceType = (AceType)ByteReader.ReadByte(buffer, offset + 0); + switch (aceType) + { + case AceType.ACCESS_ALLOWED_ACE_TYPE: + return new AccessAllowedACE(buffer, offset); + default: + throw new NotImplementedException(); + } + } + } +} diff --git a/SMBLibrary/Structures/ACE/AccessAllowedACE.cs b/SMBLibrary/Structures/ACE/AccessAllowedACE.cs new file mode 100644 index 0000000..9106ba1 --- /dev/null +++ b/SMBLibrary/Structures/ACE/AccessAllowedACE.cs @@ -0,0 +1,44 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary +{ + /// + /// [MS-DTYP] ACCESS_ALLOWED_ACE + /// + public class AccessAllowedACE : ACE + { + public AceHeader Header; + public AccessMask Mask; + public SID Sid; + + public AccessAllowedACE() + { + Header = new AceHeader(); + Header.AceType = AceType.ACCESS_ALLOWED_ACE_TYPE; + } + + public AccessAllowedACE(byte[] buffer, int offset) + { + Header = new AceHeader(buffer, offset + 0); + Mask = new AccessMask(buffer, offset + 4); + Sid = new SID(buffer, offset + 8); + } + + public override int Length + { + get + { + return 8 + Sid.Length; + } + } + } +} diff --git a/SMBLibrary/Structures/ACE/AceHeader.cs b/SMBLibrary/Structures/ACE/AceHeader.cs new file mode 100644 index 0000000..729b7c5 --- /dev/null +++ b/SMBLibrary/Structures/ACE/AceHeader.cs @@ -0,0 +1,35 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary +{ + /// + /// [MS-DTYP] ACE_HEADER + /// + public class AceHeader + { + public const int Length = 4; + + public AceType AceType; + public AceFlags AceFlags; + public ushort AceSize; + + public AceHeader() + { + } + public AceHeader(byte[] buffer, int offset) + { + AceType = (AceType)ByteReader.ReadByte(buffer, offset + 0); + AceFlags = (AceFlags)ByteReader.ReadByte(buffer, offset + 1); + AceSize = LittleEndianConverter.ToUInt16(buffer, offset + 2); + } + } +} diff --git a/SMBLibrary/Structures/ACE/Enums/AceFlags.cs b/SMBLibrary/Structures/ACE/Enums/AceFlags.cs new file mode 100644 index 0000000..f721f11 --- /dev/null +++ b/SMBLibrary/Structures/ACE/Enums/AceFlags.cs @@ -0,0 +1,16 @@ +using System; + +namespace SMBLibrary +{ + [Flags] + public enum AceFlags : byte + { + OBJECT_INHERIT_ACE = 0x01, + CONTAINER_INHERIT_ACE = 0x02, + NO_PROPAGATE_INHERIT_ACE = 0x04, + INHERIT_ONLY_ACE = 0x08, + INHERITED_ACE = 0x10, + SUCCESSFUL_ACCESS_ACE_FLAG = 0x40, + FAILED_ACCESS_ACE_FLAG = 0x80, + } +} diff --git a/SMBLibrary/Structures/ACE/Enums/AceType.cs b/SMBLibrary/Structures/ACE/Enums/AceType.cs new file mode 100644 index 0000000..ed7b1cc --- /dev/null +++ b/SMBLibrary/Structures/ACE/Enums/AceType.cs @@ -0,0 +1,27 @@ + +namespace SMBLibrary +{ + public enum AceType : byte + { + ACCESS_ALLOWED_ACE_TYPE = 0x00, + ACCESS_DENIED_ACE_TYPE = 0x01, + SYSTEM_AUDIT_ACE_TYPE = 0x02, + SYSTEM_ALARM_ACE_TYPE = 0x03, + ACCESS_ALLOWED_COMPOUND_ACE_TYPE = 0x04, + ACCESS_ALLOWED_OBJECT_ACE_TYPE = 0x05, + ACCESS_DENIED_OBJECT_ACE_TYPE = 0x06, + SYSTEM_AUDIT_OBJECT_ACE_TYPE = 0x07, + SYSTEM_ALARM_OBJECT_ACE_TYPE = 0x08, + ACCESS_ALLOWED_CALLBACK_ACE_TYPE = 0x09, + ACCESS_DENIED_CALLBACK_ACE_TYPE = 0x0A, + ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE = 0x0B, + ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE = 0x0C, + SYSTEM_AUDIT_CALLBACK_ACE_TYPE = 0x0D, + SYSTEM_ALARM_CALLBACK_ACE_TYPE = 0x0E, + SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE = 0x0F, + SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE = 0x10, + SYSTEM_MANDATORY_LABEL_ACE_TYPE = 0x11, + SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE = 0x12, + SYSTEM_SCOPED_POLICY_ID_ACE_TYPE = 0x13, + } +} diff --git a/SMBLibrary/Structures/ACL.cs b/SMBLibrary/Structures/ACL.cs new file mode 100644 index 0000000..a75f09e --- /dev/null +++ b/SMBLibrary/Structures/ACL.cs @@ -0,0 +1,59 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary +{ + /// + /// [MS-DTYP] ACL (Access Control List) + /// + public class ACL : List + { + public byte AclRevision; + public byte Sbz1; + //ushort AclSize; + //ushort AceCount; + public ushort Sbz2; + + public ACL() + { + } + + public ACL(byte[] buffer, int offset) + { + AclRevision = ByteReader.ReadByte(buffer, offset + 0); + Sbz1 = ByteReader.ReadByte(buffer, offset + 1); + ushort AclSize = LittleEndianConverter.ToUInt16(buffer, offset + 2); + ushort AceCount = LittleEndianConverter.ToUInt16(buffer, offset + 4); + Sbz2 = LittleEndianConverter.ToUInt16(buffer, offset + 6); + + offset += 8; + for (int index = 0; index < AceCount; index++) + { + ACE ace = ACE.GetAce(buffer, offset); + this.Add(ace); + offset += ace.Length; + } + } + + public void WriteBytes(byte[] buffer, ref int offset) + { + throw new NotImplementedException(); + } + + public int Length + { + get + { + throw new NotImplementedException(); + } + } + } +} diff --git a/SMBLibrary/Structures/SID.cs b/SMBLibrary/Structures/SID.cs new file mode 100644 index 0000000..e987f4a --- /dev/null +++ b/SMBLibrary/Structures/SID.cs @@ -0,0 +1,62 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary +{ + /// + /// [MS-DTYP] 2.4.2.2 - SID (Packet Representation) + /// + public class SID + { + public byte Revision; + //byte SubAuthorityCount; + public byte[] IdentifierAuthority; + public List SubAuthority = new List(); + + public SID() + { + Revision = 0x01; + IdentifierAuthority = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 }; + } + + public SID(byte[] buffer, int offset) + { + Revision = ByteReader.ReadByte(buffer, ref offset); + byte subAuthorityCount = ByteReader.ReadByte(buffer, ref offset); + IdentifierAuthority = ByteReader.ReadBytes(buffer, ref offset, 6); + for (int index = 0; index < subAuthorityCount; index++) + { + uint entry = LittleEndianReader.ReadUInt32(buffer, ref offset); + SubAuthority.Add(entry); + } + } + + public void WriteBytes(byte[] buffer, ref int offset) + { + byte subAuthorityCount = (byte)SubAuthority.Count; + ByteWriter.WriteByte(buffer, ref offset, Revision); + ByteWriter.WriteByte(buffer, ref offset, subAuthorityCount); + ByteWriter.WriteBytes(buffer, ref offset, IdentifierAuthority, 6); + for (int index = 0; index < SubAuthority.Count; index++) + { + LittleEndianWriter.WriteUInt32(buffer, ref offset, SubAuthority[index]); + } + } + + public int Length + { + get + { + return 8 + SubAuthority.Count * 4; + } + } + } +} diff --git a/SMBLibrary/Structures/SecurityDescriptor.cs b/SMBLibrary/Structures/SecurityDescriptor.cs new file mode 100644 index 0000000..36f5bf9 --- /dev/null +++ b/SMBLibrary/Structures/SecurityDescriptor.cs @@ -0,0 +1,71 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; + +namespace SMBLibrary +{ + /// + /// [MS-DTYP] SECURITY_DESCRIPTOR + /// + public class SecurityDescriptor + { + public byte Revision; + public byte Sbz1; + public ushort Control; + //uint OffsetOwner; + //uint OffsetGroup; + //uint OffsetSacl; + //uint OffsetDacl; + public SID OwnerSid; + public SID GroupSid; + public ACL Sacl; + public ACL Dacl; + + public SecurityDescriptor() + { + Revision = 0x01; + } + + public SecurityDescriptor(byte[] buffer, int offset) + { + Revision = ByteReader.ReadByte(buffer, ref offset); + Sbz1 = ByteReader.ReadByte(buffer, ref offset); + Control = LittleEndianReader.ReadUInt16(buffer, ref offset); + uint offsetOwner = LittleEndianReader.ReadUInt32(buffer, ref offset); + uint offsetGroup = LittleEndianReader.ReadUInt32(buffer, ref offset); + uint offsetSacl = LittleEndianReader.ReadUInt32(buffer, ref offset); + uint offsetDacl = LittleEndianReader.ReadUInt32(buffer, ref offset); + if (offsetOwner != 0) + { + OwnerSid = new SID(buffer, (int)offsetOwner); + } + + if (offsetGroup != 0) + { + GroupSid = new SID(buffer, (int)offsetGroup); + } + + if (offsetSacl != 0) + { + Sacl = new ACL(buffer, (int)offsetSacl); + } + + if (offsetDacl != 0) + { + Dacl = new ACL(buffer, (int)offsetDacl); + } + } + + public byte[] GetBytes() + { + throw new NotImplementedException(); + } + } +} diff --git a/SMBLibrary/Tests/AuthenticationTests.cs b/SMBLibrary/Tests/AuthenticationTests.cs new file mode 100644 index 0000000..0ec441c --- /dev/null +++ b/SMBLibrary/Tests/AuthenticationTests.cs @@ -0,0 +1,170 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using Utilities; +using SMBLibrary.Authentication; + +namespace SMBLibrary +{ + /// + /// [MS-NLMP] Tests + /// + public class AuthenticationTests + { + public static bool LMv1HashTest() + { + byte[] hash = NTAuthentication.LMOWFv1("Password"); + byte[] expected = new byte[] { 0xe5, 0x2c, 0xac, 0x67, 0x41, 0x9a, 0x9a, 0x22, 0x4a, 0x3b, 0x10, 0x8f, 0x3f, 0xa6, 0xcb, 0x6d }; + bool success = ByteUtils.AreByteArraysEqual(hash, expected); + return success; + } + + public static bool NTv1HashTest() + { + byte[] hash = NTAuthentication.NTOWFv1("Password"); + byte[] expected = new byte[] { 0xa4, 0xf4, 0x9c, 0x40, 0x65, 0x10, 0xbd, 0xca, 0xb6, 0x82, 0x4e, 0xe7, 0xc3, 0x0f, 0xd8, 0x52 }; + bool success = ByteUtils.AreByteArraysEqual(hash, expected); + return success; + } + + public static bool NTv2HashTest() + { + byte[] hash = NTAuthentication.NTOWFv2("Password", "User", "Domain"); + byte[] expected = new byte[] { 0x0c, 0x86, 0x8a, 0x40, 0x3b, 0xfd, 0x7a, 0x93, 0xa3, 0x00, 0x1e, 0xf2, 0x2e, 0xf0, 0x2e, 0x3f }; + bool success = ByteUtils.AreByteArraysEqual(hash, expected); + return success; + } + + public static bool LMv1ResponseTest() + { + byte[] challenge = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; + byte[] response = NTAuthentication.ComputeLMv1Response(challenge, "Password"); + byte[] expected = { 0x98, 0xde, 0xf7, 0xb8, 0x7f, 0x88, 0xaa, 0x5d, 0xaf, 0xe2, 0xdf, 0x77, 0x96, 0x88, 0xa1, 0x72, 0xde, 0xf1, 0x1c, 0x7d, 0x5c, 0xcd, 0xef, 0x13 }; + bool success = ByteUtils.AreByteArraysEqual(response, expected); + return success; + } + + public static bool NTLMv1ResponseTest() + { + byte[] challenge = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; + byte[] response = NTAuthentication.ComputeNTLMv1Response(challenge, "Password"); + byte[] expected = { 0x67, 0xc4, 0x30, 0x11, 0xf3, 0x02, 0x98, 0xa2, 0xad, 0x35, 0xec, 0xe6, 0x4f, 0x16, 0x33, 0x1c, 0x44, 0xbd, 0xbe, 0xd9, 0x27, 0x84, 0x1f, 0x94}; + bool success = ByteUtils.AreByteArraysEqual(response, expected); + return success; + } + + public static bool LMv2ResponseTest() + { + byte[] serverChallenge = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; + byte[] clientChallenge = new byte[] { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }; + byte[] response = NTAuthentication.ComputeLMv2Response(serverChallenge, clientChallenge, "Password", "User", "Domain"); + byte[] expected = new byte[] { 0x86, 0xc3, 0x50, 0x97, 0xac, 0x9c, 0xec, 0x10, 0x25, 0x54, 0x76, 0x4a, 0x57, 0xcc, 0xcc, 0x19, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}; + bool success = ByteUtils.AreByteArraysEqual(response, expected); + return success; + } + + public static bool NTLMv2ResponseTest() + { + byte[] serverChallenge = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; + byte[] clientChallenge = new byte[] { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }; + DateTime time = DateTime.FromFileTimeUtc(0); // same as new byte[8] + NTLMv2ClientChallengeStructure clientChallengeStructure = new NTLMv2ClientChallengeStructure(time, clientChallenge, "Domain", "Server"); + byte[] clientChallengeStructurePadded = clientChallengeStructure.GetBytesPadded(); + byte[] response = NTAuthentication.ComputeNTLMv2Response(serverChallenge, clientChallengeStructurePadded, "Password", "User", "Domain"); + + byte[] expectedHash = new byte[] { 0x68, 0xcd, 0x0a, 0xb8, 0x51, 0xe5, 0x1c, 0x96, 0xaa, 0xbc, 0x92, 0x7b, 0xeb, 0xef, 0x6a, 0x1c }; + byte[] expected = ByteUtils.Concatenate(expectedHash, clientChallengeStructurePadded); + bool success = ByteUtils.AreByteArraysEqual(response, expected); + return success; + } + + public static bool NTLMv2ChallengeMessageTest() + { + byte[] expected = new byte[]{0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x33, 0x82, 0x8a, 0xe2, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x24, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x70, 0x17, 0x00, 0x00, 0x00, 0x0f, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00}; + + byte[] serverChallenge = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; + ChallengeMessage message = new ChallengeMessage(); + message.ServerChallenge = serverChallenge; + message.Version = new Authentication.Version(6, 0, 6000, 15); + message.NegotiateFlags = NegotiateFlags.NegotiateUnicode | NegotiateFlags.NegotiateOEM | NegotiateFlags.NegotiateSign | NegotiateFlags.NegotiateSeal | NegotiateFlags.NegotiateNTLMKey | NegotiateFlags.NegotiateAlwaysSign | NegotiateFlags.NegotiateTargetTypeServer | NegotiateFlags.NegotiateExtendedSecurity | NegotiateFlags.NegotiateTargetInfo | NegotiateFlags.NegotiateVersion | NegotiateFlags.Negotiate128 | NegotiateFlags.NegotiateKeyExchange | NegotiateFlags.Negotiate56; + message.TargetName = "Server"; + byte[] serverAVPair = AVPairUtils.GetAVPairSequence("Domain", "Server"); + message.TargetInfo = serverAVPair; + + byte[] messageBytes = message.GetBytes(); + bool success = ByteUtils.AreByteArraysEqual(expected, messageBytes); + return success; + } + + public static bool NTLMv2AuthenticateMessageTest() + { + byte[] expected = new byte[] { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x54, 0x00, 0x54, 0x00, 0x84, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x54, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x35, 0x82, 0x88, 0xe2, + 0x05, 0x01, 0x28, 0x0a, 0x00, 0x00, 0x00, 0x0f, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x43, 0x00, 0x4f, 0x00, + 0x4d, 0x00, 0x50, 0x00, 0x55, 0x00, 0x54, 0x00, 0x45, 0x00, 0x52, 0x00, 0x86, 0xc3, 0x50, 0x97, + 0xac, 0x9c, 0xec, 0x10, 0x25, 0x54, 0x76, 0x4a, 0x57, 0xcc, 0xcc, 0x19, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0x68, 0xcd, 0x0a, 0xb8, 0x51, 0xe5, 0x1c, 0x96, 0xaa, 0xbc, 0x92, 0x7b, + 0xeb, 0xef, 0x6a, 0x1c, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, + 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xda, 0xd2, 0x54, 0x4f, 0xc9, 0x79, 0x90, + 0x94, 0xce, 0x1c, 0xe9, 0x0b, 0xc9, 0xd0, 0x3e}; + + AuthenticateMessage cmp = new AuthenticateMessage(expected); + + byte[] sessionKey = {0xc5, 0xda, 0xd2, 0x54, 0x4f, 0xc9, 0x79, 0x90, 0x94, 0xce, 0x1c, 0xe9, 0x0b, 0xc9, 0xd0, 0x3e}; + byte[] serverChallenge = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; + byte[] clientChallenge = new byte[] { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }; + byte[] serverAVPair = AVPairUtils.GetAVPairSequence("Domain", "Server"); + DateTime time = DateTime.FromFileTimeUtc(0); // same as new byte[8] + NTLMv2ClientChallengeStructure clientChallengeStructure = new NTLMv2ClientChallengeStructure(time, clientChallenge, "Domain", "Server"); + byte[] clientChallengeStructurePadded = clientChallengeStructure.GetBytesPadded(); + + AuthenticateMessage message = new AuthenticateMessage(); + message.EncryptedRandomSessionKey = sessionKey; + message.Version = new Authentication.Version(5, 1, 2600, Authentication.Version.NTLMSSP_REVISION_W2K3); + message.NegotiateFlags = NegotiateFlags.NegotiateUnicode | NegotiateFlags.RequestTarget | NegotiateFlags.NegotiateSign | NegotiateFlags.NegotiateSeal | NegotiateFlags.NegotiateNTLMKey | NegotiateFlags.NegotiateAlwaysSign | NegotiateFlags.NegotiateExtendedSecurity | NegotiateFlags.NegotiateTargetInfo | NegotiateFlags.NegotiateVersion | NegotiateFlags.Negotiate128 | NegotiateFlags.NegotiateKeyExchange | NegotiateFlags.Negotiate56; + message.DomainName = "Domain"; + message.WorkStation = "COMPUTER"; + message.UserName = "User"; + message.LmChallengeResponse = NTAuthentication.ComputeLMv2Response(serverChallenge, clientChallenge, "Password", "User", "Domain"); + message.NtChallengeResponse = NTAuthentication.ComputeNTLMv2Response(serverChallenge, clientChallengeStructurePadded, "Password", "User", "Domain"); + + byte[] messageBytes = message.GetBytes(); + // The payload entries may be distributed differently so we use cmp.GetBytes() + bool success = ByteUtils.AreByteArraysEqual(messageBytes, cmp.GetBytes()); + return success; + } + + public static bool TestAll() + { + bool success = (LMv1HashTest() && + NTv1HashTest() && + NTv2HashTest() && + LMv1ResponseTest() && + NTLMv1ResponseTest() && + LMv2ResponseTest() && + NTLMv2ResponseTest() && + NTLMv2ChallengeMessageTest() && + NTLMv2AuthenticateMessageTest()); + return success; + } + + } +} diff --git a/SMBLibrary/Tests/NetBiosTests.cs b/SMBLibrary/Tests/NetBiosTests.cs new file mode 100644 index 0000000..2abad18 --- /dev/null +++ b/SMBLibrary/Tests/NetBiosTests.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.NetBios; +using Utilities; + +namespace SMBLibrary +{ + public class NetBiosTests + { + public static void Test() + { + byte[] buffer = new byte[] { 0x20, 0x46, 0x47, 0x45, 0x4e, 0x44, 0x4a, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x00 }; + int offset = 0; + string name = NetBiosUtils.DecodeName(buffer, ref offset); + byte[] encodedName = NetBiosUtils.EncodeName(name, String.Empty); + bool success = ByteUtils.AreByteArraysEqual(buffer, encodedName); + } + + public static void Test2() + { + byte[] buffer = new byte[] { 0x20, 0x46, 0x47, 0x45, 0x4e, 0x44, 0x4a, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x41, 0x41, 0x00 }; + int offset = 0; + string name = NetBiosUtils.DecodeName(buffer, ref offset); + byte[] encodedName = NetBiosUtils.EncodeName(name, String.Empty); + bool success = ByteUtils.AreByteArraysEqual(buffer, encodedName); + } + } +} diff --git a/SMBLibrary/Tests/RPCTests.cs b/SMBLibrary/Tests/RPCTests.cs new file mode 100644 index 0000000..9660dd7 --- /dev/null +++ b/SMBLibrary/Tests/RPCTests.cs @@ -0,0 +1,89 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Text; +using SMBLibrary.RPC; +using SMBLibrary.Services; + +namespace SMBLibrary +{ + public class RPCTests + { + public static void RPCTest1() + { + byte[] buffer = new byte[]{ 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x54, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x32, 0x00, + 0x2d, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x37, 0x00, 0x00, 0x00, 0xcd, 0xab, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x4b, 0x00, + 0x47, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x55, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + NetrWkstaGetInfoResponse response = new NetrWkstaGetInfoResponse(buffer); + + byte[] responseBytes = response.GetBytes(); + } + + public static void RPCTest2() + { + byte[] buffer = new byte[] { 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x90, 0x84, 0x00, 0x08, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x31, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x35, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00 }; + NetrServerGetInfoResponse response = new NetrServerGetInfoResponse(buffer); + + byte[] responseBytes = response.GetBytes(); + } + + public static void RPCTest3() + { + byte[] buffer = new byte[] {0x00, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, + 0x38, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x35, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}; + NetrShareEnumRequest request = new NetrShareEnumRequest(buffer); + + byte[] requestBytes = request.GetBytes(); + } + + public static void RPCTest4() + { + byte[] buffer = new byte[] {0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x0c, 0x00, 0x02, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, + 0x18, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x80, 0x1c, 0x00, 0x02, 0x00, 0x20, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x24, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x43, 0x00, 0x24, 0x00, 0x00, 0x00, 0x69, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x44, 0x00, 0x65, 0x00, 0x66, 0x00, 0x61, 0x00, + 0x75, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x20, 0x00, 0x73, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x00, 0x00, 0x41, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, + 0x43, 0x00, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x52, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x20, 0x00, 0x49, 0x00, 0x50, 0x00, 0x43, 0x00, 0x00, 0x00, 0x68, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x44, 0x00, 0x4d, 0x00, 0x49, 0x00, + 0x4e, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x52, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x20, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + NetrShareEnumResponse response = new NetrShareEnumResponse(buffer); + + byte[] responseBytes = response.GetBytes(); + } + + public static void RPCTest5() + { + byte[] buffer = new byte[] {0x00, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x5c, 0x00, 0x31, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, + 0x38, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x35, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x53, 0x00, 0x68, 0x00, + 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x00, 0x00, 0xb7, 0x6c, 0x02, 0x00, 0x00, 0x00}; + NetrShareGetInfoRequest request = new NetrShareGetInfoRequest(buffer); + + byte[] requestBytes = request.GetBytes(); + } + } +} diff --git a/SMBLibrary/Utilities/PrefetchedStream.cs b/SMBLibrary/Utilities/PrefetchedStream.cs new file mode 100644 index 0000000..26d05aa --- /dev/null +++ b/SMBLibrary/Utilities/PrefetchedStream.cs @@ -0,0 +1,145 @@ +/* Copyright (C) 2016 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; + +namespace Utilities +{ + public class PrefetchedStream : Stream + { + public const int CacheSize = 1048576; // 1 MB + + private long m_cacheOffset; + private byte[] m_cache = new byte[0]; + + private Stream m_stream; + private object m_syncLock = new object(); + + public PrefetchedStream(Stream stream) + { + m_stream = stream; + new Thread(delegate() + { + lock (m_syncLock) + { + m_cacheOffset = 0; + m_cache = new byte[CacheSize]; + int bytesRead = m_stream.Read(m_cache, 0, CacheSize); + System.Diagnostics.Debug.Print("[{0}] bytes read {1}", DateTime.Now.ToString("HH:mm:ss:ffff"), bytesRead); + this.Position = 0; + if (bytesRead < CacheSize) + { + // EOF, we must trim the response data array + m_cache = ByteReader.ReadBytes(m_cache, 0, bytesRead); + } + } + }).Start(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + long position; + lock (m_syncLock) + { + position = this.Position; + bool isInCache = (position >= m_cacheOffset) && (position + count <= m_cacheOffset + m_cache.Length); + if (!isInCache) + { + m_cacheOffset = position; + int cacheSize = Math.Max(CacheSize, count); + m_cache = new byte[cacheSize]; + int bytesRead = m_stream.Read(m_cache, 0, cacheSize); + + if (bytesRead < cacheSize) + { + // EOF, we must trim the response data array + m_cache = ByteReader.ReadBytes(m_cache, 0, bytesRead); + } + } + } + + int offsetInCache = (int)(position - m_cacheOffset); + int bytesRemained = m_cache.Length - offsetInCache; + int dataLength = Math.Min(count, bytesRemained); + + Array.Copy(m_cache, offsetInCache, buffer, offset, dataLength); + lock (m_syncLock) + { + this.Position = position + dataLength; + } + return dataLength; + } + + public override void Write(byte[] buffer, int offset, int count) + { + m_cache = new byte[0]; + m_stream.Write(buffer, offset, count); + } + + public override bool CanRead + { + get + { + return m_stream.CanRead; + } + } + + public override bool CanSeek + { + get + { + return m_stream.CanSeek; + } + } + + public override bool CanWrite + { + get + { + return m_stream.CanWrite; + } + } + + public override long Length + { + get + { + return m_stream.Length; + } + } + + public override long Position + { + get + { + return m_stream.Position; + } + set + { + m_stream.Position = value; + } + } + + public override void Flush() + { + m_stream.Flush(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return m_stream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + m_stream.SetLength(value); + } + } +} diff --git a/SMBLibrary/Utilities/SocketUtils.cs b/SMBLibrary/Utilities/SocketUtils.cs new file mode 100644 index 0000000..3231c21 --- /dev/null +++ b/SMBLibrary/Utilities/SocketUtils.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace Utilities +{ + public class SocketUtils + { + /// + /// Socket will be forcefully closed, all pending data will be ignored, and socket will be deallocated. + /// + public static void ReleaseSocket(Socket socket) + { + if (socket != null) + { + if (socket.Connected) + { + socket.Shutdown(SocketShutdown.Both); + try + { + socket.Disconnect(false); + } + catch (SocketException) + { } + } + socket.Close(); + socket = null; + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + } + } +} diff --git a/SMBLibrary/Win32/Authentication/LoginAPI.cs b/SMBLibrary/Win32/Authentication/LoginAPI.cs new file mode 100644 index 0000000..4ce365a --- /dev/null +++ b/SMBLibrary/Win32/Authentication/LoginAPI.cs @@ -0,0 +1,70 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using Utilities; + +namespace SMBLibrary.Authentication.Win32 +{ + public class LoginAPI + { + public const int LOGON32_LOGON_NETWORK = 3; + public const int LOGON32_PROVIDER_WINNT40 = 2; + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool LogonUser( + string lpszUsername, + string lpszDomain, + string lpszPassword, + int dwLogonType, + int dwLogonProvider, + out IntPtr phToken + ); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool CloseHandle(IntPtr hObject); + + public static bool ValidateUserPassword(string userName, string password) + { + IntPtr token; + bool success = LogonUser(userName, String.Empty, password, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_WINNT40, out token); + if (!success) + { + uint error = (uint)Marshal.GetLastWin32Error(); + if (error == (uint)Win32Error.ERROR_ACCOUNT_RESTRICTION || + error == (uint)Win32Error.ERROR_LOGON_FAILURE || + error == (uint)Win32Error.ERROR_LOGON_TYPE_NOT_GRANTED) + { + return false; + } + throw new Exception("ValidateUser failed, error: 0x" + ((uint)error).ToString("X")); + } + CloseHandle(token); + return success; + } + + public static bool HasEmptyPassword(string userName) + { + IntPtr token; + bool success = LogonUser(userName, String.Empty, String.Empty, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_WINNT40, out token); + if (success) + { + CloseHandle(token); + return true; + } + else + { + uint error = (uint)Marshal.GetLastWin32Error(); + return (error == (uint)Win32Error.ERROR_ACCOUNT_RESTRICTION || + error == (uint)Win32Error.ERROR_LOGON_TYPE_NOT_GRANTED); + } + } + } +} diff --git a/SMBLibrary/Win32/Authentication/NetworkAPI.cs b/SMBLibrary/Win32/Authentication/NetworkAPI.cs new file mode 100644 index 0000000..f4e5c1c --- /dev/null +++ b/SMBLibrary/Win32/Authentication/NetworkAPI.cs @@ -0,0 +1,158 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using Utilities; + +namespace SMBLibrary.Authentication.Win32 +{ + public class NetworkAPI + { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct USER_INFO_0 + { + [MarshalAs(UnmanagedType.LPWStr)] + public String Username; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct USER_INFO_1 + { + [MarshalAs(UnmanagedType.LPWStr)] + public string Username; + [MarshalAs(UnmanagedType.LPWStr)] + public string Password; + public uint PasswordAge; + public uint Priv; + [MarshalAs(UnmanagedType.LPWStr)] + public string Home_Dir; + [MarshalAs(UnmanagedType.LPWStr)] + public string Comment; + public uint Flags; + [MarshalAs(UnmanagedType.LPWStr)] + public string ScriptPath; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + public struct LOCALGROUP_USERS_INFO_0 + { + public string groupname; + } + + // NetUserEnum - Obtains a list of all users on local machine or network + [DllImport("Netapi32.dll")] + public extern static int NetUserEnum(string servername, int level, int filter, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out int resume_handle); + + // NetAPIBufferFree - Used to clear the Network buffer after NetUserEnum + [DllImport("Netapi32.dll")] + public extern static int NetApiBufferFree(IntPtr Buffer); + + [DllImport("Netapi32.dll")] + public extern static int NetUserGetLocalGroups([MarshalAs(UnmanagedType.LPWStr)]string servername,[MarshalAs(UnmanagedType.LPWStr)] string username, int level, int flags, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries); + + public static List EnumerateGroups(string userName) + { + List result = new List(); + int entriesRead; + int totalEntries; + IntPtr bufPtr; + + const int Level = 0; + NetUserGetLocalGroups(null, userName, Level, 0, out bufPtr, 1024, out entriesRead, out totalEntries); + + if (entriesRead > 0) + { + LOCALGROUP_USERS_INFO_0[] RetGroups = new LOCALGROUP_USERS_INFO_0[entriesRead]; + IntPtr iter = bufPtr; + for (int i = 0; i < entriesRead; i++) + { + RetGroups[i] = (LOCALGROUP_USERS_INFO_0)Marshal.PtrToStructure(iter, typeof(LOCALGROUP_USERS_INFO_0)); + iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(LOCALGROUP_USERS_INFO_0))); + result.Add(RetGroups[i].groupname); + } + NetApiBufferFree(bufPtr); + } + + return result; + } + + public static List EnumerateAllUsers() + { + List result = new List(); + int entriesRead; + int totalEntries; + int resume; + + IntPtr bufPtr; + const int Level = 0; + const int FILTER_NORMAL_ACCOUNT = 2; + NetUserEnum(null, Level, FILTER_NORMAL_ACCOUNT, out bufPtr, -1, out entriesRead, out totalEntries, out resume); + + if (entriesRead > 0) + { + USER_INFO_0[] Users = new USER_INFO_0[entriesRead]; + IntPtr iter = bufPtr; + for (int i = 0; i < entriesRead; i++) + { + Users[i] = (USER_INFO_0)Marshal.PtrToStructure(iter, typeof(USER_INFO_0)); + iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(USER_INFO_0))); + result.Add(Users[i].Username); + } + NetApiBufferFree(bufPtr); + } + return result; + } + + public static List EnumerateEnabledUsers() + { + List result = new List(); + int entriesRead; + int totalEntries; + int resume; + + IntPtr bufPtr; + const int Level = 1; + const int FILTER_NORMAL_ACCOUNT = 2; + const int UF_ACCOUNTDISABLE = 2; + NetUserEnum(null, Level, FILTER_NORMAL_ACCOUNT, out bufPtr, -1, out entriesRead, out totalEntries, out resume); + + if (entriesRead > 0) + { + USER_INFO_1[] Users = new USER_INFO_1[entriesRead]; + IntPtr iter = bufPtr; + for (int i = 0; i < entriesRead; i++) + { + Users[i] = (USER_INFO_1)Marshal.PtrToStructure(iter, typeof(USER_INFO_1)); + iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(USER_INFO_1))); + if ((Users[i].Flags & UF_ACCOUNTDISABLE) == 0) + { + result.Add(Users[i].Username); + } + } + NetApiBufferFree(bufPtr); + } + return result; + } + + public static List EnumerateNetworkUsers() + { + List result = new List(); + List users = EnumerateEnabledUsers(); + foreach (string userName in users) + { + List groups = EnumerateGroups(userName); + if (groups.Contains("Users") || groups.Contains("Administrators") || groups.Contains("Guests")) + { + result.Add(userName); + } + } + return result; + } + } +} diff --git a/SMBLibrary/Win32/Authentication/SSPIHelper.cs b/SMBLibrary/Win32/Authentication/SSPIHelper.cs new file mode 100644 index 0000000..4174fbe --- /dev/null +++ b/SMBLibrary/Win32/Authentication/SSPIHelper.cs @@ -0,0 +1,324 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace SMBLibrary.Authentication.Win32 +{ + [StructLayout(LayoutKind.Sequential)] + public struct SecHandle + { + public IntPtr dwLower; + public IntPtr dwUpper; + }; + + public class SSPIHelper + { + public const int MAX_TOKEN_SIZE = 12000; + + public const uint SEC_E_OK = 0; + public const uint SEC_I_CONTINUE_NEEDED = 0x90312; + public const uint SEC_E_INVALID_HANDLE = 0x80090301; + public const uint SEC_E_INVALID_TOKEN = 0x80090308; + public const uint SEC_E_LOGON_DENIED = 0x8009030C; + public const uint SEC_E_BUFFER_TOO_SMALL = 0x80090321; + + public const int SECURITY_NETWORK_DREP = 0x00; + public const int SECURITY_NATIVE_DREP = 0x10; + + public const int SECPKG_CRED_INBOUND = 0x01; + public const int SECPKG_CRED_OUTBOUND = 0x02; + public const int SECPKG_CRED_BOTH = 0x03; + + public const int ISC_REQ_CONFIDENTIALITY = 0x00000010; + public const int ISC_REQ_ALLOCATE_MEMORY = 0x00000100; + public const int ISC_REQ_INTEGRITY = 0x00010000; + + public const int ASC_REQ_REPLAY_DETECT = 0x00000004; + public const int ASC_REQ_CONFIDENTIALITY = 0x00000010; + public const int ASC_REQ_USE_SESSION_KEY = 0x00000020; + public const int ASC_REQ_INTEGRITY = 0x00020000; + + public const int SEC_WINNT_AUTH_IDENTITY_ANSI = 1; + public const int SEC_WINNT_AUTH_IDENTITY_UNICODE = 2; + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_INTEGER + { + public uint LowPart; + public int HighPart; + }; + + /// + /// When using the NTLM package, the maximum character lengths for user name, password, and domain are 256, 256, and 15, respectively. + /// + [StructLayout(LayoutKind.Sequential)] + public struct SEC_WINNT_AUTH_IDENTITY + { + public string User; + public int UserLength; + public string Domain; + public int DomainLength; + public string Password; + public int PasswordLength; + public int Flags; + }; + + [DllImport("secur32.dll", SetLastError = true)] + private static extern int AcquireCredentialsHandle( + string pszPrincipal, + string pszPackage, + int fCredentialUse, + IntPtr pvLogonID, + IntPtr pAuthData, + IntPtr pGetKeyFn, + IntPtr pvGetKeyArgument, + out SecHandle phCredential, + out SECURITY_INTEGER ptsExpiry); + + [DllImport("secur32.dll", SetLastError = true)] + static extern int InitializeSecurityContext( + ref SecHandle phCredential, + IntPtr phContext, + string pszTargetName, + int fContextReq, + int Reserved1, + int TargetDataRep, + IntPtr pInput, + int Reserved2, + ref SecHandle phNewContext, + ref SecBufferDesc pOutput, + out uint pfContextAttr, + out SECURITY_INTEGER ptsExpiry); + + [DllImport("secur32.dll", SetLastError = true)] + static extern int InitializeSecurityContext( + IntPtr phCredential, + ref SecHandle phContext, + string pszTargetName, + int fContextReq, + int Reserved1, + int TargetDataRep, + ref SecBufferDesc pInput, + int Reserved2, + ref SecHandle phNewContext, + ref SecBufferDesc pOutput, + out uint pfContextAttr, + out SECURITY_INTEGER ptsExpiry); + + [DllImport("secur32.dll", SetLastError = true)] + static extern int AcceptSecurityContext( + ref SecHandle phCredential, + IntPtr phContext, + ref SecBufferDesc pInput, + uint fContextReq, + uint TargetDataRep, + ref SecHandle phNewContext, + ref SecBufferDesc pOutput, + out uint pfContextAttr, + out SECURITY_INTEGER ptsTimeStamp); + + [DllImport("secur32.dll", SetLastError = true)] + static extern int AcceptSecurityContext( + IntPtr phCredential, + ref SecHandle phContext, + ref SecBufferDesc pInput, + uint fContextReq, + uint TargetDataRep, + ref SecHandle phNewContext, + ref SecBufferDesc pOutput, + out uint pfContextAttr, + out SECURITY_INTEGER ptsTimeStamp); + + [DllImport("Secur32.dll")] + private extern static int FreeContextBuffer( + IntPtr pvContextBuffer + ); + + [DllImport("Secur32.dll")] + private extern static int FreeCredentialsHandle( + ref SecHandle phCredential + ); + + [DllImport("Secur32.dll")] + private extern static int DeleteSecurityContext( + ref SecHandle phContext + ); + + public static SecHandle AcquireNTLMCredentialsHandle() + { + return AcquireNTLMCredentialsHandle(null); + } + + public static SecHandle AcquireNTLMCredentialsHandle(string domainName, string userName, string password) + { + SEC_WINNT_AUTH_IDENTITY auth = new SEC_WINNT_AUTH_IDENTITY(); + auth.Domain = domainName; + auth.DomainLength = domainName.Length; + auth.User = userName; + auth.UserLength = userName.Length; + auth.Password = password; + auth.PasswordLength = password.Length; + auth.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + return AcquireNTLMCredentialsHandle(auth); + } + + private static SecHandle AcquireNTLMCredentialsHandle(SEC_WINNT_AUTH_IDENTITY? auth) + { + SecHandle credential; + SECURITY_INTEGER expiry; + + IntPtr pAuthData; + if (auth.HasValue) + { + pAuthData = Marshal.AllocHGlobal(Marshal.SizeOf(auth.Value)); + Marshal.StructureToPtr(auth.Value, pAuthData, false); + } + else + { + pAuthData = IntPtr.Zero; + } + + int result = AcquireCredentialsHandle(null, "NTLM", SECPKG_CRED_BOTH, IntPtr.Zero, pAuthData, IntPtr.Zero, IntPtr.Zero, out credential, out expiry); + if (pAuthData != IntPtr.Zero) + { + Marshal.FreeHGlobal(pAuthData); + } + if (result != SEC_E_OK) + { + throw new Exception("AcquireCredentialsHandle failed, Error code " + ((uint)result).ToString("X")); + } + + return credential; + } + + public static byte[] GetType1Message(string userName, string password, out SecHandle clientContext) + { + return GetType1Message(String.Empty, userName, password, out clientContext); + } + + public static byte[] GetType1Message(string domainName, string userName, string password, out SecHandle clientContext) + { + SecHandle handle = AcquireNTLMCredentialsHandle(domainName, userName, password); + clientContext = new SecHandle(); + SecBufferDesc output = new SecBufferDesc(MAX_TOKEN_SIZE); + uint contextAttributes; + SECURITY_INTEGER expiry; + + int result = InitializeSecurityContext(ref handle, IntPtr.Zero, null, ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY, 0, SECURITY_NATIVE_DREP, IntPtr.Zero, 0, ref clientContext, ref output, out contextAttributes, out expiry); + if (result != SEC_E_OK && result != SEC_I_CONTINUE_NEEDED) + { + if ((uint)result == SEC_E_INVALID_HANDLE) + { + throw new Exception("InitializeSecurityContext failed, Invalid handle"); + } + else if ((uint)result == SEC_E_BUFFER_TOO_SMALL) + { + throw new Exception("InitializeSecurityContext failed, Buffer too small"); + } + else + { + throw new Exception("InitializeSecurityContext failed, Error code " + ((uint)result).ToString("X")); + } + } + return output.GetSecBufferBytes(); + } + + public static byte[] GetType3Message(SecHandle clientContext, byte[] type2Message) + { + SecHandle newContext = new SecHandle(); + SecBufferDesc input = new SecBufferDesc(type2Message); + SecBufferDesc output = new SecBufferDesc(MAX_TOKEN_SIZE); + uint contextAttributes; + SECURITY_INTEGER expiry; + + int result = InitializeSecurityContext(IntPtr.Zero, ref clientContext, null, ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY, 0, SECURITY_NATIVE_DREP, ref input, 0, ref newContext, ref output, out contextAttributes, out expiry); + if (result != SEC_E_OK) + { + if ((uint)result == SEC_E_INVALID_HANDLE) + { + throw new Exception("InitializeSecurityContext failed, invalid handle"); + } + else if ((uint)result == SEC_E_BUFFER_TOO_SMALL) + { + throw new Exception("InitializeSecurityContext failed, buffer too small"); + } + else + { + throw new Exception("InitializeSecurityContext failed, error code " + ((uint)result).ToString("X")); + } + } + return output.GetSecBufferBytes(); + } + + public static byte[] GetType2Message(byte[] type1MessageBytes, out SecHandle serverContext) + { + SecHandle handle = AcquireNTLMCredentialsHandle(); + SecBufferDesc type1Message = new SecBufferDesc(type1MessageBytes); + serverContext = new SecHandle(); + SecBufferDesc output = new SecBufferDesc(MAX_TOKEN_SIZE); + uint contextAttributes; + SECURITY_INTEGER timestamp; + + int result = AcceptSecurityContext(ref handle, IntPtr.Zero, ref type1Message, ASC_REQ_INTEGRITY | ASC_REQ_CONFIDENTIALITY, SECURITY_NATIVE_DREP, ref serverContext, ref output, out contextAttributes, out timestamp); + if (result != SEC_E_OK && result != SEC_I_CONTINUE_NEEDED) + { + if ((uint)result == SEC_E_INVALID_HANDLE) + { + throw new Exception("AcceptSecurityContext failed, invalid handle"); + } + else if ((uint)result == SEC_E_BUFFER_TOO_SMALL) + { + throw new Exception("AcceptSecurityContext failed, buffer too small"); + } + else + { + throw new Exception("AcceptSecurityContext failed, error code " + ((uint)result).ToString("X")); + } + } + FreeCredentialsHandle(ref handle); + return output.GetSecBufferBytes(); + } + + /// + /// Note: The 'limitblankpassworduse' (Under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa) + /// will cause AcceptSecurityContext to return SEC_E_LOGON_DENIED when the correct password is blank. + /// + public static bool AuthenticateType3Message(SecHandle serverContext, byte[] type3MessageBytes) + { + SecHandle newContext = new SecHandle(); + SecBufferDesc type3Message = new SecBufferDesc(type3MessageBytes); + SecBufferDesc output = new SecBufferDesc(MAX_TOKEN_SIZE); + uint contextAttributes; + SECURITY_INTEGER timestamp; + + int result = AcceptSecurityContext(IntPtr.Zero, ref serverContext, ref type3Message, ASC_REQ_INTEGRITY | ASC_REQ_CONFIDENTIALITY, SECURITY_NATIVE_DREP, ref newContext, ref output, out contextAttributes, out timestamp); + + if (result == SEC_E_OK) + { + return true; + } + else if ((uint)result == SEC_E_LOGON_DENIED) + { + return false; + } + else + { + if ((uint)result == SEC_E_INVALID_TOKEN) + { + throw new Exception("AcceptSecurityContext failed, invalid security token"); + } + else + { + throw new Exception("AcceptSecurityContext failed, error code " + ((uint)result).ToString("X")); + } + } + } + } +} diff --git a/SMBLibrary/Win32/Authentication/SecBufferDesc.cs b/SMBLibrary/Win32/Authentication/SecBufferDesc.cs new file mode 100644 index 0000000..e8d1850 --- /dev/null +++ b/SMBLibrary/Win32/Authentication/SecBufferDesc.cs @@ -0,0 +1,120 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace SMBLibrary.Authentication.Win32 +{ + public enum SecBufferType + { + SECBUFFER_VERSION = 0, + SECBUFFER_EMPTY = 0, + SECBUFFER_DATA = 1, + SECBUFFER_TOKEN = 2 + } + + [StructLayout(LayoutKind.Sequential)] + public struct SecBuffer + { + public int cbBuffer; + public int BufferType; + public IntPtr pvBuffer; + + public SecBuffer(int bufferSize) + { + cbBuffer = bufferSize; + BufferType = (int)SecBufferType.SECBUFFER_TOKEN; + pvBuffer = Marshal.AllocHGlobal(bufferSize); + } + + public SecBuffer(byte[] secBufferBytes) + { + cbBuffer = secBufferBytes.Length; + BufferType = (int)SecBufferType.SECBUFFER_TOKEN; + pvBuffer = Marshal.AllocHGlobal(cbBuffer); + Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); + } + + public SecBuffer(byte[] secBufferBytes, SecBufferType bufferType) + { + cbBuffer = secBufferBytes.Length; + BufferType = (int)bufferType; + pvBuffer = Marshal.AllocHGlobal(cbBuffer); + Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); + } + + public void Dispose() + { + if (pvBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(pvBuffer); + pvBuffer = IntPtr.Zero; + } + } + + public byte[] GetBytes() + { + byte[] buffer = null; + if (cbBuffer > 0) + { + buffer = new byte[cbBuffer]; + Marshal.Copy(pvBuffer, buffer, 0, cbBuffer); + } + return buffer; + } + } + + /// + /// Simplified SecBufferDesc struct with only one SecBuffer + /// + [StructLayout(LayoutKind.Sequential)] + public struct SecBufferDesc + { + public int ulVersion; + public int cBuffers; + public IntPtr pBuffers; + + public SecBufferDesc(int bufferSize) + { + ulVersion = (int)SecBufferType.SECBUFFER_VERSION; + cBuffers = 1; + SecBuffer secBuffer = new SecBuffer(bufferSize); + pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(secBuffer)); + Marshal.StructureToPtr(secBuffer, pBuffers, false); + } + + public SecBufferDesc(byte[] secBufferBytes) + { + ulVersion = (int)SecBufferType.SECBUFFER_VERSION; + cBuffers = 1; + SecBuffer secBuffer = new SecBuffer(secBufferBytes); + pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(secBuffer)); + Marshal.StructureToPtr(secBuffer, pBuffers, false); + } + + public void Dispose() + { + if (pBuffers != IntPtr.Zero) + { + SecBuffer secBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); + secBuffer.Dispose(); + Marshal.FreeHGlobal(pBuffers); + pBuffers = IntPtr.Zero; + } + } + + public byte[] GetSecBufferBytes() + { + if (pBuffers == IntPtr.Zero) + throw new ObjectDisposedException("SecBufferDesc"); + SecBuffer secBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); + return secBuffer.GetBytes(); + } + } +} diff --git a/SMBLibrary/Win32/Win32UserCollection.cs b/SMBLibrary/Win32/Win32UserCollection.cs new file mode 100644 index 0000000..63e39ba --- /dev/null +++ b/SMBLibrary/Win32/Win32UserCollection.cs @@ -0,0 +1,217 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using Utilities; +using SMBLibrary.Authentication; +using SMBLibrary.Authentication.Win32; +using Microsoft.Win32; + +namespace SMBLibrary.Server.Win32 +{ + public class Win32UserCollection : UserCollection, INTLMAuthenticationProvider + { + private SecHandle m_serverContext; + private byte[] m_serverChallenge = new byte[8]; + + public Win32UserCollection() + { + List users = NetworkAPI.EnumerateNetworkUsers(); + foreach (string user in users) + { + this.Add(new User(user, String.Empty)); + } + } + + public byte[] GenerateServerChallenge() + { + NegotiateMessage negotiateMessage = new NegotiateMessage(); + negotiateMessage.NegotiateFlags = NegotiateFlags.NegotiateUnicode | NegotiateFlags.NegotiateOEM | NegotiateFlags.RequestTarget | NegotiateFlags.NegotiateSign | NegotiateFlags.NegotiateSeal | NegotiateFlags.NegotiateLanManagerKey | NegotiateFlags.NegotiateNTLMKey | NegotiateFlags.NegotiateAlwaysSign | NegotiateFlags.NegotiateVersion | NegotiateFlags.Negotiate128 | NegotiateFlags.Negotiate56; + negotiateMessage.Version = Authentication.Version.Server2003; + + byte[] negotiateMessageBytes = negotiateMessage.GetBytes(); + byte[] challengeMessageBytes = SSPIHelper.GetType2Message(negotiateMessageBytes, out m_serverContext); + ChallengeMessage challengeMessage = new ChallengeMessage(challengeMessageBytes); + + m_serverChallenge = challengeMessage.ServerChallenge; + return m_serverChallenge; + } + + public byte[] GetChallengeMessageBytes(byte[] negotiateMessageBytes) + { + byte[] challengeMessageBytes = SSPIHelper.GetType2Message(negotiateMessageBytes, out m_serverContext); + ChallengeMessage message = new ChallengeMessage(challengeMessageBytes); + m_serverChallenge = message.ServerChallenge; + return challengeMessageBytes; + } + + /// + /// Note: The 'limitblankpassworduse' (Under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa) + /// will cause AcceptSecurityContext to return SEC_E_LOGON_DENIED when the correct password is blank. + /// + public User Authenticate(string accountNameToAuth, byte[] lmResponse, byte[] ntlmResponse) + { + if (accountNameToAuth == String.Empty || + (String.Equals(accountNameToAuth, "Guest", StringComparison.InvariantCultureIgnoreCase) && IsPasswordEmpty(lmResponse, ntlmResponse) && this.EnableGuestLogin)) + { + int guestIndex = IndexOf("Guest"); + if (guestIndex >= 0) + { + return this[guestIndex]; + } + return null; + } + + int index = IndexOf(accountNameToAuth); + if (index >= 0) + { + // We should not spam the security event log, and should call the Windows LogonUser API + // just to verify the user has a blank password. + if (!AreEmptyPasswordsAllowed() && + IsPasswordEmpty(lmResponse, ntlmResponse) && + LoginAPI.HasEmptyPassword(accountNameToAuth)) + { + throw new EmptyPasswordNotAllowedException(); + } + + AuthenticateMessage authenticateMessage = new AuthenticateMessage(); + authenticateMessage.NegotiateFlags = NegotiateFlags.NegotiateUnicode | NegotiateFlags.NegotiateOEM | NegotiateFlags.RequestTarget | NegotiateFlags.NegotiateSign | NegotiateFlags.NegotiateSeal | NegotiateFlags.NegotiateLanManagerKey | NegotiateFlags.NegotiateNTLMKey | NegotiateFlags.NegotiateAlwaysSign | NegotiateFlags.NegotiateVersion | NegotiateFlags.Negotiate128 | NegotiateFlags.Negotiate56; + authenticateMessage.UserName = accountNameToAuth; + authenticateMessage.LmChallengeResponse = lmResponse; + authenticateMessage.NtChallengeResponse = ntlmResponse; + authenticateMessage.Version = Authentication.Version.Server2003; + byte[] authenticateMessageBytes = authenticateMessage.GetBytes(); + + bool success = SSPIHelper.AuthenticateType3Message(m_serverContext, authenticateMessageBytes); + if (success) + { + return this[index]; + } + } + return null; + } + + /// + /// Note: The 'limitblankpassworduse' (Under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa) + /// will cause AcceptSecurityContext to return SEC_E_LOGON_DENIED when the correct password is blank. + /// + public User Authenticate(byte[] authenticateMessageBytes) + { + AuthenticateMessage message = new AuthenticateMessage(authenticateMessageBytes); + if ((message.NegotiateFlags & NegotiateFlags.NegotiateAnonymous) > 0 || + (String.Equals(message.UserName, "Guest", StringComparison.InvariantCultureIgnoreCase) && IsPasswordEmpty(message) && this.EnableGuestLogin)) + { + int guestIndex = IndexOf("Guest"); + if (guestIndex >= 0) + { + return this[guestIndex]; + } + return null; + } + + int index = IndexOf(message.UserName); + if (index >= 0) + { + // We should not spam the security event log, and should call the Windows LogonUser API + // just to verify the user has a blank password. + if (!AreEmptyPasswordsAllowed() && + IsPasswordEmpty(message) && + LoginAPI.HasEmptyPassword(message.UserName)) + { + throw new EmptyPasswordNotAllowedException(); + } + + bool success = SSPIHelper.AuthenticateType3Message(m_serverContext, authenticateMessageBytes); + if (success) + { + return this[index]; + } + } + return null; + } + + public bool IsPasswordEmpty(byte[] lmResponse, byte[] ntlmResponse) + { + // Special case for anonymous authentication + // Windows NT4 SP6 will send 1 null byte OEMPassword and 0 bytes UnicodePassword for anonymous authentication + if (lmResponse.Length == 0 || ByteUtils.AreByteArraysEqual(lmResponse, new byte[] { 0x00 }) || ntlmResponse.Length == 0) + { + return true; + } + + byte[] emptyPasswordLMv1Response = NTAuthentication.ComputeLMv1Response(m_serverChallenge, String.Empty); + if (ByteUtils.AreByteArraysEqual(emptyPasswordLMv1Response, lmResponse)) + { + return true; + } + + byte[] emptyPasswordNTLMv1Response = NTAuthentication.ComputeNTLMv1Response(m_serverChallenge, String.Empty); + if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv1Response, ntlmResponse)) + { + return true; + } + + return false; + } + + public bool IsPasswordEmpty(AuthenticateMessage message) + { + // Special case for anonymous authentication, see [MS-NLMP] 3.3.1 - NTLM v1 Authentication + if (message.LmChallengeResponse.Length == 1 || message.NtChallengeResponse.Length == 0) + { + return true; + } + + byte[] clientChallenge = ByteReader.ReadBytes(message.LmChallengeResponse, 0, 8); + byte[] emptyPasswordNTLMv1Response = NTAuthentication.ComputeNTLMv1ExtendedSecurityResponse(m_serverChallenge, clientChallenge, String.Empty); + if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv1Response, message.NtChallengeResponse)) + { + return true; + } + + if (message.NtChallengeResponse.Length > 24) + { + NTLMv2ClientChallengeStructure clientChallengeStructure = new NTLMv2ClientChallengeStructure(message.NtChallengeResponse, 16); + byte[] clientChallengeStructurePadded = clientChallengeStructure.GetBytesPadded(); + byte[] emptyPasswordNTLMv2Response = NTAuthentication.ComputeNTLMv2Response(m_serverChallenge, clientChallengeStructurePadded, String.Empty, message.UserName, message.DomainName); + if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv2Response, message.NtChallengeResponse)) + { + return true; + } + } + + return false; + } + + /// + /// We immitate Windows, Guest logins are disabled when the guest account has password set + /// + public bool EnableGuestLogin + { + get + { + return (IndexOf("Guest") >= 0) && LoginAPI.HasEmptyPassword("Guest"); + } + } + + public static bool AreEmptyPasswordsAllowed() + { + RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Lsa"); + object value = key.GetValue("limitblankpassworduse", 1); + if (value is int) + { + if ((int)value != 0) + { + return false; + } + } + return true; + } + } +} diff --git a/SMBServer.sln b/SMBServer.sln new file mode 100644 index 0000000..0fbd3a8 --- /dev/null +++ b/SMBServer.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SMBServer", "SMBServer\SMBServer.csproj", "{70D43E2A-26A2-4046-A472-5BA8C9437612}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SMBLibrary", "SMBLibrary\SMBLibrary.csproj", "{8D9E8F5D-FD13-4E4C-9723-A333DA2034A7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utilities", "Utilities\Utilities.csproj", "{6E0F2D1E-6167-4032-BA90-DEE3A99207D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {70D43E2A-26A2-4046-A472-5BA8C9437612}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70D43E2A-26A2-4046-A472-5BA8C9437612}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70D43E2A-26A2-4046-A472-5BA8C9437612}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70D43E2A-26A2-4046-A472-5BA8C9437612}.Release|Any CPU.Build.0 = Release|Any CPU + {8D9E8F5D-FD13-4E4C-9723-A333DA2034A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D9E8F5D-FD13-4E4C-9723-A333DA2034A7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D9E8F5D-FD13-4E4C-9723-A333DA2034A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D9E8F5D-FD13-4E4C-9723-A333DA2034A7}.Release|Any CPU.Build.0 = Release|Any CPU + {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E0F2D1E-6167-4032-BA90-DEE3A99207D0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/SMBServer/DirectoryFileSystem.cs b/SMBServer/DirectoryFileSystem.cs new file mode 100644 index 0000000..43f6dac --- /dev/null +++ b/SMBServer/DirectoryFileSystem.cs @@ -0,0 +1,282 @@ +/* Copyright (C) 2014 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Utilities; + +namespace SMBServer +{ + public class DirectoryFileSystem : FileSystem + { + private DirectoryInfo m_directory; + + public DirectoryFileSystem(string path) : this(new DirectoryInfo(path)) + { + } + + public DirectoryFileSystem(DirectoryInfo directory) + { + m_directory = directory; + } + + public override FileSystemEntry GetEntry(string path) + { + ValidatePath(path); + string fullPath = m_directory.FullName + path; + if (File.Exists(fullPath)) + { + FileInfo file = new FileInfo(fullPath); + bool isHidden = (file.Attributes & FileAttributes.Hidden) > 0; + bool isReadonly = (file.Attributes & FileAttributes.ReadOnly) > 0; + bool isArchived = (file.Attributes & FileAttributes.Archive) > 0; + return new FileSystemEntry(path, file.Name, false, (ulong)file.Length, file.CreationTime, file.LastWriteTime, file.LastAccessTime, isHidden, isReadonly, isArchived); + } + else if (Directory.Exists(fullPath)) + { + DirectoryInfo directory = new DirectoryInfo(fullPath); + string fullName = FileSystem.GetDirectoryPath(path); + bool isHidden = (directory.Attributes & FileAttributes.Hidden) > 0; + bool isReadonly = (directory.Attributes & FileAttributes.ReadOnly) > 0; + bool isArchived = (directory.Attributes & FileAttributes.Archive) > 0; + return new FileSystemEntry(fullName, directory.Name, true, 0, directory.CreationTime, directory.LastWriteTime, directory.LastAccessTime, isHidden, isReadonly, isArchived); + } + else + { + return null; + } + } + + public override FileSystemEntry CreateFile(string path) + { + ValidatePath(path); + string fullPath = m_directory.FullName + path; + FileStream stream = File.Create(fullPath); + stream.Close(); + return GetEntry(path); + } + + public override FileSystemEntry CreateDirectory(string path) + { + ValidatePath(path); + string fullPath = m_directory.FullName + path; + Directory.CreateDirectory(fullPath); + + return GetEntry(path); + } + + public override void Move(string source, string destination) + { + ValidatePath(source); + ValidatePath(destination); + string sourcePath = m_directory.FullName + source; + string destinationPath = m_directory.FullName + destination; + if (File.Exists(sourcePath)) + { + File.Move(sourcePath, destinationPath); + } + else // Entry is a directory + { + Directory.Move(sourcePath, destinationPath); + } + } + + public override void Delete(string path) + { + ValidatePath(path); + string fullPath = m_directory.FullName + path; + if (File.Exists(fullPath)) + { + File.Delete(fullPath); + } + else if (Directory.Exists(fullPath)) + { + Directory.Delete(fullPath, true); + } + else + { + throw new FileNotFoundException(); + } + } + + public override List ListEntriesInDirectory(string path) + { + ValidatePath(path); + if (path == String.Empty) + { + path = @"\"; + } + string fullPath = m_directory.FullName + path; + DirectoryInfo directory = new DirectoryInfo(fullPath); + List result = new List(); + foreach (DirectoryInfo subDirectory in directory.GetDirectories()) + { + string fullName = GetRelativeDirectoryPath(subDirectory.FullName); + bool isHidden = (subDirectory.Attributes & FileAttributes.Hidden) > 0; + bool isReadonly = (subDirectory.Attributes & FileAttributes.ReadOnly) > 0; + bool isArchived = (subDirectory.Attributes & FileAttributes.Archive) > 0; + result.Add(new FileSystemEntry(fullName, subDirectory.Name, true, 0, subDirectory.CreationTime, subDirectory.LastWriteTime, subDirectory.LastAccessTime, isHidden, isReadonly, isArchived)); + } + foreach (FileInfo file in directory.GetFiles()) + { + string fullName = GetRelativePath(file.FullName); + bool isHidden = (file.Attributes & FileAttributes.Hidden) > 0; + bool isReadonly = (file.Attributes & FileAttributes.ReadOnly) > 0; + bool isArchived = (file.Attributes & FileAttributes.Archive) > 0; + result.Add(new FileSystemEntry(fullName, file.Name, false, (ulong)file.Length, file.CreationTime, file.LastWriteTime, file.LastAccessTime, isHidden, isReadonly, isArchived)); + } + return result; + } + + public override Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share) + { + ValidatePath(path); + string fullPath = m_directory.FullName + path; + return new FileInfo(fullPath).Open(mode, access, share); + } + + public override void SetAttributes(string path, bool? isHidden, bool? isReadonly, bool? isArchived) + { + ValidatePath(path); + string fullPath = m_directory.FullName + path; + if (File.Exists(fullPath) || Directory.Exists(fullPath)) + { + FileAttributes attributes = File.GetAttributes(fullPath); + if (isHidden.HasValue) + { + if (isHidden.Value) + { + attributes |= FileAttributes.Hidden; + } + else + { + attributes &= ~FileAttributes.Hidden; + } + } + + if (isReadonly.HasValue) + { + if (isReadonly.Value) + { + attributes |= FileAttributes.ReadOnly; + } + else + { + attributes &= ~FileAttributes.ReadOnly; + } + } + + if (isArchived.HasValue) + { + if (isArchived.Value) + { + attributes |= FileAttributes.Archive; + } + else + { + attributes &= ~FileAttributes.Archive; + } + } + + File.SetAttributes(fullPath, attributes); + } + else + { + throw new FileNotFoundException(); + } + } + + public override void SetDates(string path, DateTime? creationDT, DateTime? lastWriteDT, DateTime? lastAccessDT) + { + ValidatePath(path); + string fullPath = m_directory.FullName + path; + if (File.Exists(fullPath)) + { + if (creationDT.HasValue) + { + File.SetCreationTime(fullPath, creationDT.Value); + } + + if (lastWriteDT.HasValue) + { + File.SetLastWriteTime(fullPath, lastWriteDT.Value); + } + + if (lastAccessDT.HasValue) + { + File.SetLastAccessTime(fullPath, lastAccessDT.Value); + } + } + else if (Directory.Exists(fullPath)) + { + if (creationDT.HasValue) + { + Directory.SetCreationTime(fullPath, creationDT.Value); + } + + if (lastWriteDT.HasValue) + { + Directory.SetLastWriteTime(fullPath, lastWriteDT.Value); + } + + if (lastAccessDT.HasValue) + { + Directory.SetLastAccessTime(fullPath, lastAccessDT.Value); + } + } + else + { + throw new FileNotFoundException(); + } + } + + private void ValidatePath(string path) + { + if (path.StartsWith(@"..\") || path.Contains(@"\..\")) + { + throw new UnauthorizedAccessException("Given path is not allowed"); + } + } + + public override string Name + { + get + { + return "DirFS"; + } + } + + public override long Size + { + get + { + DriveInfo drive = new DriveInfo(m_directory.FullName.Substring(0, 2)); + return drive.TotalSize; + } + } + + public override long FreeSpace + { + get + { + DriveInfo drive = new DriveInfo(m_directory.FullName.Substring(0, 2)); + return drive.AvailableFreeSpace; + } + } + + private string GetRelativePath(string fullPath) + { + return fullPath.Substring(m_directory.FullName.Length); + } + + private string GetRelativeDirectoryPath(string fullPath) + { + return GetRelativePath(GetDirectoryPath(fullPath)); + } + } +} diff --git a/SMBServer/Program.cs b/SMBServer/Program.cs new file mode 100644 index 0000000..e01e773 --- /dev/null +++ b/SMBServer/Program.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Windows.Forms; + +namespace SMBServer +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException); + AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new ServerUI()); + } + + public static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) + { + HandleUnhandledException(e.Exception); + } + + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + if (e.ExceptionObject != null) + { + Exception ex = (Exception)e.ExceptionObject; + HandleUnhandledException(ex); + } + } + + private static void HandleUnhandledException(Exception ex) + { + string message = String.Format("Exception: {0}: {1} Source: {2} {3}", ex.GetType(), ex.Message, ex.Source, ex.StackTrace); + MessageBox.Show(message, "Error"); + Application.Exit(); + } + } +} \ No newline at end of file diff --git a/SMBServer/Properties/AssemblyInfo.cs b/SMBServer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..70b190d --- /dev/null +++ b/SMBServer/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SMBServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Tal Aloni")] +[assembly: AssemblyProduct("SMBServer")] +[assembly: AssemblyCopyright("Copyright © Tal Aloni 2014-2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6efa6101-f82c-4798-99a6-11e4ee2f2588")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.4.0")] +[assembly: AssemblyFileVersion("1.0.4.0")] diff --git a/SMBServer/Properties/Resources.Designer.cs b/SMBServer/Properties/Resources.Designer.cs new file mode 100644 index 0000000..4c71817 --- /dev/null +++ b/SMBServer/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3053 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SMBServer.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SMBServer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/SMBServer/Properties/Resources.resx b/SMBServer/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/SMBServer/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SMBServer/Properties/Settings.Designer.cs b/SMBServer/Properties/Settings.Designer.cs new file mode 100644 index 0000000..f2650e9 --- /dev/null +++ b/SMBServer/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3053 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SMBServer.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/SMBServer/Properties/Settings.settings b/SMBServer/Properties/Settings.settings new file mode 100644 index 0000000..abf36c5 --- /dev/null +++ b/SMBServer/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SMBServer/SMBServer.csproj b/SMBServer/SMBServer.csproj new file mode 100644 index 0000000..ebd921a --- /dev/null +++ b/SMBServer/SMBServer.csproj @@ -0,0 +1,94 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {70D43E2A-26A2-4046-A472-5BA8C9437612} + WinExe + Properties + SMBServer + SMBServer + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + Form + + + ServerUI.cs + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + Designer + ServerUI.cs + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {8D9E8F5D-FD13-4E4C-9723-A333DA2034A7} + SMBLibrary + + + {6E0F2D1E-6167-4032-BA90-DEE3A99207D0} + Utilities + + + + + Always + + + + + \ No newline at end of file diff --git a/SMBServer/ServerUI.Designer.cs b/SMBServer/ServerUI.Designer.cs new file mode 100644 index 0000000..43f67b1 --- /dev/null +++ b/SMBServer/ServerUI.Designer.cs @@ -0,0 +1,160 @@ +namespace SMBServer +{ + partial class ServerUI + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.comboIPAddress = new System.Windows.Forms.ComboBox(); + this.rbtNetBiosOverTCP = new System.Windows.Forms.RadioButton(); + this.rbtDirectTCPTransport = new System.Windows.Forms.RadioButton(); + this.btnStart = new System.Windows.Forms.Button(); + this.btnStop = new System.Windows.Forms.Button(); + this.lblTransport = new System.Windows.Forms.Label(); + this.lblAddress = new System.Windows.Forms.Label(); + this.chkIntegratedWindowsAuthentication = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // comboIPAddress + // + this.comboIPAddress.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboIPAddress.FormattingEnabled = true; + this.comboIPAddress.Location = new System.Drawing.Point(79, 6); + this.comboIPAddress.Name = "comboIPAddress"; + this.comboIPAddress.Size = new System.Drawing.Size(121, 21); + this.comboIPAddress.TabIndex = 0; + // + // rbtNetBiosOverTCP + // + this.rbtNetBiosOverTCP.AutoSize = true; + this.rbtNetBiosOverTCP.Checked = true; + this.rbtNetBiosOverTCP.Location = new System.Drawing.Point(79, 41); + this.rbtNetBiosOverTCP.Name = "rbtNetBiosOverTCP"; + this.rbtNetBiosOverTCP.Size = new System.Drawing.Size(164, 17); + this.rbtNetBiosOverTCP.TabIndex = 2; + this.rbtNetBiosOverTCP.TabStop = true; + this.rbtNetBiosOverTCP.Text = "NetBIOS over TCP (Port 139)"; + this.rbtNetBiosOverTCP.UseVisualStyleBackColor = true; + // + // rbtDirectTCPTransport + // + this.rbtDirectTCPTransport.AutoSize = true; + this.rbtDirectTCPTransport.Location = new System.Drawing.Point(79, 64); + this.rbtDirectTCPTransport.Name = "rbtDirectTCPTransport"; + this.rbtDirectTCPTransport.Size = new System.Drawing.Size(174, 17); + this.rbtDirectTCPTransport.TabIndex = 3; + this.rbtDirectTCPTransport.Text = "Direct TCP Transport (Port 445)"; + this.rbtDirectTCPTransport.UseVisualStyleBackColor = true; + // + // btnStart + // + this.btnStart.Location = new System.Drawing.Point(339, 9); + this.btnStart.Name = "btnStart"; + this.btnStart.Size = new System.Drawing.Size(91, 23); + this.btnStart.TabIndex = 4; + this.btnStart.Text = "Start"; + this.btnStart.UseVisualStyleBackColor = true; + this.btnStart.Click += new System.EventHandler(this.btnStart_Click); + // + // btnStop + // + this.btnStop.Enabled = false; + this.btnStop.Location = new System.Drawing.Point(339, 38); + this.btnStop.Name = "btnStop"; + this.btnStop.Size = new System.Drawing.Size(91, 23); + this.btnStop.TabIndex = 5; + this.btnStop.Text = "Stop"; + this.btnStop.UseVisualStyleBackColor = true; + this.btnStop.Click += new System.EventHandler(this.btnStop_Click); + // + // lblTransport + // + this.lblTransport.AutoSize = true; + this.lblTransport.Location = new System.Drawing.Point(12, 43); + this.lblTransport.Name = "lblTransport"; + this.lblTransport.Size = new System.Drawing.Size(55, 13); + this.lblTransport.TabIndex = 14; + this.lblTransport.Text = "Transport:"; + // + // lblAddress + // + this.lblAddress.AutoSize = true; + this.lblAddress.Location = new System.Drawing.Point(12, 9); + this.lblAddress.Name = "lblAddress"; + this.lblAddress.Size = new System.Drawing.Size(61, 13); + this.lblAddress.TabIndex = 13; + this.lblAddress.Text = "IP Address:"; + // + // chkIntegratedWindowsAuthentication + // + this.chkIntegratedWindowsAuthentication.AutoSize = true; + this.chkIntegratedWindowsAuthentication.Checked = true; + this.chkIntegratedWindowsAuthentication.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkIntegratedWindowsAuthentication.Location = new System.Drawing.Point(79, 94); + this.chkIntegratedWindowsAuthentication.Name = "chkIntegratedWindowsAuthentication"; + this.chkIntegratedWindowsAuthentication.Size = new System.Drawing.Size(192, 17); + this.chkIntegratedWindowsAuthentication.TabIndex = 15; + this.chkIntegratedWindowsAuthentication.Text = "Integrated Windows Authentication"; + this.chkIntegratedWindowsAuthentication.UseVisualStyleBackColor = true; + // + // ServerUI + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(442, 123); + this.Controls.Add(this.chkIntegratedWindowsAuthentication); + this.Controls.Add(this.lblTransport); + this.Controls.Add(this.lblAddress); + this.Controls.Add(this.btnStop); + this.Controls.Add(this.btnStart); + this.Controls.Add(this.rbtDirectTCPTransport); + this.Controls.Add(this.rbtNetBiosOverTCP); + this.Controls.Add(this.comboIPAddress); + this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(450, 150); + this.MinimumSize = new System.Drawing.Size(450, 150); + this.Name = "ServerUI"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.Text = "SMB Server"; + this.Load += new System.EventHandler(this.ServerUI_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.ComboBox comboIPAddress; + private System.Windows.Forms.RadioButton rbtNetBiosOverTCP; + private System.Windows.Forms.RadioButton rbtDirectTCPTransport; + private System.Windows.Forms.Button btnStart; + private System.Windows.Forms.Button btnStop; + private System.Windows.Forms.Label lblTransport; + private System.Windows.Forms.Label lblAddress; + private System.Windows.Forms.CheckBox chkIntegratedWindowsAuthentication; + } +} + diff --git a/SMBServer/ServerUI.cs b/SMBServer/ServerUI.cs new file mode 100644 index 0000000..f572d2a --- /dev/null +++ b/SMBServer/ServerUI.cs @@ -0,0 +1,218 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Net; +using System.Net.NetworkInformation; +using System.Text; +using System.Windows.Forms; +using System.Xml; +using SMBLibrary; +using SMBLibrary.Server; +using SMBLibrary.Server.Win32; +using Utilities; + +namespace SMBServer +{ + public partial class ServerUI : Form + { + public const string SettingsFileName = "Settings.xml"; + private SMBLibrary.Server.SMBServer m_server; + private SMBLibrary.Server.NameServer m_nameServer; + + public ServerUI() + { + InitializeComponent(); + } + + private void ServerUI_Load(object sender, EventArgs e) + { + List localIPs = GetHostIPAddresses(); + KeyValuePairList list = new KeyValuePairList(); + list.Add("Any", IPAddress.Any); + foreach (IPAddress address in localIPs) + { + list.Add(address.ToString(), address); + } + comboIPAddress.DataSource = list; + comboIPAddress.DisplayMember = "Key"; + comboIPAddress.ValueMember = "Value"; + } + + private void btnStart_Click(object sender, EventArgs e) + { + IPAddress serverAddress = (IPAddress)comboIPAddress.SelectedValue; + SMBTransportType transportType; + if (rbtNetBiosOverTCP.Checked) + { + transportType = SMBTransportType.NetBiosOverTCP; + } + else + { + transportType = SMBTransportType.DirectTCPTransport; + } + + INTLMAuthenticationProvider provider; + if (chkIntegratedWindowsAuthentication.Checked) + { + provider = new Win32UserCollection(); + + } + else + { + UserCollection users; + try + { + users = ReadUserSettings(); + } + catch + { + MessageBox.Show("Cannot read " + SettingsFileName, "Error"); + return; + } + + provider = new IndependentUserCollection(users); + } + + + List allUsers = provider.ListUsers(); + ShareCollection shares; + try + { + shares = ReadShareSettings(allUsers); + } + catch (Exception) + { + MessageBox.Show("Cannot read " + SettingsFileName, "Error"); + return; + } + + m_server = new SMBLibrary.Server.SMBServer(shares, provider, serverAddress, transportType); + + try + { + m_server.Start(); + if (transportType == SMBTransportType.NetBiosOverTCP) + { + m_nameServer = new NameServer(serverAddress); + m_nameServer.Start(); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "Error"); + return; + } + + btnStart.Enabled = false; + btnStop.Enabled = true; + comboIPAddress.Enabled = false; + rbtDirectTCPTransport.Enabled = false; + rbtNetBiosOverTCP.Enabled = false; + chkIntegratedWindowsAuthentication.Enabled = false; + } + + private XmlDocument GetSettingsXML() + { + string executableDirectory = Path.GetDirectoryName(Application.ExecutablePath) + "\\"; + XmlDocument document = GetXmlDocument(executableDirectory + SettingsFileName); + return document; + } + + private UserCollection ReadUserSettings() + { + UserCollection users = new UserCollection(); + XmlDocument document = GetSettingsXML(); + XmlNode usersNode = document.SelectSingleNode("Settings/Users"); + + foreach (XmlNode userNode in usersNode.ChildNodes) + { + string accountName = userNode.Attributes["AccountName"].Value; + string password = userNode.Attributes["Password"].Value; + users.Add(accountName, password); + } + return users; + } + + private ShareCollection ReadShareSettings(List allUsers) + { + ShareCollection shares = new ShareCollection(); + XmlDocument document = GetSettingsXML(); + XmlNode sharesNode = document.SelectSingleNode("Settings/Shares"); + + foreach (XmlNode shareNode in sharesNode.ChildNodes) + { + string shareName = shareNode.Attributes["Name"].Value; + string sharePath = shareNode.Attributes["Path"].Value; + + XmlNode readAccessNode = shareNode.SelectSingleNode("ReadAccess"); + List readAccess = ReadAccessList(readAccessNode, allUsers); + XmlNode writeAccessNode = shareNode.SelectSingleNode("WriteAccess"); + List writeAccess = ReadAccessList(writeAccessNode, allUsers); + shares.Add(shareName, readAccess, writeAccess, new DirectoryFileSystem(sharePath)); + } + return shares; + } + + private List ReadAccessList(XmlNode node, List allUsers) + { + List result = new List(); + if (node != null) + { + string accounts = node.Attributes["Accounts"].Value; + if (accounts == "*") + { + result.AddRange(allUsers); + } + else + { + string[] splitted = accounts.Split(','); + result.AddRange(splitted); + } + } + return result; + } + + private void btnStop_Click(object sender, EventArgs e) + { + m_server.Stop(); + btnStart.Enabled = true; + btnStop.Enabled = false; + comboIPAddress.Enabled = true; + rbtDirectTCPTransport.Enabled = true; + rbtNetBiosOverTCP.Enabled = true; + chkIntegratedWindowsAuthentication.Enabled = true; + + if (m_nameServer != null) + { + m_nameServer.Stop(); + } + } + + private static XmlDocument GetXmlDocument(string path) + { + XmlDocument doc = new XmlDocument(); + doc.Load(path); + return doc; + } + + private static List GetHostIPAddresses() + { + List result = new List(); + foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) + { + IPInterfaceProperties ipProperties = netInterface.GetIPProperties(); + foreach (UnicastIPAddressInformation addressInfo in ipProperties.UnicastAddresses) + { + if (addressInfo.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { + result.Add(addressInfo.Address); + } + } + } + return result; + } + } +} \ No newline at end of file diff --git a/SMBServer/ServerUI.resx b/SMBServer/ServerUI.resx new file mode 100644 index 0000000..ff31a6d --- /dev/null +++ b/SMBServer/ServerUI.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SMBServer/Settings.xml b/SMBServer/Settings.xml new file mode 100644 index 0000000..de4c880 --- /dev/null +++ b/SMBServer/Settings.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Utilities/ByteUtils/BigEndianReader.cs b/Utilities/ByteUtils/BigEndianReader.cs new file mode 100644 index 0000000..6eb767d --- /dev/null +++ b/Utilities/ByteUtils/BigEndianReader.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Utilities +{ + public class BigEndianReader + { + public static short ReadInt16(byte[] buffer, ref int offset) + { + offset += 2; + return BigEndianConverter.ToInt16(buffer, offset - 2); + } + + public static ushort ReadUInt16(byte[] buffer, ref int offset) + { + offset += 2; + return BigEndianConverter.ToUInt16(buffer, offset - 2); + } + + public static int ReadInt32(byte[] buffer, ref int offset) + { + offset += 4; + return BigEndianConverter.ToInt32(buffer, offset - 4); + } + + public static uint ReadUInt32(byte[] buffer, ref int offset) + { + offset += 4; + return BigEndianConverter.ToUInt32(buffer, offset - 4); + } + + public static long ReadInt64(byte[] buffer, ref int offset) + { + offset += 8; + return BigEndianConverter.ToInt64(buffer, offset - 8); + } + + public static ulong ReadUInt64(byte[] buffer, ref int offset) + { + offset += 8; + return BigEndianConverter.ToUInt64(buffer, offset - 8); + } + + public static Guid ReadGuidBytes(byte[] buffer, ref int offset) + { + offset += 16; + return BigEndianConverter.ToGuid(buffer, offset - 16); + } + + public static short ReadInt16(Stream stream) + { + byte[] buffer = new byte[2]; + stream.Read(buffer, 0, 2); + return BigEndianConverter.ToInt16(buffer, 0); + } + + public static ushort ReadUInt16(Stream stream) + { + byte[] buffer = new byte[2]; + stream.Read(buffer, 0, 2); + return BigEndianConverter.ToUInt16(buffer, 0); + } + + public static int ReadInt32(Stream stream) + { + byte[] buffer = new byte[4]; + stream.Read(buffer, 0, 4); + return BigEndianConverter.ToInt32(buffer, 0); + } + + public static uint ReadUInt32(Stream stream) + { + byte[] buffer = new byte[4]; + stream.Read(buffer, 0, 4); + return BigEndianConverter.ToUInt32(buffer, 0); + } + + public static long ReadInt64(Stream stream) + { + byte[] buffer = new byte[8]; + stream.Read(buffer, 0, 8); + return BigEndianConverter.ToInt64(buffer, 0); + } + + public static ulong ReadUInt64(Stream stream) + { + byte[] buffer = new byte[8]; + stream.Read(buffer, 0, 8); + return BigEndianConverter.ToUInt64(buffer, 0); + } + + public static Guid ReadGuidBytes(Stream stream) + { + byte[] buffer = new byte[16]; + stream.Read(buffer, 0, 16); + return BigEndianConverter.ToGuid(buffer, 0); + } + } +} diff --git a/Utilities/ByteUtils/BigEndianWriter.cs b/Utilities/ByteUtils/BigEndianWriter.cs new file mode 100644 index 0000000..8c01ca7 --- /dev/null +++ b/Utilities/ByteUtils/BigEndianWriter.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Utilities +{ + public class BigEndianWriter + { + public static void WriteInt16(byte[] buffer, int offset, short value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteInt16(byte[] buffer, ref int offset, short value) + { + WriteInt16(buffer, offset, value); + offset += 2; + } + + public static void WriteUInt16(byte[] buffer, int offset, ushort value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteUInt16(byte[] buffer, ref int offset, ushort value) + { + WriteUInt16(buffer, offset, value); + offset += 2; + } + + public static void WriteInt32(byte[] buffer, int offset, int value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteInt32(byte[] buffer, ref int offset, int value) + { + WriteInt32(buffer, offset, value); + offset += 4; + } + + public static void WriteUInt32(byte[] buffer, int offset, uint value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteUInt32(byte[] buffer, ref int offset, uint value) + { + WriteUInt32(buffer, offset, value); + offset += 4; + } + + public static void WriteInt64(byte[] buffer, int offset, long value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteInt64(byte[] buffer, ref int offset, long value) + { + WriteInt64(buffer, offset, value); + offset += 8; + } + + public static void WriteUInt64(byte[] buffer, int offset, ulong value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteUInt64(byte[] buffer, ref int offset, ulong value) + { + WriteUInt64(buffer, offset, value); + offset += 8; + } + + public static void WriteGuidBytes(byte[] buffer, int offset, Guid value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteGuidBytes(byte[] buffer, ref int offset, Guid value) + { + WriteGuidBytes(buffer, offset, value); + offset += 16; + } + + public static void WriteInt16(Stream stream, short value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + + public static void WriteUInt16(Stream stream, ushort value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + + public static void WriteInt32(Stream stream, int value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + + public static void WriteUInt32(Stream stream, uint value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + + public static void WriteInt64(Stream stream, long value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + + public static void WriteUInt64(Stream stream, ulong value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + + public static void WriteGuidBytes(Stream stream, Guid value) + { + byte[] bytes = BigEndianConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + } +} diff --git a/Utilities/ByteUtils/ByteReader.cs b/Utilities/ByteUtils/ByteReader.cs new file mode 100644 index 0000000..f1580ae --- /dev/null +++ b/Utilities/ByteUtils/ByteReader.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Utilities +{ + public class ByteReader + { + public static byte ReadByte(byte[] buffer, int offset) + { + return buffer[offset]; + } + + public static byte ReadByte(byte[] buffer, ref int offset) + { + offset++; + return buffer[offset - 1]; + } + + public static byte[] ReadBytes(byte[] buffer, int offset, int length) + { + byte[] result = new byte[length]; + Array.Copy(buffer, offset, result, 0, length); + return result; + } + + public static byte[] ReadBytes(byte[] buffer, ref int offset, int length) + { + offset += length; + return ReadBytes(buffer, offset - length, length); + } + + /// + /// Will return the ANSI string stored in the buffer + /// + public static string ReadAnsiString(byte[] buffer, int offset, int count) + { + // ASCIIEncoding.ASCII.GetString will convert some values to '?' (byte value of 63) + // Any codepage will do, but the only one that Mono supports is 28591. + return ASCIIEncoding.GetEncoding(28591).GetString(buffer, offset, count); + } + + public static string ReadAnsiString(byte[] buffer, ref int offset, int count) + { + offset += count; + return ReadAnsiString(buffer, offset - count, count); + } + + public static string ReadUTF16String(byte[] buffer, int offset, int numberOfCharacters) + { + int numberOfBytes = numberOfCharacters * 2; + return Encoding.Unicode.GetString(buffer, offset, numberOfBytes); + } + + public static string ReadUTF16String(byte[] buffer, ref int offset, int numberOfCharacters) + { + int numberOfBytes = numberOfCharacters * 2; + offset += numberOfBytes; + return ReadUTF16String(buffer, offset - numberOfBytes, numberOfCharacters); + } + + public static string ReadNullTerminatedAnsiString(byte[] buffer, int offset) + { + StringBuilder builder = new StringBuilder(); + char c = (char)ByteReader.ReadByte(buffer, offset); + while (c != '\0') + { + builder.Append(c); + offset++; + c = (char)ByteReader.ReadByte(buffer, offset); + } + return builder.ToString(); + } + + public static string ReadNullTerminatedUTF16String(byte[] buffer, int offset) + { + StringBuilder builder = new StringBuilder(); + char c = (char)LittleEndianConverter.ToUInt16(buffer, offset); + while (c != 0) + { + builder.Append(c); + offset += 2; + c = (char)LittleEndianConverter.ToUInt16(buffer, offset); + } + return builder.ToString(); + } + + public static string ReadNullTerminatedAnsiString(byte[] buffer, ref int offset) + { + string result = ReadNullTerminatedAnsiString(buffer, offset); + offset += result.Length + 1; + return result; + } + + public static string ReadNullTerminatedUTF16String(byte[] buffer, ref int offset) + { + string result = ReadNullTerminatedUTF16String(buffer, offset); + offset += result.Length * 2 + 2; + return result; + } + + public static byte[] ReadBytes(Stream stream, int count) + { + MemoryStream temp = new MemoryStream(); + ByteUtils.CopyStream(stream, temp, count); + return temp.ToArray(); + } + + /// + /// Return all bytes from current stream position to the end of the stream + /// + public static byte[] ReadAllBytes(Stream stream) + { + MemoryStream temp = new MemoryStream(); + ByteUtils.CopyStream(stream, temp); + return temp.ToArray(); + } + } +} diff --git a/Utilities/ByteUtils/ByteUtils.cs b/Utilities/ByteUtils/ByteUtils.cs new file mode 100644 index 0000000..d1eb83f --- /dev/null +++ b/Utilities/ByteUtils/ByteUtils.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Utilities +{ + public class ByteUtils + { + public static byte[] Concatenate(byte[] a, byte[] b) + { + byte[] result = new byte[a.Length + b.Length]; + Array.Copy(a, 0, result, 0, a.Length); + Array.Copy(b, 0, result, a.Length, b.Length); + return result; + } + + public static bool AreByteArraysEqual(byte[] array1, byte[] array2) + { + if (array1.Length != array2.Length) + { + return false; + } + + for (int index = 0; index < array1.Length; index++) + { + if (array1[index] != array2[index]) + { + return false; + } + } + + return true; + } + + public static long CopyStream(Stream input, Stream output) + { + // input may not support seeking, so don't use input.Position + return CopyStream(input, output, Int64.MaxValue); + } + + public static long CopyStream(Stream input, Stream output, long count) + { + const int MaxBufferSize = 4194304; // 4 MB + int bufferSize = (int)Math.Min(MaxBufferSize, count); + byte[] buffer = new byte[bufferSize]; + long totalBytesRead = 0; + while (totalBytesRead < count) + { + int numberOfBytesToRead = (int)Math.Min(bufferSize, count - totalBytesRead); + int bytesRead = input.Read(buffer, 0, numberOfBytesToRead); + totalBytesRead += bytesRead; + output.Write(buffer, 0, bytesRead); + if (bytesRead == 0) // no more bytes to read from input stream + { + return totalBytesRead; + } + } + return totalBytesRead; + } + } +} diff --git a/Utilities/ByteUtils/ByteWriter.cs b/Utilities/ByteUtils/ByteWriter.cs new file mode 100644 index 0000000..aba1657 --- /dev/null +++ b/Utilities/ByteUtils/ByteWriter.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Utilities +{ + public class ByteWriter + { + public static void WriteByte(byte[] buffer, int offset, byte value) + { + buffer[offset] = value; + } + + public static void WriteByte(byte[] buffer, ref int offset, byte value) + { + buffer[offset] = value; + offset += 1; + } + + public static void WriteBytes(byte[] buffer, int offset, byte[] bytes) + { + WriteBytes(buffer, offset, bytes, bytes.Length); + } + + public static void WriteBytes(byte[] buffer, ref int offset, byte[] bytes) + { + WriteBytes(buffer, offset, bytes); + offset += bytes.Length; + } + + public static void WriteBytes(byte[] buffer, int offset, byte[] bytes, int length) + { + Array.Copy(bytes, 0, buffer, offset, length); + } + + public static void WriteBytes(byte[] buffer, ref int offset, byte[] bytes, int length) + { + Array.Copy(bytes, 0, buffer, offset, length); + offset += length; + } + + public static void WriteAnsiString(byte[] buffer, int offset, string value) + { + WriteAnsiString(buffer, offset, value, value.Length); + } + + public static void WriteAnsiString(byte[] buffer, ref int offset, string value) + { + WriteAnsiString(buffer, ref offset, value, value.Length); + } + + public static void WriteAnsiString(byte[] buffer, int offset, string value, int maximumLength) + { + byte[] bytes = ASCIIEncoding.GetEncoding(28591).GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, Math.Min(value.Length, maximumLength)); + } + + public static void WriteAnsiString(byte[] buffer, ref int offset, string value, int fieldLength) + { + WriteAnsiString(buffer, offset, value, fieldLength); + offset += fieldLength; + } + + public static void WriteUTF16String(byte[] buffer, int offset, string value) + { + WriteUTF16String(buffer, offset, value, value.Length); + } + + public static void WriteUTF16String(byte[] buffer, ref int offset, string value) + { + WriteUTF16String(buffer, ref offset, value, value.Length); + } + + public static void WriteUTF16String(byte[] buffer, int offset, string value, int maximumNumberOfCharacters) + { + byte[] bytes = UnicodeEncoding.Unicode.GetBytes(value); + int maximumNumberOfBytes = Math.Min(value.Length, maximumNumberOfCharacters) * 2; + Array.Copy(bytes, 0, buffer, offset, maximumNumberOfBytes); + } + + public static void WriteUTF16String(byte[] buffer, ref int offset, string value, int numberOfCharacters) + { + WriteUTF16String(buffer, offset, value, numberOfCharacters); + offset += numberOfCharacters * 2; + } + + public static void WriteNullTerminatedAnsiString(byte[] buffer, int offset, string value) + { + WriteAnsiString(buffer, offset, value); + WriteByte(buffer, offset + value.Length, 0x00); + } + + public static void WriteNullTerminatedAnsiString(byte[] buffer, ref int offset, string value) + { + WriteNullTerminatedAnsiString(buffer, offset, value); + offset += value.Length + 1; + } + + public static void WriteNullTerminatedUTF16String(byte[] buffer, int offset, string value) + { + WriteUTF16String(buffer, offset, value); + WriteBytes(buffer, offset + value.Length * 2, new byte[] { 0x00, 0x00 }); + } + + public static void WriteNullTerminatedUTF16String(byte[] buffer, ref int offset, string value) + { + WriteNullTerminatedUTF16String(buffer, offset, value); + offset += value.Length * 2 + 2; + } + + public static void WriteBytes(Stream stream, byte[] bytes) + { + stream.Write(bytes, 0, bytes.Length); + } + + public static void WriteBytes(Stream stream, byte[] bytes, int count) + { + stream.Write(bytes, 0, count); + } + + public static void WriteAnsiString(Stream stream, string value) + { + WriteAnsiString(stream, value, value.Length); + } + + public static void WriteAnsiString(Stream stream, string value, int fieldLength) + { + byte[] bytes = ASCIIEncoding.GetEncoding(28591).GetBytes(value); + stream.Write(bytes, 0, Math.Min(bytes.Length, fieldLength)); + if (bytes.Length < fieldLength) + { + byte[] zeroFill = new byte[fieldLength - bytes.Length]; + stream.Write(zeroFill, 0, zeroFill.Length); + } + } + + public static void WriteUTF16String(Stream stream, string value) + { + byte[] bytes = UnicodeEncoding.Unicode.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + } +} diff --git a/Utilities/ByteUtils/LittleEndianReader.cs b/Utilities/ByteUtils/LittleEndianReader.cs new file mode 100644 index 0000000..6d05e74 --- /dev/null +++ b/Utilities/ByteUtils/LittleEndianReader.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Utilities +{ + public class LittleEndianReader + { + public static ushort ReadUInt16(byte[] buffer, ref int offset) + { + offset += 2; + return LittleEndianConverter.ToUInt16(buffer, offset - 2); + } + + public static uint ReadUInt32(byte[] buffer, ref int offset) + { + offset += 4; + return LittleEndianConverter.ToUInt32(buffer, offset - 4); + } + + public static ulong ReadUInt64(byte[] buffer, ref int offset) + { + offset += 8; + return LittleEndianConverter.ToUInt64(buffer, offset - 8); + } + + public static Guid ReadGuid(byte[] buffer, ref int offset) + { + offset += 16; + return LittleEndianConverter.ToGuid(buffer, offset - 16); + } + } +} diff --git a/Utilities/ByteUtils/LittleEndianWriter.cs b/Utilities/ByteUtils/LittleEndianWriter.cs new file mode 100644 index 0000000..30462ef --- /dev/null +++ b/Utilities/ByteUtils/LittleEndianWriter.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Utilities +{ + public class LittleEndianWriter + { + public static void WriteUInt16(byte[] buffer, int offset, ushort value) + { + byte[] bytes = LittleEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteUInt16(byte[] buffer, ref int offset, ushort value) + { + WriteUInt16(buffer, offset, value); + offset += 2; + } + + public static void WriteInt16(byte[] buffer, int offset, short value) + { + byte[] bytes = LittleEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteUInt32(byte[] buffer, int offset, uint value) + { + byte[] bytes = LittleEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteUInt32(byte[] buffer, ref int offset, uint value) + { + WriteUInt32(buffer, offset, value); + offset += 4; + } + + public static void WriteInt32(byte[] buffer, int offset, int value) + { + byte[] bytes = LittleEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteInt32(byte[] buffer, ref int offset, int value) + { + WriteInt32(buffer, offset, value); + offset += 4; + } + + public static void WriteUInt64(byte[] buffer, int offset, ulong value) + { + byte[] bytes = LittleEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteUInt64(byte[] buffer, ref int offset, ulong value) + { + WriteUInt64(buffer, offset, value); + offset += 8; + } + + public static void WriteInt64(byte[] buffer, int offset, long value) + { + byte[] bytes = LittleEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteGuidBytes(byte[] buffer, int offset, Guid value) + { + byte[] bytes = LittleEndianConverter.GetBytes(value); + Array.Copy(bytes, 0, buffer, offset, bytes.Length); + } + + public static void WriteGuidBytes(byte[] buffer, ref int offset, Guid value) + { + WriteGuidBytes(buffer, offset, value); + offset += 16; + } + + public static void WriteUInt16(Stream stream, ushort value) + { + byte[] bytes = LittleEndianConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + + public static void WriteInt32(Stream stream, int value) + { + byte[] bytes = LittleEndianConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + + public static void WriteUInt32(Stream stream, uint value) + { + byte[] bytes = LittleEndianConverter.GetBytes(value); + stream.Write(bytes, 0, bytes.Length); + } + } +} diff --git a/Utilities/Conversion/BigEndianConverter.cs b/Utilities/Conversion/BigEndianConverter.cs new file mode 100644 index 0000000..ce902b7 --- /dev/null +++ b/Utilities/Conversion/BigEndianConverter.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Utilities +{ + public class BigEndianConverter + { + public static ushort ToUInt16(byte[] buffer, int offset) + { + return (ushort)((buffer[offset + 0] << 8) | (buffer[offset + 1] << 0)); + } + + public static short ToInt16(byte[] buffer, int offset) + { + return (short)ToUInt16(buffer, offset); + } + + public static uint ToUInt32(byte[] buffer, int offset) + { + return (uint)((buffer[offset + 0] << 24) | (buffer[offset + 1] << 16) + | (buffer[offset + 2] << 8) | (buffer[offset + 3] << 0)); + } + + public static int ToInt32(byte[] buffer, int offset) + { + return (int)ToUInt32(buffer, offset); + } + + public static ulong ToUInt64(byte[] buffer, int offset) + { + return (((ulong)ToUInt32(buffer, offset + 0)) << 32) | ToUInt32(buffer, offset + 4); + } + + public static long ToInt64(byte[] buffer, int offset) + { + return (long)ToUInt64(buffer, offset); + } + + public static Guid ToGuid(byte[] buffer, int offset) + { + return new Guid( + ToUInt32(buffer, offset + 0), + ToUInt16(buffer, offset + 4), + ToUInt16(buffer, offset + 6), + buffer[offset + 8], + buffer[offset + 9], + buffer[offset + 10], + buffer[offset + 11], + buffer[offset + 12], + buffer[offset + 13], + buffer[offset + 14], + buffer[offset + 15]); + } + + public static byte[] GetBytes(ushort value) + { + byte[] result = new byte[2]; + result[0] = (byte)((value >> 8) & 0xFF); + result[1] = (byte)((value >> 0) & 0xFF); + return result; + } + + public static byte[] GetBytes(short value) + { + return GetBytes((ushort)value); + } + + public static byte[] GetBytes(uint value) + { + byte[] result = new byte[4]; + result[0] = (byte)((value >> 24) & 0xFF); + result[1] = (byte)((value >> 16) & 0xFF); + result[2] = (byte)((value >> 8) & 0xFF); + result[3] = (byte)((value >> 0) & 0xFF); + + return result; + } + + public static byte[] GetBytes(int value) + { + return GetBytes((uint)value); + } + + public static byte[] GetBytes(ulong value) + { + byte[] result = new byte[8]; + Array.Copy(GetBytes((uint)(value >> 32)), 0, result, 0, 4); + Array.Copy(GetBytes((uint)(value & 0xFFFFFFFF)), 0, result, 4, 4); + + return result; + } + + public static byte[] GetBytes(long value) + { + return GetBytes((ulong)value); + } + + public static byte[] GetBytes(Guid value) + { + byte[] result = value.ToByteArray(); + if (BitConverter.IsLittleEndian) + { + // reverse first 4 bytes + byte temp = result[0]; + result[0] = result[3]; + result[3] = temp; + + temp = result[1]; + result[1] = result[2]; + result[2] = temp; + + // reverse next 2 bytes + temp = result[4]; + result[4] = result[5]; + result[5] = temp; + + // reverse next 2 bytes + temp = result[6]; + result[6] = result[7]; + result[7] = temp; + } + return result; + } + } +} diff --git a/Utilities/Conversion/Conversion.SimpleTypes.cs b/Utilities/Conversion/Conversion.SimpleTypes.cs new file mode 100644 index 0000000..72fec08 --- /dev/null +++ b/Utilities/Conversion/Conversion.SimpleTypes.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Web; + +namespace Utilities +{ + public partial class Conversion + { + public static int ToInt32(object obj) + { + return ToInt32(obj, 0); + } + + public static int ToInt32(object obj, int defaultValue) + { + int result = defaultValue; + if (obj != null) + { + try + { + result = Convert.ToInt32(obj); + } + catch + {} + } + return result; + } + + public static long ToInt64(object obj) + { + return ToInt64(obj, 0); + } + + public static long ToInt64(object obj, long defaultValue) + { + long result = defaultValue; + if (obj != null) + { + try + { + result = Convert.ToInt64(obj); + } + catch + { } + } + return result; + } + } +} diff --git a/Utilities/Conversion/LittleEndianConverter.cs b/Utilities/Conversion/LittleEndianConverter.cs new file mode 100644 index 0000000..85b7ead --- /dev/null +++ b/Utilities/Conversion/LittleEndianConverter.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Utilities +{ + public class LittleEndianConverter + { + public static ushort ToUInt16(byte[] buffer, int offset) + { + return (ushort)((buffer[offset + 1] << 8) | (buffer[offset + 0] << 0)); + } + + public static short ToInt16(byte[] buffer, int offset) + { + return (short)ToUInt16(buffer, offset); + } + + public static uint ToUInt32(byte[] buffer, int offset) + { + return (uint)((buffer[offset + 3] << 24) | (buffer[offset + 2] << 16) + | (buffer[offset + 1] << 8) | (buffer[offset + 0] << 0)); + } + + public static int ToInt32(byte[] buffer, int offset) + { + return (int)ToUInt32(buffer, offset); + } + + public static ulong ToUInt64(byte[] buffer, int offset) + { + return (((ulong)ToUInt32(buffer, offset + 4)) << 32) | ToUInt32(buffer, offset + 0); + } + + public static long ToInt64(byte[] buffer, int offset) + { + return (long)ToUInt64(buffer, offset); + } + + public static float ToFloat32(byte[] buffer, int offset) + { + byte[] bytes = new byte[4]; + Array.Copy(buffer, offset, bytes, 0, 4); + if (!BitConverter.IsLittleEndian) + { + // reverse the order of 'bytes' + for (int index = 0; index < 2; index++) + { + byte temp = bytes[index]; + bytes[index] = bytes[3 - index]; + bytes[3 - index] = temp; + } + } + return BitConverter.ToSingle(bytes, 0); + } + + public static double ToFloat64(byte[] buffer, int offset) + { + byte[] bytes = new byte[8]; + Array.Copy(buffer, offset, bytes, 0, 8); + if (!BitConverter.IsLittleEndian) + { + // reverse the order of 'bytes' + for(int index = 0; index < 4; index++) + { + byte temp = bytes[index]; + bytes[index] = bytes[7 - index]; + bytes[7 - index] = temp; + } + } + return BitConverter.ToDouble(bytes, 0); + } + + public static Guid ToGuid(byte[] buffer, int offset) + { + return new Guid( + ToUInt32(buffer, offset + 0), + ToUInt16(buffer, offset + 4), + ToUInt16(buffer, offset + 6), + buffer[offset + 8], + buffer[offset + 9], + buffer[offset + 10], + buffer[offset + 11], + buffer[offset + 12], + buffer[offset + 13], + buffer[offset + 14], + buffer[offset + 15]); + } + + public static byte[] GetBytes(ushort value) + { + byte[] result = new byte[2]; + result[0] = (byte)((value >> 0) & 0xFF); + result[1] = (byte)((value >> 8) & 0xFF); + return result; + } + + public static byte[] GetBytes(short value) + { + return GetBytes((ushort)value); + } + + public static byte[] GetBytes(uint value) + { + byte[] result = new byte[4]; + result[0] = (byte)((value >> 0) & 0xFF); + result[1] = (byte)((value >> 8) & 0xFF); + result[2] = (byte)((value >> 16) & 0xFF); + result[3] = (byte)((value >> 24) & 0xFF); + + return result; + } + + public static byte[] GetBytes(int value) + { + return GetBytes((uint)value); + } + + public static byte[] GetBytes(ulong value) + { + byte[] result = new byte[8]; + Array.Copy(GetBytes((uint)(value & 0xFFFFFFFF)), 0, result, 0, 4); + Array.Copy(GetBytes((uint)(value >> 32)), 0, result, 4, 4); + + return result; + } + + public static byte[] GetBytes(long value) + { + return GetBytes((ulong)value); + } + + public static byte[] GetBytes(Guid value) + { + byte[] result = value.ToByteArray(); + if (!BitConverter.IsLittleEndian) + { + // reverse first 4 bytes + byte temp = result[0]; + result[0] = result[3]; + result[3] = temp; + + temp = result[1]; + result[1] = result[2]; + result[2] = temp; + + // reverse next 2 bytes + temp = result[4]; + result[4] = result[5]; + result[5] = temp; + + // reverse next 2 bytes + temp = result[6]; + result[6] = result[7]; + result[7] = temp; + } + return result; + } + } +} diff --git a/Utilities/Cryptography/CRC32.cs b/Utilities/Cryptography/CRC32.cs new file mode 100644 index 0000000..c909b2e --- /dev/null +++ b/Utilities/Cryptography/CRC32.cs @@ -0,0 +1,120 @@ +using System; +using System.Security.Cryptography; + +namespace Utilities +{ + // Author: Damien Guard (damieng@gmail.com) + // http://damieng.com/blog/2006/08/08/calculating_crc32_in_c_and_net + public class CRC32 : HashAlgorithm + { + public const UInt32 DefaultPolynomial = 0xedb88320; + public const UInt32 DefaultSeed = 0xffffffff; + + private UInt32 hash; + private UInt32 seed; + private UInt32[] table; + private static UInt32[] defaultTable; + + public CRC32() + { + table = InitializeTable(DefaultPolynomial); + seed = DefaultSeed; + Initialize(); + } + + public CRC32(UInt32 polynomial, UInt32 seed) + { + table = InitializeTable(polynomial); + this.seed = seed; + Initialize(); + } + + public override void Initialize() + { + hash = seed; + } + + protected override void HashCore(byte[] buffer, int start, int length) + { + hash = CalculateHash(table, hash, buffer, start, length); + } + + protected override byte[] HashFinal() + { + byte[] hashBuffer = UInt32ToBigEndianBytes(~hash); + this.HashValue = hashBuffer; + return hashBuffer; + } + + public override int HashSize + { + get { return 32; } + } + + public static UInt32 Compute(byte[] buffer) + { + return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length); + } + + public static UInt32 Compute(UInt32 seed, byte[] buffer) + { + return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length); + } + + public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer) + { + return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length); + } + + private static UInt32[] InitializeTable(UInt32 polynomial) + { + if (polynomial == DefaultPolynomial && defaultTable != null) + return defaultTable; + + UInt32[] createTable = new UInt32[256]; + for (int i = 0; i < 256; i++) + { + UInt32 entry = (UInt32)i; + for (int j = 0; j < 8; j++) + if ((entry & 1) == 1) + entry = (entry >> 1) ^ polynomial; + else + entry = entry >> 1; + createTable[i] = entry; + } + + if (polynomial == DefaultPolynomial) + defaultTable = createTable; + + return createTable; + } + + private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size) + { + UInt32 crc = seed; + for (int i = start; i < size; i++) + unchecked + { + crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff]; + } + return crc; + } + + private byte[] UInt32ToBigEndianBytes(UInt32 x) + { + return new byte[] { + (byte)((x >> 24) & 0xff), + (byte)((x >> 16) & 0xff), + (byte)((x >> 8) & 0xff), + (byte)(x & 0xff) + }; + } + + // Added by Tal Aloni 2013.07.11 + public static uint UPDC32(byte octet, uint crc) + { + uint[] table = InitializeTable(DefaultPolynomial); + return (table[((crc) ^ ((byte)octet)) & 0xff] ^ ((crc) >> 8)); + } + } +} diff --git a/Utilities/Generics/KeyValuePairList.cs b/Utilities/Generics/KeyValuePairList.cs new file mode 100644 index 0000000..57fc465 --- /dev/null +++ b/Utilities/Generics/KeyValuePairList.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; + +namespace Utilities +{ + public partial class KeyValuePairList : List> + { + public bool ContainsKey(TKey key) + { + return (this.IndexOf(key) != -1); + } + + public int IndexOf(TKey key) + { + for (int index = 0; index < this.Count; index++) + { + if (this[index].Key.Equals(key)) + { + return index; + } + } + + return -1; + } + + public TValue ValueOf(TKey key) + { + for (int index = 0; index < this.Count; index++) + { + if (this[index].Key.Equals(key)) + { + return this[index].Value; + } + } + + return default(TValue); + } + + public void Add(TKey key, TValue value) + { + this.Add(new KeyValuePair(key, value)); + } + + public List Keys + { + get + { + List result = new List(); + foreach (KeyValuePair entity in this) + { + result.Add(entity.Key); + } + return result; + } + } + + public List Values + { + get + { + List result = new List(); + foreach (KeyValuePair entity in this) + { + result.Add(entity.Value); + } + return result; + } + } + } +} diff --git a/Utilities/Generics/Map.cs b/Utilities/Generics/Map.cs new file mode 100644 index 0000000..5a83dc5 --- /dev/null +++ b/Utilities/Generics/Map.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Utilities +{ + /// + /// Based on: + /// http://stackoverflow.com/questions/10966331/two-way-bidirectional-dictionary-in-c + /// + public class Map + { + private Dictionary m_forward = new Dictionary(); + private Dictionary m_reverse = new Dictionary(); + + public Map() + { + m_forward = new Dictionary(); + m_reverse = new Dictionary(); + } + + public void Add(T1 key, T2 value) + { + m_forward.Add(key, value); + m_reverse.Add(value, key); + } + + public bool ContainsKey(T1 key) + { + return m_forward.ContainsKey(key); + } + + public bool ContainsValue(T2 value) + { + return m_reverse.ContainsKey(value); + } + + public T2 this[T1 key] + { + get + { + return m_forward[key]; + } + } + + public T1 GetKey(T2 value) + { + return m_reverse[value]; + } + } +} diff --git a/Utilities/IFileSystem/FileSystem.cs b/Utilities/IFileSystem/FileSystem.cs new file mode 100644 index 0000000..4575867 --- /dev/null +++ b/Utilities/IFileSystem/FileSystem.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Utilities +{ + public abstract class FileSystem : IFileSystem + { + public abstract FileSystemEntry GetEntry(string path); + public abstract FileSystemEntry CreateFile(string path); + public abstract FileSystemEntry CreateDirectory(string path); + public abstract void Move(string source, string destination); + public abstract void Delete(string path); + public abstract List ListEntriesInDirectory(string path); + public abstract Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share); + public abstract void SetAttributes(string path, bool? isHidden, bool? isReadonly, bool? isArchived); + public abstract void SetDates(string path, DateTime? creationDT, DateTime? lastWriteDT, DateTime? lastAccessDT); + + public List ListEntriesInRootDirectory() + { + return ListEntriesInDirectory(@"\"); + } + + public void CopyFile(string sourcePath, string destinationPath) + { + const int bufferLength = 1024 * 1024; + FileSystemEntry sourceFile = GetEntry(sourcePath); + FileSystemEntry destinationFile = GetEntry(destinationPath); + if (sourceFile == null | sourceFile.IsDirectory) + { + throw new FileNotFoundException(); + } + + if (destinationFile != null && destinationFile.IsDirectory) + { + throw new ArgumentException("Destination cannot be a directory"); + } + + if (destinationFile == null) + { + destinationFile = CreateFile(destinationPath); + } + Stream sourceStream = OpenFile(sourcePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + Stream destinationStream = OpenFile(destinationPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); + while (sourceStream.Position < sourceStream.Length) + { + int readSize = (int)Math.Max(bufferLength, sourceStream.Length - sourceStream.Position); + byte[] buffer = new byte[readSize]; + sourceStream.Read(buffer, 0, buffer.Length); + destinationStream.Write(buffer, 0, buffer.Length); + } + sourceStream.Close(); + destinationStream.Close(); + } + + public abstract string Name + { + get; + } + + public abstract long Size + { + get; + } + + public abstract long FreeSpace + { + get; + } + + public static string GetParentDirectory(string path) + { + if (path == String.Empty) + { + path = @"\"; + } + + if (!path.StartsWith(@"\")) + { + throw new ArgumentException("Invalid path"); + } + + if (path.Length > 1 && path.EndsWith(@"\")) + { + path = path.Substring(0, path.Length - 1); + } + + int separatorIndex = path.LastIndexOf(@"\"); + return path.Substring(0, separatorIndex + 1); + } + + /// + /// Will append a trailing slash to a directory path if not already present + /// + /// + /// + public static string GetDirectoryPath(string path) + { + if (path.EndsWith(@"\")) + { + return path; + } + else + { + return path + @"\"; + } + } + } +} diff --git a/Utilities/IFileSystem/FileSystemEntry.cs b/Utilities/IFileSystem/FileSystemEntry.cs new file mode 100644 index 0000000..59e1d8b --- /dev/null +++ b/Utilities/IFileSystem/FileSystemEntry.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Utilities +{ + public class FileSystemEntry + { + /// + /// Full Path. Directory path should end with a trailing slash. + /// + public string FullName; + public string Name; + public bool IsDirectory; + public ulong Size; + public DateTime CreationTime; + public DateTime LastWriteTime; + public DateTime LastAccessTime; + public bool IsHidden; + public bool IsReadonly; + public bool IsArchived; + + public FileSystemEntry(string fullName, string name, bool isDirectory, ulong size, DateTime creationTime, DateTime lastWriteTime, DateTime lastAccessTime, bool isHidden, bool isReadonly, bool isArchived) + { + FullName = fullName; + Name = name; + IsDirectory = isDirectory; + Size = size; + CreationTime = creationTime; + LastWriteTime = lastWriteTime; + LastAccessTime = lastAccessTime; + IsHidden = isHidden; + IsReadonly = isHidden; + IsArchived = isHidden; + + if (isDirectory) + { + FullName = FileSystem.GetDirectoryPath(FullName); + } + } + + public FileSystemEntry Clone() + { + FileSystemEntry clone = (FileSystemEntry)MemberwiseClone(); + return clone; + } + } +} diff --git a/Utilities/IFileSystem/IFileSystem.cs b/Utilities/IFileSystem/IFileSystem.cs new file mode 100644 index 0000000..1ada613 --- /dev/null +++ b/Utilities/IFileSystem/IFileSystem.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Utilities +{ + public interface IFileSystem + { + FileSystemEntry GetEntry(string path); + FileSystemEntry CreateFile(string path); + FileSystemEntry CreateDirectory(string path); + void Move(string source, string destination); + void Delete(string path); + List ListEntriesInDirectory(string path); + Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share); + void SetAttributes(string path, bool? isHidden, bool? isReadonly, bool? isArchived); + void SetDates(string path, DateTime? creationDT, DateTime? lastWriteDT, DateTime? lastAccessDT); + + string Name + { + get; + } + + long Size + { + get; + } + + long FreeSpace + { + get; + } + } +} diff --git a/Utilities/Properties/AssemblyInfo.cs b/Utilities/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..56878dc --- /dev/null +++ b/Utilities/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Utilities")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Tal Aloni")] +[assembly: AssemblyProduct("Utilities")] +[assembly: AssemblyCopyright("Copyright © Tal Aloni 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a4607d70-de29-4ae9-8b5a-d0b9cb405727")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Utilities/Strings/QuotedStringUtils.cs b/Utilities/Strings/QuotedStringUtils.cs new file mode 100644 index 0000000..c597c7a --- /dev/null +++ b/Utilities/Strings/QuotedStringUtils.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Utilities +{ + public class QuotedStringUtils + { + public static string Quote(string str) + { + return String.Format("\"{0}\"", str); + } + + public static string Unquote(string str) + { + string quote = '"'.ToString(); + if (str.Length >= 2 && str.StartsWith(quote) && str.EndsWith(quote)) + { + return str.Substring(1, str.Length - 2); + } + else + { + return str; + } + } + + public static bool IsQuoted(string str) + { + string quote = '"'.ToString(); + if (str.Length >= 2 && str.StartsWith(quote) && str.EndsWith(quote)) + { + return true; + } + else + { + return false; + } + } + + public static int IndexOfUnquotedChar(string str, char charToFind) + { + return IndexOfUnquotedChar(str, charToFind, 0); + } + + public static int IndexOfUnquotedChar(string str, char charToFind, int startIndex) + { + if (startIndex >= str.Length) + { + return -1; + } + + bool inQuote = false; + int index = startIndex; + while (index < str.Length) + { + if (str[index] == '"') + { + inQuote = !inQuote; + } + else if (!inQuote && str[index] == charToFind) + { + return index; + } + index++; + } + return -1; + } + + public static int IndexOfUnquotedString(string str, string stringToFind) + { + return IndexOfUnquotedString(str, stringToFind, 0); + } + + public static int IndexOfUnquotedString(string str, string stringToFind, int startIndex) + { + if (startIndex >= str.Length) + { + return -1; + } + + bool inQuote = false; + int index = startIndex; + while (index < str.Length) + { + if (str[index] == '"') + { + inQuote = !inQuote; + } + else if (!inQuote && str.Substring(index).StartsWith(stringToFind)) + { + return index; + } + index++; + } + return -1; + } + + public static List SplitIgnoreQuotedSeparators(string str, char separator) + { + List result = new List(); + int nextEntryIndex = 0; + int separatorIndex = IndexOfUnquotedChar(str, separator); + while (separatorIndex >= nextEntryIndex) + { + result.Add(str.Substring(nextEntryIndex, separatorIndex - nextEntryIndex)); + + nextEntryIndex = separatorIndex + 1; + separatorIndex = IndexOfUnquotedChar(str, separator, nextEntryIndex); + } + result.Add(str.Substring(nextEntryIndex)); + return result; + } + } +} diff --git a/Utilities/Threading/Parallel.cs b/Utilities/Threading/Parallel.cs new file mode 100644 index 0000000..49acf7f --- /dev/null +++ b/Utilities/Threading/Parallel.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace Utilities +{ + public delegate void ForDelegate(int i); + public delegate void DelegateProcess(); + + // Based on: + // http://coding-time.blogspot.pt/2008/03/implement-your-own-parallelfor-in-c.html + // C# 2.0 adaptation based on: + // http://dotnetgalactics.wordpress.com/2009/11/19/how-to-provide-a-parallel-for-loop-in-c2-0-2/ + public class Parallel + { + /// + /// Parallel for loop. Invokes given action, passing arguments + /// fromInclusive - toExclusive on multiple threads. + /// Returns when loop finished. + /// + public static void For(int fromInclusive, int toExclusive, ForDelegate forDelegate) + { + int chunkSize = 4; + For(fromInclusive, toExclusive, chunkSize, forDelegate); + } + + /// + /// Parallel for loop. Invokes given action, passing arguments + /// fromInclusive - toExclusive on multiple threads. + /// Returns when loop finished. + /// + /// + /// chunkSize = 1 makes items to be processed in order. + /// Bigger chunk size should reduce lock waiting time and thus + /// increase paralelism. + /// + public static void For(int fromInclusive, int toExclusive, int chunkSize, ForDelegate forDelegate) + { + int threadCount = Environment.ProcessorCount; + For(fromInclusive, toExclusive, chunkSize, threadCount, forDelegate); + } + + /// + /// Parallel for loop. Invokes given action, passing arguments + /// fromInclusive - toExclusive on multiple threads. + /// Returns when loop finished. + /// + /// + /// chunkSize = 1 makes items to be processed in order. + /// Bigger chunk size should reduce lock waiting time and thus + /// increase paralelism. + /// + /// number of process() threads + public static void For(int fromInclusive, int toExclusive, int chunkSize, int threadCount, ForDelegate forDelegate) + { + int index = fromInclusive - chunkSize; + // locker object shared by all the process() delegates + object locker = new object(); + + // processing function + // takes next chunk and processes it using action + DelegateProcess process = delegate() + { + while (true) + { + int chunkStart = 0; + lock (locker) + { + // take next chunk + index += chunkSize; + chunkStart = index; + } + // process the chunk + // (another thread is processing another chunk + // so the real order of items will be out-of-order) + for (int i = chunkStart; i < chunkStart + chunkSize; i++) + { + if (i >= toExclusive) return; + forDelegate(i); + } + } + }; + + // launch process() threads + IAsyncResult[] asyncResults = new IAsyncResult[threadCount]; + for (int i = 0; i < threadCount; ++i) + { + asyncResults[i] = process.BeginInvoke(null, null); + } + // wait for all threads to complete + for (int i = 0; i < threadCount; ++i) + { + process.EndInvoke(asyncResults[i]); + } + } + } +} \ No newline at end of file diff --git a/Utilities/Utilities.csproj b/Utilities/Utilities.csproj new file mode 100644 index 0000000..1545d91 --- /dev/null +++ b/Utilities/Utilities.csproj @@ -0,0 +1,62 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {6E0F2D1E-6167-4032-BA90-DEE3A99207D0} + Library + Properties + Utilities + Utilities + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file