diff --git a/SMBLibrary/Client/SMB1Client.cs b/SMBLibrary/Client/SMB1Client.cs index 6edb092..d596245 100644 --- a/SMBLibrary/Client/SMB1Client.cs +++ b/SMBLibrary/Client/SMB1Client.cs @@ -12,6 +12,7 @@ using System.Net.Sockets; using System.Threading; using SMBLibrary.Authentication.NTLM; using SMBLibrary.NetBios; +using SMBLibrary.Services; using SMBLibrary.SMB1; using Utilities; @@ -28,6 +29,7 @@ namespace SMBLibrary.Client private SMBTransportType m_transport; private bool m_isConnected; + private bool m_isLoggedIn; private Socket m_clientSocket; private IAsyncResult m_currentAsyncResult; private bool m_forceExtendedSecurity; @@ -186,6 +188,7 @@ namespace SMBLibrary.Client SMB1Message reply = WaitForMessage(CommandName.SMB_COM_SESSION_SETUP_ANDX); if (reply != null) { + m_isLoggedIn = (reply.Header.Status == NTStatus.STATUS_SUCCESS); return reply.Header.Status; } return NTStatus.STATUS_INVALID_SMB; @@ -217,6 +220,7 @@ namespace SMBLibrary.Client reply = WaitForMessage(CommandName.SMB_COM_SESSION_SETUP_ANDX); if (reply != null) { + m_isLoggedIn = (reply.Header.Status == NTStatus.STATUS_SUCCESS); return reply.Header.Status; } } @@ -229,8 +233,44 @@ namespace SMBLibrary.Client } } + public NTStatus Logoff() + { + LogoffAndXRequest request = new LogoffAndXRequest(); + TrySendMessage(request); + + SMB1Message reply = WaitForMessage(CommandName.SMB_COM_LOGOFF_ANDX); + if (reply != null) + { + m_isLoggedIn = (reply.Header.Status != NTStatus.STATUS_SUCCESS); + return reply.Header.Status; + } + return NTStatus.STATUS_INVALID_SMB; + } + + public List ListShares(out NTStatus status) + { + if (!m_isConnected || !m_isLoggedIn) + { + throw new InvalidOperationException("A login session must be successfully established before retrieving share list"); + } + + + SMB1FileStore namedPipeShare = TreeConnect("IPC$", ServiceName.NamedPipe, out status); + if (namedPipeShare == null) + { + return null; + } + + return ServerServiceHelper.ListShares(namedPipeShare, ShareType.DiskDrive, out status); + } + public SMB1FileStore TreeConnect(string shareName, ServiceName serviceName, out NTStatus status) { + if (!m_isConnected || !m_isLoggedIn) + { + throw new InvalidOperationException("A login session must be successfully established before connecting to a share"); + } + TreeConnectAndXRequest request = new TreeConnectAndXRequest(); request.Path = shareName; request.Service = serviceName; diff --git a/SMBLibrary/Client/ServerServiceHelper.cs b/SMBLibrary/Client/ServerServiceHelper.cs new file mode 100644 index 0000000..5b803da --- /dev/null +++ b/SMBLibrary/Client/ServerServiceHelper.cs @@ -0,0 +1,105 @@ +/* Copyright (C) 2014-2017 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.Collections.Generic; +using SMBLibrary.RPC; +using SMBLibrary.Services; + +namespace SMBLibrary.Client +{ + public class ServerServiceHelper + { + public static List ListShares(INTFileStore namedPipeShare, ShareType? shareType, out NTStatus status) + { + object pipeHandle; + FileStatus fileStatus; + status = namedPipeShare.CreateFile(out pipeHandle, out fileStatus, ServerService.ServicePipeName, (AccessMask)(FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA), 0, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN, 0, null); + if (status != NTStatus.STATUS_SUCCESS) + { + return null; + } + BindPDU bindPDU = new BindPDU(); + bindPDU.Flags = PacketFlags.FirstFragment | PacketFlags.LastFragment; + bindPDU.DataRepresentation.CharacterFormat = CharacterFormat.ASCII; + bindPDU.DataRepresentation.ByteOrder = ByteOrder.LittleEndian; + bindPDU.DataRepresentation.FloatingPointRepresentation = FloatingPointRepresentation.IEEE; + bindPDU.MaxTransmitFragmentSize = 5680; + bindPDU.MaxReceiveFragmentSize = 5680; + + ContextElement serverServiceContext = new ContextElement(); + serverServiceContext.AbstractSyntax = new SyntaxID(ServerService.ServiceInterfaceGuid, ServerService.ServiceVersion); + serverServiceContext.TransferSyntaxList.Add(new SyntaxID(RemoteServiceHelper.NDRTransferSyntaxIdentifier, RemoteServiceHelper.NDRTransferSyntaxVersion)); + + bindPDU.ContextList.Add(serverServiceContext); + + byte[] input = bindPDU.GetBytes(); + byte[] output; + status = namedPipeShare.DeviceIOControl(pipeHandle, (uint)IoControlCode.FSCTL_PIPE_TRANSCEIVE, input, out output, 4096); + if (status != NTStatus.STATUS_SUCCESS) + { + return null; + } + BindAckPDU bindAckPDU = RPCPDU.GetPDU(output, 0) as BindAckPDU; + if (bindAckPDU == null) + { + status = NTStatus.STATUS_NOT_SUPPORTED; + return null; + } + + NetrShareEnumRequest shareEnumRequest = new NetrShareEnumRequest(); + shareEnumRequest.InfoStruct = new ShareEnum(); + shareEnumRequest.InfoStruct.Level = 1; + shareEnumRequest.InfoStruct.Info = new ShareInfo1Container(); + shareEnumRequest.PreferedMaximumLength = UInt32.MaxValue; + shareEnumRequest.ServerName = "*"; + RequestPDU requestPDU = new RequestPDU(); + requestPDU.Flags = PacketFlags.FirstFragment | PacketFlags.LastFragment; + requestPDU.DataRepresentation.CharacterFormat = CharacterFormat.ASCII; + requestPDU.DataRepresentation.ByteOrder = ByteOrder.LittleEndian; + requestPDU.DataRepresentation.FloatingPointRepresentation = FloatingPointRepresentation.IEEE; + requestPDU.OpNum = (ushort)ServerServiceOpName.NetrShareEnum; + requestPDU.Data = shareEnumRequest.GetBytes(); + requestPDU.AllocationHint = (uint)requestPDU.Data.Length; + input = requestPDU.GetBytes(); + status = namedPipeShare.DeviceIOControl(pipeHandle, (uint)IoControlCode.FSCTL_PIPE_TRANSCEIVE, input, out output, 4096); + if (status != NTStatus.STATUS_SUCCESS) + { + return null; + } + ResponsePDU responsePDU = RPCPDU.GetPDU(output, 0) as ResponsePDU; + if (responsePDU == null) + { + status = NTStatus.STATUS_NOT_SUPPORTED; + return null; + } + NetrShareEnumResponse shareEnumResponse = new NetrShareEnumResponse(responsePDU.Data); + ShareInfo1Container shareInfo1 = shareEnumResponse.InfoStruct.Info as ShareInfo1Container; + if (shareInfo1 == null) + { + if (shareEnumResponse.Result == Win32Error.ERROR_ACCESS_DENIED) + { + status = NTStatus.STATUS_ACCESS_DENIED; + } + else + { + status = NTStatus.STATUS_NOT_SUPPORTED; + } + return null; + } + + List result = new List(); + foreach (ShareInfo1Entry entry in shareInfo1.Entries) + { + if (!shareType.HasValue || shareType.Value == entry.ShareType.ShareType) + { + result.Add(entry.NetName.Value); + } + } + return result; + } + } +} diff --git a/SMBLibrary/SMBLibrary.csproj b/SMBLibrary/SMBLibrary.csproj index c8cb67e..6e909bc 100644 --- a/SMBLibrary/SMBLibrary.csproj +++ b/SMBLibrary/SMBLibrary.csproj @@ -57,6 +57,7 @@ +