使用C#远程windows执行命令

使用SMBLibray库,示例如下:

using SMBLibrary;
using SMBLibrary.Client;
using SMBLibrary.RPC;
using SMBLibrary.Services;
using System;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using FileAttributes = SMBLibrary.FileAttributes;

namespace SMBLibraryExample
{
    class Program
    {
        static void Main(string[] args)
        {
            IPAddress serverAddress = IPAddress.Parse("10.10.4.21");
            string domain = String.Empty;
            //string username = "xfeldat";
            //string password = "seam1BTMM";
            string username = "administrator";
            string password = "1";


            // Define execution commands
            string binaryPath = @"C:\Windows\System32\shutdown.exe";
            string arguments = " -r -f -t 10";

            // User input options: "v1" or "v2"
            string preferredVersion = "v1";

            ISMBClient connectedClient = null;
            string structuralVersionUsed = "";

            if (preferredVersion.ToLower() == "v1")
            {
                Console.WriteLine("Attempting preferred SMBv1 connection...");
                connectedClient = TrySMB1Connection(serverAddress, domain, username, password);
                structuralVersionUsed = "v1";

                if (connectedClient == null)
                {
                    Console.WriteLine("SMBv1 failed. Attempting fallback to SMBv2...");
                    connectedClient = TrySMB2Connection(serverAddress, domain, username, password);
                    structuralVersionUsed = "v2";
                }
            }
            else // Default to v2 first
            {
                Console.WriteLine("Attempting preferred SMBv2 connection...");
                connectedClient = TrySMB2Connection(serverAddress, domain, username, password);
                structuralVersionUsed = "v2";

                if (connectedClient == null)
                {
                    Console.WriteLine("SMBv2 failed. Attempting fallback to SMBv1...");
                    connectedClient = TrySMB1Connection(serverAddress, domain, username, password);
                    structuralVersionUsed = "v1";
                }
            }

            if (connectedClient != null)
            {
                Console.WriteLine($"[SUCCESS] Logged in using SMB {structuralVersionUsed.ToUpper()}");

                try
                {
                    ExecuteRemoteCommand(connectedClient, serverAddress, binaryPath, arguments);
                }
                finally
                {
                    connectedClient.Disconnect();
                    Console.WriteLine("Disconnected from host.");
                }
            }
            else
            {
                Console.WriteLine("[ERROR] Both SMBv1 and SMBv2 authentication chains failed.");
            }
        }

