Manage remote service using alternate credentials

2019-02-27 20:17发布

问题:

--Original Post--

I am trying to manage (start/stop) a windows service on a remote machine using alternate credentials. I know that I can use the ServiceController class to manage a service using my current credentials:

Dim sc As New ServiceController(ServiceName, ComputerName)

but I want to use different credentials. The other classes I am using (DirectoryEntry and System.Management) both support using alternate credentials... Help would be greatly appreciated.

--Working Code (built based off accepted answer)--

I have to admit that I was sceptical it would work... but below is the code. I had to make a minor change to the code you suggested. Whenever I tried IPC$ it would return a 53 result code, even though I'm sure the share exists. So at the suggestion of another website I removed the share and just the computer name and this worked.

Imports System.Runtime.InteropServices
Imports System.Net
Imports System.IO
Imports System.ServiceProcess

Module Module1

    Sub Main()
        Dim Computername As String = "SomeComputer"
        'Create connection to remote computer'
        Using nc As New NetworkConnection("\\" + Computername, New NetworkCredential("Domain\User", "Password"))
            Dim sc As New ServiceController("Windows Firewall/Internet Connection Sharing (ICS)", Computername)
            'now we can start/stop/whatever we want here'
        End Using
        Console.ReadLine()
    End Sub

    Public Class NetworkConnection
        Implements IDisposable


        Private _networkName As String

        Public Sub New(ByVal networkName As String, ByVal credentials As NetworkCredential)
            _networkName = networkName

            Dim netResource = New NetResource() With { _
             .Scope = ResourceScope.GlobalNetwork, _
             .ResourceType = ResourceType.Disk, _
             .DisplayType = ResourceDisplaytype.Share, _
             .RemoteName = networkName _
            }

            Dim result = WNetAddConnection2(netResource, credentials.Password, credentials.UserName, 0)

            If result <> 0 Then
                Throw New IOException("Error connecting to remote share", result)
            End If
        End Sub

        Protected Overrides Sub Finalize()
            Try
                Dispose(False)
            Finally
                MyBase.Finalize()
            End Try
        End Sub

        Public Sub Dispose() Implements System.IDisposable.Dispose
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub

        Protected Sub Dispose(ByVal disposing As Boolean)
            WNetCancelConnection2(_networkName, 0, True)
        End Sub

        <DllImport("mpr.dll")> _
        Private Shared Function WNetAddConnection2(ByVal netResource As NetResource, ByVal password As String, ByVal username As String, ByVal flags As Integer) As Integer
        End Function

        <DllImport("mpr.dll")> _
        Private Shared Function WNetCancelConnection2(ByVal name As String, ByVal flags As Integer, ByVal force As Boolean) As Integer
        End Function
    End Class

    <StructLayout(LayoutKind.Sequential)> _
    Public Class NetResource
        Public Scope As ResourceScope
        Public ResourceType As ResourceType
        Public DisplayType As ResourceDisplaytype
        Public Usage As Integer
        Public LocalName As String
        Public RemoteName As String
        Public Comment As String
        Public Provider As String
    End Class

    Public Enum ResourceScope As Integer
        Connected = 1
        GlobalNetwork
        Remembered
        Recent
        Context
    End Enum

    Public Enum ResourceType As Integer
        Any = 0
        Disk = 1
        Print = 2
        Reserved = 8
    End Enum

    Public Enum ResourceDisplaytype As Integer
        Generic = &H0
        Domain = &H1
        Server = &H2
        Share = &H3
        File = &H4
        Group = &H5
        Network = &H6
        Root = &H7
        Shareadmin = &H8
        Directory = &H9
        Tree = &HA
        Ndscontainer = &HB
    End Enum
End Module

回答1:

To make remote login you should use WNetAddConnection2 (see http://msdn.microsoft.com/en-us/library/aa385413.aspx) or NetUseAdd (see http://msdn.microsoft.com/en-us/library/aa370645.aspx) API. You can use \\RemoteComputer\IPC$ as the destination resource.

UPDATED based on the question from the comment: The explanation about IPC$ sessions can be long. Just the main information.

If you want to do something on a remote computer the first thing which will be done is the establishing a authenticated "connection" to the remote computer. The network login (remote login) on the remote computer will be done, which works quite other as a local login. The network logon session stay holding and if you have a connection to for example \\RemoteComputer\share1 and one other program on your computer try access for example \\RemoteComputer\share2, the same session will be used.

You can simulate the situation with net.exe. Just start cmd.exe and type

net use \\RemoteComputer\IPC$ /u:Domain\User password

or

net use \\RemoteComputer\IPC$ /u:RemoteComputer\LocalRemoteUser password

then you will have a connection to the destination computer. Then you can type \\RemoteComputer\AnyShare in Explorer and access file system under the user's Domain\User or RemoteComputer\LocalRemoteUser credential. To disconnect use

net use \\RemoteComputer\IPC /d

If you try to start/stop a service on the remote computer the same IPC session will be tried to established. If you have already such session with one of user's credentials it will be used. Functions WNetAddConnection2, NetUseAdd can be used as replacement of "net use". If you permanently want to access a remote computer with other user's credentials you can use CredWrite, CredWriteDomainCredentials or CredUIPromptForCredentials / CredUIPromptForWindowsCredentials. The Cred-function seems me not the best way for your case.