65.9K
CodeProject 正在变化。 阅读更多。
Home

NTRemoteProcessControl – 通过 WMI 和 WinForms 枚举和控制 Windows 进程和服务

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (4投票s)

2013年2月19日

CPOL

4分钟阅读

viewsIcon

35175

downloadIcon

1029

枚举本地管理员组中具有 Windows 身份的远程进程和服务。

引言

您是否曾遇到过一些应用程序以管理员特权身份运行的 Windows 服务器?有一天,该应用程序无法运行,您想重新启动它。您的域 AD 帐户被拒绝 RDP 或任何类型的访问这些 Windows 服务器的权限,但是用于在服务器上运行这些应用程序的帐户(身份)是一个域帐户,并且它已添加到本地管理员组,您知道其凭据。我猜这些被称为服务帐户(至少在企业用语中),它们通常没有交互式访问权限,也就是说,使用此服务帐户,您无法通过 RDP 登录;如果您能够使用服务帐户 RDP 到这些 Windows 服务器,也许您在不知不觉中违反了您的 IT 协议 Wink | ;),因为服务帐户并非用于 RDP 会话。但情况很危急,业务用户一直在催促您更新,您必须尽快重新启动作业。

如果您管理的是属于本地管理员组的服务帐户(用作运行作业的身份)的凭据,那您很幸运。您有多种选择,可以使用 runas 命令,或者使用 Sysinternals 的 psexec,来模拟服务帐户并重新启动进程。如果您需要启动或停止 Windows 服务,您可以从 services.msc 连接到目标计算机并操作远程计算机上运行的服务。另一种选择是您可以编写一个简单的 WinForms 应用程序来为您完成所有这些工作,并将其命名为 NTRemoteProcessControl。

背景

要操作进程,我们将使用 WMI,而要操作 Windows 服务,我们将使用 ServiceController 类——通过模拟。代码也像说起来一样简单。Impersonator.cs 的原始源代码来自 http://platinumdogs.me/2008/10/30/net-c-impersonation-with-network-credentials/

通过 System.Management 命名空间使用 WMI 进行的任何操作都涉及创建 ManagementScopeObjectQueryManagementObjectSearcherManagementObjectCollection 的标准过程。关于上述所有类,已有充足的文档。请允许我安抚一下,并参考 MSDN。我们将只关注获取几个进程的属性并将其绑定到 GridView。

进程管理

创建 ManagementScope 对象

ManagementScope 对象定义了我们要连接的计算机,以及在查询时应使用的该计算机上的用户帐户凭据。

private static ManagementScope GetManagementScope(string pComputerName, string pAccountName, string pAccountDomain, string pAccountPassword)
{
    ManagementScope managementScope = default(ManagementScope);

    if (Utilities.IsLocalHost(pComputerName))
    {
        managementScope = new ManagementScope("\\\\" + pComputerName + "\\root\\cimv2");
    }
    else
    {
        ConnectionOptions connectionOptions = new ConnectionOptions();
        connectionOptions.Username = pAccountDomain + "\\" + pAccountName;
        connectionOptions.Password = pAccountPassword;
        managementScope = new ManagementScope("\\\\" + pComputerName + "\\root\\cimv2", connectionOptions);
    }

    return managementScope;
}

查询进程列表

Win32_Process 类——具有一组可以查询的属性。在本例中,我们将有选择地查询 Win32_Process 类的 NameCreationDateProcessId 属性。当我们创建 ManagementScope 
对象时,我们会传入一个 ConnectionOptions 对象,其中包含服务帐户的凭据。我们还会传入我们想要查询的服务器名称。一旦查询完成,ObjectQuery 的 select 子句中指定的属性将作为 ManagementObject 对象的索引属性可用。

private void GetProcessList()
{
    try
    {
        ManagementScope managementScope = GetManagementScope(txtComputerName.Text.Trim(), 
            txtUserAccountName.Text.Trim(), txtUserAccountDomain.Text.Trim(), 
            txtUserAccountPassword.Text.Trim());
        managementScope.Connect();

        ObjectQuery objectQuery = new ObjectQuery("SELECT Name, ProcessId, CreationDate FROM Win32_Process");
        ManagementObjectSearcher searcher = new ManagementObjectSearcher(managementScope, objectQuery);
        ManagementObjectCollection managementObjectCollection = searcher.Get();

        List<Win32_Process_Subset> processList = new List<Win32_Process_Subset>();
        foreach (ManagementObject mo in managementObjectCollection)
        {
            Win32_Process_Subset process = new Win32_Process_Subset();
            process.Name = Convert.ToString(mo["Name"]);
            process.ProcessId = Convert.ToString(mo["ProcessId"]);
            process.CreationDate = Convert.ToString(mo["CreationDate"]) == string.Empty ? 
              "" : Convert.ToString(ManagementDateTimeConverter.ToDateTime(Convert.ToString(mo["CreationDate"])));

            processList.Add(process);
        }

        DisplayGridView(ref processList);
    }
    catch (Exception eX)
    {
        if (eX.InnerException != null)
        {
            MessageBox.Show(eX.Message + "\n" + eX.InnerException.Message, "An Exception Occurred!");
        }
        else
        {
            MessageBox.Show(eX.Message, "An Exception Occurred!");
        }
    }
}