        static bool HasArgument(string[] args, string expected)
        {
            if (args == null || args.Length == 0)
            {
                return false;
            }

            foreach (string arg in args)
            {
                if (String.Equals(arg, expected, StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }
            }

            return false;
        }

        static SMB1Client TrySMB1Connection(IPAddress ip, string domain, string user, string pass)
        {
            try
            {
                SMB1Client client = new SMB1Client();
                // Pass false to drop GSSAPI and cleanly downgrade to NTLMv1 for Win7
                bool isConnected = client.Connect(ip, SMBTransportType.DirectTCPTransport, false);
                if (!isConnected) return null;

                NTStatus loginStatus = client.Login(domain, user, pass, AuthenticationMethod.NTLMv1);
                if (loginStatus == NTStatus.STATUS_SUCCESS)
                {
                    return client;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"SMBv1 Exception: {ex.Message}");
            }
            return null;
        }

        static SMB2Client TrySMB2Connection(IPAddress ip, string domain, string user, string pass)
        {
            try
            {
                SMB2Client client = new SMB2Client();
                bool isConnected = client.Connect(ip, SMBTransportType.DirectTCPTransport);
                if (!isConnected) return null;

                // Win7 with SMB2 enabled typically handles standard NTLMv2 or NTLMv1 over security wrappers
                NTStatus loginStatus = client.Login(domain, user, pass, AuthenticationMethod.NTLMv2);
                if (loginStatus == NTStatus.STATUS_SUCCESS)
                {
                    return client;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"SMBv2 Exception: {ex.Message}");
            }
            return null;
        }

        static void ExecuteRemoteCommand(ISMBClient client, IPAddress serverAddress, string binary, string args)
        {
            ISMBFileStore ipcStore = client.TreeConnect("IPC$", out NTStatus treeStatus);
            if (treeStatus != NTStatus.STATUS_SUCCESS || ipcStore == null) return;

            PrintServerFingerprint(ipcStore, serverAddress.ToString());

            if (TryExecuteViaAtsvc(client, ipcStore, serverAddress, binary, args))
            {
                ipcStore.Disconnect();
                return;
            }

            object pipeHandle = null;
            int maxTransmitFragmentSize;
            NTStatus bindStatus = NamedPipeHelper.BindPipe(ipcStore, "svcctl", new Guid("367abb81-9844-35f1-ad32-98f038001003"), 2, out pipeHandle, out maxTransmitFragmentSize);
            if (bindStatus != NTStatus.STATUS_SUCCESS || pipeHandle == null)
            {
                Console.WriteLine($"[ERROR] DCE/RPC Bind rejection: {bindStatus}");
                ipcStore.Disconnect();
                return;
            }

            // Bind the SMB IO layer to our RPC driver
            SmbPipeStream pipeStream = new SmbPipeStream(ipcStore, pipeHandle);
            RpcEngine rpc = new RpcEngine(pipeStream, @"\\" + serverAddress.ToString());

            try
            {
                Console.WriteLine("[RPC] Initializing DCE/RPC Bind to svcctl...");
                Console.WriteLine("[RPC] Bind successful.");

                // Step 1: Open Service Control Manager
                byte[] scmHandle = rpc.OpenSCManager();
                if (scmHandle == null)
                {
                    Console.WriteLine("[ERROR] OpenSCManager failed. Check administrator rights.");
                    return;
                }
                Console.WriteLine("[RPC] SCM Manager Handle acquired.");

                // Step 2: Create a transient command service
                string serviceName = "SMBLibraryExec_" + Guid.NewGuid().ToString().Substring(0, 8);
                string commandLine = $"\"{binary}\" {args}";

                byte[] svcHandle = rpc.CreateService(scmHandle, serviceName, commandLine);
                if (svcHandle == null)
                {
                    Console.WriteLine("[ERROR] CreateService failed.");
                    return;
                }
                Console.WriteLine($"[RPC] Service '{serviceName}' registered target path.");

                // Step 3: Trigger the service thread execution
                Console.WriteLine("[RPC] Initiating service start command...");
                rpc.StartService(svcHandle);

                // Step 4: Clean up service database records immediately
                rpc.DeleteService(svcHandle);
                Console.WriteLine("[RPC] Execution pipeline finished. Cleanup complete.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[CRITICAL] RPC Exception: {ex.Message}");
            }
            finally
            {
                ipcStore.CloseFile(pipeHandle);
                ipcStore.Disconnect();
            }
        }

        static bool TryExecuteViaAtsvc(ISMBClient client, ISMBFileStore ipcStore, IPAddress serverAddress, string binary, string args)
        {
            object pipeHandle;
            int maxTransmitFragmentSize;
            NTStatus bindStatus = NamedPipeHelper.BindPipe(ipcStore, "atsvc", new Guid("1ff70682-0a51-30e8-076d-740be8cee98b"), 1, out pipeHandle, out maxTransmitFragmentSize);
            if (bindStatus != NTStatus.STATUS_SUCCESS || pipeHandle == null)
            {
                Console.WriteLine($"[ATSVC] Bind failed: {bindStatus}");
                return false;
            }

            SmbPipeStream pipeStream = new SmbPipeStream(ipcStore, pipeHandle);
            RpcEngine rpc = new RpcEngine(pipeStream, @"\\" + serverAddress.ToString());
            string sentinelFileName = "SMBLibraryExec_" + Guid.NewGuid().ToString("N").Substring(0, 8) + ".txt";
            string atCommand = BuildAtsvcCommand(binary, args, sentinelFileName);
            string sentinelShareName = "C$";
            string sentinelRelativePath = sentinelFileName;
            DateTime remoteNow = GetRemoteLocalTime(client);
            DateTime runAt = GetNextAtsvcRunTime(remoteNow);

            try
            {
          
                Console.WriteLine($"[ATSVC] Remote time is {remoteNow:yyyy-MM-dd HH:mm:ss}.");
                Console.WriteLine($"[ATSVC] Planned run time is {runAt:yyyy-MM-dd HH:mm:ss}.");
                Console.WriteLine($"[ATSVC] Sentinel target is {sentinelShareName}\\{sentinelRelativePath}.");
                Console.WriteLine($"[ATSVC] Command is: {atCommand}");

                Console.WriteLine("[ATSVC] Submitting remote scheduled job...");
                uint jobId = rpc.ScheduleAtsvcJob(atCommand, runAt);
                if (jobId == 0)
                {
                    Console.WriteLine("[ATSVC] Job creation failed.");
                    return false;
                }

            
                Console.WriteLine($"[ATSVC] Job {jobId} queued for {runAt:HH:mm:ss}.");
                AtsvcJobInfo scheduledJob = rpc.GetAtsvcJobInfo(jobId);
                if (scheduledJob != null)
                {
                    Console.WriteLine($"[ATSVC] JobInfo Flags=0x{scheduledJob.Flags:X2}, JobTime={scheduledJob.JobTime}, Command={scheduledJob.Command.Value}");
                }

                string sentinelContents;
                if (TryWaitForSentinelFile(client, sentinelShareName, sentinelRelativePath, TimeSpan.FromSeconds(120), out sentinelContents))
                {
                    Console.WriteLine("[ATSVC] Remote command executed.");
                    if (!String.IsNullOrWhiteSpace(sentinelContents))
                    {
                        Console.WriteLine($"[ATSVC] Sentinel: {sentinelContents.Trim()}");
                    }
                }
                else
                {
                    AtsvcJobInfo currentJob = rpc.GetAtsvcJobInfo(jobId);
                    if (currentJob != null)
                    {
                        Console.WriteLine($"[ATSVC] JobInfo after poll Flags=0x{currentJob.Flags:X2}, JobTime={currentJob.JobTime}, Command={currentJob.Command.Value}");
                    }
                    Console.WriteLine("[ATSVC] Job queued, but sentinel file was not observed yet.");
                }

                rpc.DeleteAtsvcJob(jobId);
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ATSVC] Exception: {ex.Message}");
                return false;
            }
            finally
            {
                ipcStore.CloseFile(pipeHandle);
            }
        }

