首页 文章

使用ServiceController和模拟启动远程Windows服务

提问于
浏览
4

我有一个.NET MVC3应用程序,需要能够打开和关闭远程服务 . 为此,我通过WindowsIdentity.Impersonate()模拟特定的用户帐户 . 要测试用户的权限,我可以以用户身份登录并从命令提示符执行 sc.exe \\[server] start [service] . 我也知道impersonate命令正在按预期工作,因为应用程序匿名运行,因此无法模拟本地计算机上的服务( . ),但可以通过模拟控制本地服务 . 但是,当我把它放在一起并尝试启动远程服务而不是本地服务时,我总是收到错误“无法在计算机上打开 [service] 服务' [server] '”

有没有人遇到类似的问题?在我意识到sc.exe没有问题的情况下,我期待它是服务器配置而不是.NET问题 . 这是我正在使用的类的缩写版本:

public class Service
{
    public string Name;
    public bool Running;
    private ServiceController serviceController;

    public Service(string name, string host)
    {
        Name = name;

        serviceController = new ServiceController(Name, host);
        Running = serviceController.Status == ServiceControllerStatus.Running;
    }

    public bool StartService()
    {
        ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, serviceController.MachineName, Name);
        scp.Assert();

        serviceController.Start();
        serviceController.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 5));
        serviceController.Refresh();

        Running = serviceController.Status == ServiceControllerStatus.Running;

        return Running;
    }
}

还有一个注意事项:如果我将应用程序指向域上的另一台Windows 7 PC而不是服务器,并将模拟凭据更改为该PC的所有者,我实际上可以远程控制其服务而不会出现问题 .

根据请求,我在这里添加了模拟代码 . 这是一个长一点,所以忍受我:

public class Impersonate
{
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_PROVIDER_DEFAULT = 0;

    WindowsImpersonationContext impersonationContext;

    [DllImport("advapi32.dll")]
    public static extern int LogonUserA(String lpszUserName,
        String lpszDomain,
        String lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool RevertToSelf();

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);

    public bool impersonateValidUser(String userName, String domain, String password)
    {
        WindowsIdentity tempWindowsIdentity;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        if (RevertToSelf())
        {
            if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                    if (impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero)
            CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero)
            CloseHandle(tokenDuplicate);
        return false;
    }

    public void undoImpersonation()
    {
        impersonationContext.Undo();
    }
}

我在尝试启动或停止服务之前调用此代码:

Service s = new Service(ServiceName, MachineName);

if (Impersonation.impersonateValidUser(Username, Domain, Password))
{
    if (s.Running)
        s.StopService();
    else
        s.StartService();

    Impersonation.undoImpersonation();
}

值得注意的是,我可以列出服务并获得单个服务的状态(就像我在这里一样) - 只有当我开始或停止服务时才会遇到麻烦 .

2 回答

  • 2

    也许我找到了答案:

    在假冒方法 LogonUserA

    使用 LOGON32_LOGON_SERVICE (= 5)而不是 LOGON32_LOGON_INTERACTIVE .

    确保您模拟的用户与运行该服务的用户相同 .

    就我而言,用户包含在本地策略中 - >将会话作为服务启动 . 我没有使用未包含在本地策略中的用户进行测试 .

    希望能帮助到你!

  • 0

    嗯,如果您要模拟的用户是机器管理员,那是正确的 . 如果不是,则必须授予用户启动服务的特定权限 .

    由于我不是网络管理员,我不知道如何制作它,但我发现这个链接解释了如何使用subinacl工具:

    去下载

    最后,命令行将是:

    • subinacl /service SERVICENAME /grant=DOMAINNAME\USERNAME Users=TO

    Users = TO授予用户启动/停止权限

    在这种情况下,您必须使用 INTERACTIVE 模式而不是 SERVICE 模式模拟用户

相关问题