生成进程

使用 Win32_Process 类的 Create 方法来创建进程。

ManagementScope managementScope = GetManagementScope(txtComputerName.Text.Trim(), txtUserAccountName.Text.Trim(), 
         txtUserAccountDomain.Text.Trim(), txtUserAccountPassword.Text.Trim());
 
managementScope.Connect();

ObjectGetOptions objectGetOptions = new ObjectGetOptions();
ManagementPath managementPath = new ManagementPath("Win32_Process");
ManagementClass processClass = new ManagementClass(managementScope, managementPath, objectGetOptions);
ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
inParams["CommandLine"] = txtNewProcessName.Text.Trim();
ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null);

MessageBox.Show("Create process command issued successfully for the name " + 
  txtNewProcessName.Text.Trim() + ".");

终止进程

使用 Win32_Process 类的 Terminate 方法来终止进程。

ManagementScope managementScope = GetManagementScope(txtComputerName.Text.Trim(), txtUserAccountName.Text.Trim(), 
txtUserAccountDomain.Text.Trim(), txtUserAccountPassword.Text.Trim());
 
managementScope.Connect();

ObjectQuery objectQuery = new ObjectQuery("SELECT * FROM Win32_Process WHERE Name = '" + 
  txtProcessNameToKill.Text.Trim() + "'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(managementScope, objectQuery);
ManagementObjectCollection managementObjectCollection = searcher.Get();

foreach (ManagementObject mo in managementObjectCollection)
{
    mo.InvokeMethod("Terminate", null);
}

服务管理

当我们向 WMI ManagementScope 对象传递计算机名称和用户凭据时,它们已经为我们处理了模拟。我不知道是我个人感觉还是 WMI 本身就比较慢。查看 ServiceController 类,它提供了对远程服务控制的相当好的控制,所以我想坚持使用 ServiceController 类及其方法,而不是 WMI 的 Win32_Services 类。

查询服务列表

使用我们的 Impersonator 类模拟服务帐户用户,然后在成功时调用 ServiceController 类的 GetServices 方法,并传入计算机名称。

private void GetServiceList()
{
    try
    {
        if (Impersonator.AreValidCredentils(txtUserAccountName.Text.Trim(), 
            txtUserAccountDomain.Text.Trim(), txtUserAccountPassword.Text.Trim(), 
            LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
        {
            using (new Impersonator(txtUserAccountName.Text.Trim(), 
                   txtUserAccountDomain.Text.Trim(), txtUserAccountPassword.Text.Trim(), 
                   LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
            {
                ServiceController[] serviceList = ServiceController.GetServices(txtComputerName.Text.Trim());
                DisplayGridView(ref serviceList);
            }
        }
    }
    catch (Exception eX)
    {
        if (eX.InnerException != null)
        {
            MessageBox.Show(eX.Message + "\n" + eX.InnerException.Message, "An Exception Occurred!");
        }
        else
        {
            MessageBox.Show(eX.Message, "An Exception Occurred!");
        }
    }
}

启动服务

模拟 ServiceAccount 用户,创建一个 ServiceController 对象,将其构造函数传入服务名称和计算机名称。调用 ServiceController 对象的 Stop() 方法。

停止服务

模拟 ServiceAccount 用户,创建一个 ServiceController 对象,将其构造函数传入服务名称和计算机名称。调用 ServiceController 对象的 Start() 方法。

打开管理共享

让我们设想一下,我们的目标服务器有一个需要编辑、移动或替换的配置文件。幸运的是,我们的服务帐户对该文件具有写入或完全访问权限,并且远程计算机上启用了管理共享。由于管理共享(C$、D$ 等)已启用,并且我们的服务帐户是本地管理员组的成员,我们可以使用我们的模拟功能,并显示一个 OpenFileDialog 来允许文件编辑和移动。

模拟后,OpenFileDialog 将使用服务帐户的身份,您可以将 OpenFileDialog 控件的 InitalDirectory 设置为 \\machinename\C$\\machinename\D$ 等。

using (new Impersonator(txtUserAccountName.Text.Trim(), txtUserAccountDomain.Text.Trim(), 
txtUserAccountPassword.Text.Trim(), LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
{
    OpenFileDialog openFileDialog = new OpenFileDialog();
    if (!String.IsNullOrEmpty(txtOpenFileLocation.Text))
    {
        openFileDialog.InitialDirectory = txtOpenFileLocation.Text;
    }
    openFileDialog.Multiselect = true;
    openFileDialog.ShowDialog();
}

就是这样;使用一些模拟和 WMI 魔法,如果您知道属于该远程计算机上管理员组的用户帐户的凭据,您就不需要 RDP 访问远程计算机了。

历史

  • 初始版本 - 2013 年 2 月 19 日
NTRemoteProcessControl – 通过 WMI 和 WinForms 枚举和控制 Windows 进程和服务 - CodeProject - 代码之家
© . All rights reserved.