        static string BuildAtsvcCommand(string binary, string args, string sentinelFileName)
        {
            // string sentinelPath = @"C:\" + sentinelFileName;
            string normalizedArgs = (args ?? String.Empty).Trim();

            if (String.Equals(Path.GetFileName(binary), "cmd.exe", StringComparison.OrdinalIgnoreCase))
            {
                if (normalizedArgs.StartsWith("/c ", StringComparison.OrdinalIgnoreCase) ||
                    normalizedArgs.StartsWith("/k ", StringComparison.OrdinalIgnoreCase))
                {
                    normalizedArgs = normalizedArgs.Substring(2).TrimStart();
                }
                else if (String.Equals(normalizedArgs, "/c", StringComparison.OrdinalIgnoreCase) ||
                         String.Equals(normalizedArgs, "/k", StringComparison.OrdinalIgnoreCase))
                {
                    normalizedArgs = String.Empty;
                }

                return $"%COMSPEC% /Q /C \"{normalizedArgs} ";
            }

            string commandLine = $"\"{binary}\" {normalizedArgs}".Trim();
            return commandLine;
        }

        static DateTime GetNextAtsvcRunTime(ISMBClient client)
        {
            return GetNextAtsvcRunTime(GetRemoteLocalTime(client));
        }

        static DateTime GetNextAtsvcRunTime(DateTime now)
        {
            DateTime runAt = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0).AddMinutes(1);
            if ((runAt - now).TotalSeconds < 30)
            {
                runAt = runAt.AddMinutes(1);
            }

            return runAt;
        }

        static DateTime GetRemoteLocalTime(ISMBClient client)
        {
            if (client is SMB1Client smb1Client &&
                smb1Client.ServerSystemTime.HasValue &&
                smb1Client.ServerTimeZone.HasValue)
            {
                DateTime remoteUtc = DateTime.SpecifyKind(smb1Client.ServerSystemTime.Value, DateTimeKind.Utc);
                return remoteUtc.AddMinutes(-smb1Client.ServerTimeZone.Value);
            }

            return DateTime.Now;
        }

        static bool TryWaitForSentinelFile(ISMBClient client, string shareName, string relativePath, TimeSpan timeout, out string contents)
        {
            contents = null;
            ISMBFileStore adminStore = client.TreeConnect(shareName, out NTStatus treeStatus);
         
            if (treeStatus != NTStatus.STATUS_SUCCESS || adminStore == null)
            {
                Console.WriteLine($"[ATSVC] {shareName} unavailable: {treeStatus}");
                return false;
            }

            try
            {
                DateTime deadline = DateTime.UtcNow.Add(timeout);
                int attempt = 0;
                while (DateTime.UtcNow < deadline)
                {
                    attempt++;
                    if (TryReadTextFile(adminStore, relativePath, out contents))
                    {
                  
                        return true;
                    }

                 
                    Thread.Sleep(TimeSpan.FromSeconds(10));
                }

                return false;
            }
            finally
            {
                adminStore.Disconnect();
            }
        }

        static bool TryReadTextFile(ISMBFileStore store, string relativePath, out string contents)
        {
            contents = null;

            object fileHandle;
            FileStatus fileStatus;
            NTStatus openStatus = store.CreateFile(
                out fileHandle,
                out fileStatus,
                relativePath,
                AccessMask.GENERIC_READ,
                0,
                ShareAccess.Read | ShareAccess.Write | ShareAccess.Delete,
                CreateDisposition.FILE_OPEN,
                CreateOptions.FILE_NON_DIRECTORY_FILE,
                null);

         
            if (openStatus != NTStatus.STATUS_SUCCESS)
            {
                return false;
            }

            try
            {
                NTStatus readStatus = store.ReadFile(out byte[] data, fileHandle, 0, 4096);
             
                if (readStatus != NTStatus.STATUS_SUCCESS && readStatus != NTStatus.STATUS_END_OF_FILE)
                {
                    return false;
                }

                contents = Encoding.ASCII.GetString(data ?? Array.Empty<byte>());
                return true;
            }
            finally
            {
                store.CloseFile(fileHandle);
            }
        }

        static void PrintServerFingerprint(ISMBFileStore ipcStore, string serverName)
        {
            object pipeHandle;
            int maxTransmitFragmentSize;
            NTStatus bindStatus = NamedPipeHelper.BindPipe(ipcStore, ServerService.ServicePipeName, ServerService.ServiceInterfaceGuid, ServerService.ServiceVersion, out pipeHandle, out maxTransmitFragmentSize);
            if (bindStatus != NTStatus.STATUS_SUCCESS)
            {
                Console.WriteLine($"[TARGET] srvsvc bind failed: {bindStatus}");
                return;
            }

            try
            {
                NetrServerGetInfoRequest infoRequest = new NetrServerGetInfoRequest();
                infoRequest.ServerName = @"\\" + serverName;
                infoRequest.Level = 101;

                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.NetrServerGetInfo;
                requestPDU.Data = infoRequest.GetBytes();
                requestPDU.AllocationHint = (uint)requestPDU.Data.Length;

                NTStatus status = ipcStore.DeviceIOControl(pipeHandle, (uint)IoControlCode.FSCTL_PIPE_TRANSCEIVE, requestPDU.GetBytes(), out byte[] output, maxTransmitFragmentSize);
                if (status != NTStatus.STATUS_SUCCESS)
                {
                    Console.WriteLine($"[TARGET] srvsvc query failed: {status}");
                    return;
                }

                RPCPDU responsePacket = RPCPDU.GetPDU(output, 0);
                if (responsePacket is FaultPDU srvFault)
                {
                    Console.WriteLine($"[TARGET] srvsvc fault: {srvFault.Status}");
                    return;
                }

                ResponsePDU responsePDU = responsePacket as ResponsePDU;
                if (responsePDU == null)
                {
                    Console.WriteLine($"[TARGET] Unexpected srvsvc PDU: {responsePacket.GetType().Name}");
                    return;
                }

                byte[] responseData = responsePDU.Data;
                while ((responsePDU.Flags & PacketFlags.LastFragment) == 0)
                {
                    status = ipcStore.ReadFile(out output, pipeHandle, 0, maxTransmitFragmentSize);
                    if (status != NTStatus.STATUS_SUCCESS)
                    {
                        Console.WriteLine($"[TARGET] srvsvc read failed: {status}");
                        return;
                    }

                    responsePacket = RPCPDU.GetPDU(output, 0);
                    responsePDU = responsePacket as ResponsePDU;
                    if (responsePDU == null)
                    {
                        Console.WriteLine($"[TARGET] Unexpected fragmented srvsvc PDU: {responsePacket.GetType().Name}");
                        return;
                    }

                    byte[] combinedResponseData = new byte[responseData.Length + responsePDU.Data.Length];
                    Buffer.BlockCopy(responseData, 0, combinedResponseData, 0, responseData.Length);
                    Buffer.BlockCopy(responsePDU.Data, 0, combinedResponseData, responseData.Length, responsePDU.Data.Length);
                    responseData = combinedResponseData;
                }

                NetrServerGetInfoResponse infoResponse = new NetrServerGetInfoResponse(responseData);
                if (infoResponse.Result != 0)
                {
                    Console.WriteLine($"[TARGET] NetrServerGetInfo result: {infoResponse.Result}");
                    return;
                }

                ServerInfo101 info101 = infoResponse.InfoStruct.Info as ServerInfo101;
                if (info101 == null)
                {
                    Console.WriteLine($"[TARGET] Unexpected server info level: {infoResponse.InfoStruct.Level}");
                    return;
                }

                Console.WriteLine($"[TARGET] Name={info101.ServerName.Value}, Platform={info101.PlatformID}, Version={info101.VerMajor}.{info101.VerMinor}, Type={info101.Type}, Comment={info101.Comment.Value}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[TARGET] srvsvc probe exception: {ex.Message}");
            }
            finally
            {
                ipcStore.CloseFile(pipeHandle);
            }
        }
    }

    public class AtsvcJobInfo : INDRStructure
    {
        public uint JobTime;
        public uint DaysOfMonth;
        public byte DaysOfWeek;
        public byte Flags;
        public NDRUnicodeString Command;

        public AtsvcJobInfo()
        {
            Command = new NDRUnicodeString();
        }

        public void Read(NDRParser parser)
        {
            parser.BeginStructure();
            JobTime = parser.ReadUInt32();
            DaysOfMonth = parser.ReadUInt32();
            DaysOfWeek = parser.ReadBytes(1)[0];
            Flags = parser.ReadBytes(1)[0];
            parser.ReadEmbeddedStructureFullPointer(ref Command);
            parser.EndStructure();
        }

        public void Write(NDRWriter writer)
        {
            writer.BeginStructure();
            writer.WriteUInt32(JobTime);
            writer.WriteUInt32(DaysOfMonth);
            writer.WriteBytes(new byte[] { DaysOfWeek, Flags });
            writer.WriteEmbeddedStructureFullPointer(Command);
            writer.EndStructure();
        }
    }

    /// <summary>
    /// Encapsulates SMB Read/Write actions into a standard continuous data stream for RPC
    /// </summary>
    public class SmbPipeStream
    {
        private readonly ISMBFileStore _store;
        private readonly object _handle;

        public SmbPipeStream(ISMBFileStore store, object handle)
        {
            _store = store;
            _handle = handle;
        }

        public void Write(byte[] buffer)
        {
            // FIX: Named pipes must use offset 0 for message mode packets
            _store.WriteFile(out _, _handle, 0, buffer);
        }

        public byte[] Read()
        {
            // FIX: Named pipes must use offset 0 for reading sequential messages
            _store.ReadFile(out byte[] data, _handle, 0, 1024);
            return data;
        }

        public byte[] Transceive(byte[] buffer)
        {
            // SMB2 named pipes are expected to use FSCTL_PIPE_TRANSCEIVE for RPC request/response exchange.
            NTStatus status = _store.DeviceIOControl(
                _handle,
                (uint)IoControlCode.FSCTL_PIPE_TRANSCEIVE,
                buffer,
                out byte[] data,
                4096);

            if (status == NTStatus.STATUS_SUCCESS || status == NTStatus.STATUS_BUFFER_OVERFLOW)
            {
                return data;
            }

            Console.WriteLine($"[DEBUG] Pipe transceive failed with status: {status}");
            return null;
        }
    }
    /// <summary>
    /// Manages NDR data marshalling and RPC Call/Response sequences
    /// </summary>
    public class RpcEngine
    {
        private readonly SmbPipeStream _stream;
        private readonly string _machineName;
        private ushort _callId = 1;

        public RpcEngine(SmbPipeStream stream, string machineName)
        {
            _stream = stream;
            _machineName = machineName;
        }

        private static void WriteRpcStringPointer(BinaryWriter writer, string value, uint referentId)
        {
            if (value == null)
            {
                writer.Write((uint)0);
                return;
            }

            byte[] bytes = Encoding.Unicode.GetBytes(value + "\0");
            writer.Write(referentId);
            writer.Write((uint)(value.Length + 1));
            writer.Write((uint)0);
            writer.Write((uint)(value.Length + 1));
            writer.Write(bytes);

            int remainder = bytes.Length % 4;
            if (remainder > 0)
            {
                writer.Write(new byte[4 - remainder]);
            }
        }
        // #endregion

        public bool Bind(Guid interfaceGuid, ushort major, ushort minor)
        {
            MemoryStream ms = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(ms);

            // DCE/RPC Bind Header (Exactly 24 bytes)
            bw.Write((byte)5);          // RPC Version Major
            bw.Write((byte)0);          // RPC Version Minor
            bw.Write((byte)11);         // Packet Type: Bind (11)
            bw.Write((byte)3);          // Flags: First Frag | Last Frag
            bw.Write((uint)0x10000000); // Data Representation (Little Endian)
            bw.Write((ushort)0);        // Total Frag Length (patched after serialization)
            bw.Write((ushort)0);        // Auth Length
            bw.Write((uint)_callId++);  // Call ID
            bw.Write((ushort)5840);     // Max Xmit Frag
            bw.Write((ushort)5840);     // Max Recv Frag
            bw.Write((uint)0);          // Assoc Group

            // Context List (Exactly 48 bytes)
            bw.Write((byte)1);          // Number of items
            bw.Write((byte)0);          // Reserved Alignment Padding
            bw.Write((ushort)0);        // Reserved Alignment Padding
            bw.Write((ushort)0);        // Context ID
            bw.Write((byte)1);          // Number of Transfer Syntaxes
            bw.Write((byte)0);          // Reserved

            // Abstract Syntax (Target Interface: svcctl)
            bw.Write(interfaceGuid.ToByteArray());
            bw.Write(major);            // Major Version (2)
            bw.Write(minor);            // Minor Version (0)

            // Transfer Syntax (NDR Engine UUID)
            bw.Write(new Guid("8a88a004-52ac-11ce-8b3a-00aa0080c14d").ToByteArray());
            bw.Write((ushort)2);        // NDR Major
            bw.Write((ushort)0);        // NDR Minor

            byte[] bindRequest = ms.ToArray();
            Buffer.BlockCopy(BitConverter.GetBytes((ushort)bindRequest.Length), 0, bindRequest, 8, 2);
            byte[] response = _stream.Transceive(bindRequest);

            // Verify packet response
            if (response == null || response.Length < 24 || response[2] != 12) // Type 12 = Bind_Ack
            {
                // Debug assistance: If response is not null, find what packet type it returned
                if (response != null && response.Length > 2)
                {
                    Console.WriteLine($"[DEBUG] Received unexpected packet type: {response[2]}");
                }
                return false;
            }

            return true;
        }

        private byte[] ExecuteCall(ushort opNum, byte[] stub)
        {
            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.CallID = _callId++;
            requestPDU.AllocationHint = (uint)stub.Length;
            requestPDU.ContextID = 0;
            requestPDU.OpNum = opNum;
            requestPDU.Data = stub;

            // #region debug-point D:createservice-request
            if (opNum == 12)
            {
              
            }
            // #endregion

            byte[] response = _stream.Transceive(requestPDU.GetBytes());
            if (response == null)
            {
                return null;
            }

            // #region debug-point D:createservice-response
            if (opNum == 12)
            {
                byte packetType = response.Length > 2 ? response[2] : (byte)255;
           
            }
            // #endregion

            RPCPDU responsePacket = RPCPDU.GetPDU(response, 0);
            FaultPDU faultPDU = responsePacket as FaultPDU;
            if (faultPDU != null)
            {
                // #region debug-point D:createservice-fault
                if (opNum == 12)
                {
                 
                }
                // #endregion
                Console.WriteLine($"[RPC FAULT] OpNum={opNum}, Status={faultPDU.Status}, Flags={faultPDU.Flags}");
                return null;
            }

            ResponsePDU responsePDU = responsePacket as ResponsePDU;
            if (responsePDU == null)
            {
                Console.WriteLine($"[RPC] Unexpected PDU type: {responsePacket.GetType().Name}");
                return null;
            }

            return responsePDU.Data;
        }

        public static string ToHex(byte[] buffer, int maxBytes)
        {
            if (buffer == null)
            {
                return String.Empty;
            }

            int length = Math.Min(buffer.Length, maxBytes);
            StringBuilder builder = new StringBuilder(length * 2 + 16);
            for (int index = 0; index < length; index++)
            {
                builder.Append(buffer[index].ToString("X2"));
            }
            if (buffer.Length > maxBytes)
            {
                builder.Append("...");
            }
            return builder.ToString();
        }

        public byte[] OpenSCManager()
        {
            MemoryStream ms = new MemoryStream();
            BinaryWriter writer = new BinaryWriter(ms);
            WriteRpcStringPointer(writer, _machineName, 1);
            WriteRpcStringPointer(writer, "ServicesActive", 2);
            writer.Write((uint)0x00000003); // SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE
            byte[] reply = ExecuteCall(15, ms.ToArray());
            if (reply == null || reply.Length < 24) return null;

            // #region debug-point B:openscmanager-response
            uint returnCode = BitConverter.ToUInt32(reply, 20);
           

            if (returnCode != 0)
            {
                Console.WriteLine($"[RPC] OpenSCManagerW returned Win32 error: {returnCode}");
                return null;
            }

            byte[] handle = new byte[20];
            Buffer.BlockCopy(reply, 0, handle, 0, 20); // First 20 bytes contain Context Handle
            if (ToHex(handle, 20).TrimEnd('0').Length == 0)
            {
                Console.WriteLine("[RPC] OpenSCManagerW returned an empty SCM handle.");
                return null;
            }
            return handle;
        }
        public byte[] CreateService(byte[] scmHandle, string serviceName, string binaryPath)
        {
            NDRWriter writer = new NDRWriter();
            int scmHandleStart = writer.GetBytes().Length;
            writer.WriteBytes(scmHandle); // 20-byte Parent Context Handle
            int scmHandleEnd = writer.GetBytes().Length;
            int serviceNameStart = writer.GetBytes().Length;
            writer.WriteTopLevelUnicodeStringPointer(serviceName); // [in, string] wchar_t* lpServiceName
            int serviceNameEnd = writer.GetBytes().Length;
            int displayNameStart = writer.GetBytes().Length;
            writer.WriteTopLevelUnicodeStringPointer(serviceName); // [in, string, unique] wchar_t* lpDisplayName
            int displayNameEnd = writer.GetBytes().Length;
            int desiredAccessStart = writer.GetBytes().Length;
            writer.WriteUInt32(0x000F01FF); // Desired Access: SERVICE_ALL_ACCESS
            int desiredAccessEnd = writer.GetBytes().Length;
            int serviceTypeStart = writer.GetBytes().Length;
            writer.WriteUInt32(0x00000010); // Service Type: SERVICE_WIN32_OWN_PROCESS
            int serviceTypeEnd = writer.GetBytes().Length;
            int startTypeStart = writer.GetBytes().Length;
            writer.WriteUInt32(0x00000003); // Start Type: SERVICE_DEMAND_START
            int startTypeEnd = writer.GetBytes().Length;
            int errorControlStart = writer.GetBytes().Length;
            writer.WriteUInt32(0x00000001); // Error Control: SERVICE_ERROR_NORMAL
            int errorControlEnd = writer.GetBytes().Length;
            int binaryPathStart = writer.GetBytes().Length;
            writer.WriteTopLevelUnicodeStringPointer(binaryPath); // [in, string] wchar_t* lpBinaryPathName
            int binaryPathEnd = writer.GetBytes().Length;
            int loadOrderGroupStart = writer.GetBytes().Length;
            writer.WriteTopLevelUnicodeStringPointer(null); // [in, string, unique] wchar_t* lpLoadOrderGroup
            int loadOrderGroupEnd = writer.GetBytes().Length;
            int tagIdStart = writer.GetBytes().Length;
            writer.WriteUInt32(0); // [in, out, unique] LPDWORD lpdwTagId
            int tagIdEnd = writer.GetBytes().Length;
            int dependenciesStart = writer.GetBytes().Length;
            writer.WriteUInt32(0); // [in, unique, size_is(dwDependSize)] LPBYTE lpDependencies
            int dependenciesEnd = writer.GetBytes().Length;
            int dependSizeStart = writer.GetBytes().Length;
            writer.WriteUInt32(0); // dwDependSize
            int dependSizeEnd = writer.GetBytes().Length;
            int startNameStart = writer.GetBytes().Length;
            writer.WriteTopLevelUnicodeStringPointer(null); // [in, string, unique] wchar_t* lpServiceStartName
            int startNameEnd = writer.GetBytes().Length;
            int passwordStart = writer.GetBytes().Length;
            writer.WriteUInt32(0); // [in, unique, size_is(dwPwSize)] LPBYTE lpPassword
            int passwordEnd = writer.GetBytes().Length;
            int passwordSizeStart = writer.GetBytes().Length;
            writer.WriteUInt32(0); // dwPwSize
            int passwordSizeEnd = writer.GetBytes().Length;
            byte[] stub = writer.GetBytes();

            byte[] reply = ExecuteCall(12, stub); // OpNum 12: CreateServiceW
            if (reply == null || reply.Length < 24) return null;
            byte[] handle = new byte[20];
            Buffer.BlockCopy(reply, 0, handle, 0, 20);
            return handle;
        }
        public uint ScheduleAtsvcJob(string command, DateTime runAt)
        {
            NDRWriter writer = new NDRWriter();
            writer.WriteTopLevelUnicodeStringPointer(_machineName);
            writer.WriteStructure(new AtsvcJobInfo
            {
                JobTime = GetMillisecondsAfterMidnight(runAt),
                DaysOfMonth = 0,
                DaysOfWeek = 0,
                Flags = 0x10, // JOB_NONINTERACTIVE
                Command = new NDRUnicodeString(command),
            });

         
            byte[] reply = ExecuteCall(0, writer.GetBytes());
            if (reply == null || reply.Length < 8)
            {
                return 0;
            }

            uint jobId = BitConverter.ToUInt32(reply, 0);
            uint returnCode = BitConverter.ToUInt32(reply, 4);

            if (returnCode != 0)
            {
                Console.WriteLine($"[ATSVC] NetrJobAdd returned Win32 error: {returnCode}");
                return 0;
            }

            return jobId;
        }

        public void DeleteAtsvcJob(uint jobId)
        {
            NDRWriter writer = new NDRWriter();
            writer.WriteTopLevelUnicodeStringPointer(_machineName);
            writer.WriteUInt32(jobId);
            writer.WriteUInt32(jobId);

         

            byte[] reply = ExecuteCall(1, writer.GetBytes());
            if (reply == null || reply.Length < 4)
            {
                return;
            }

            uint returnCode = BitConverter.ToUInt32(reply, 0);
      
    
            if (returnCode != 0)
            {
                Console.WriteLine($"[ATSVC] NetrJobDel returned Win32 error: {returnCode}");
            }
        }

        public AtsvcJobInfo GetAtsvcJobInfo(uint jobId)
        {
            NDRWriter writer = new NDRWriter();
            writer.WriteTopLevelUnicodeStringPointer(_machineName);
            writer.WriteUInt32(jobId);

            byte[] reply = ExecuteCall(3, writer.GetBytes());
            if (reply == null || reply.Length < 8)
            {
                return null;
            }


            NDRParser parser = new NDRParser(reply);
            uint referentId = parser.ReadUInt32();
            AtsvcJobInfo info = null;
            if (referentId != 0)
            {
                info = new AtsvcJobInfo();
                parser.ReadStructure(info);
            }

            uint returnCode = parser.ReadUInt32();
            if (returnCode != 0)
            {
                Console.WriteLine($"[ATSVC] NetrJobGetInfo returned Win32 error: {returnCode}");
                return null;
            }

            return info;
        }

        private static uint GetMillisecondsAfterMidnight(DateTime value)
        {
            TimeSpan timeOfDay = value.TimeOfDay;
            return (uint)timeOfDay.TotalMilliseconds;
        }

        public void StartService(byte[] svcHandle)
        {
            MemoryStream ms = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(ms);
            bw.Write(svcHandle); // 20-byte Service Context Handle
            bw.Write((uint)0); // Argument Count (None Passed)
            bw.Write((uint)0); // Arguments Pointer Array Reference
            ExecuteCall(19, ms.ToArray()); // OpNum 19: StartServiceW
        }
        public void DeleteService(byte[] svcHandle)
        {
            ExecuteCall(2, svcHandle); // OpNum 2: DeleteService
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值