使用 c# 枚举远程系统上的 Windows 用户组成员

Posted

技术标签:

【中文标题】使用 c# 枚举远程系统上的 Windows 用户组成员【英文标题】:Enumerate Windows user group members on remote system using c# 【发布时间】:2010-09-06 11:55:29 【问题描述】:

在c#中,我需要能够

连接到远程系统,根据需要指定用户名/密码 列出该系统上本地组的成员 将结果取回给正在执行的计算机

例如,我将使用适当的凭据连接到 \SOMESYSTEM,并取回本地管理员列表,包括 SOMESYSTEM\Administrator、SOMESYSTEM\Bob、DOMAIN\AlanH、“DOMAIN\Domain Administrators”。

我已经尝试使用 system.directoryservices.accountmanagement 进行此操作,但遇到了身份验证问题。有时我会得到:

不允许同一用户使用多个用户名与服务器或共享资源建立多个连接。断开与服务器或共享资源的所有先前连接,然后重试。 (HRESULT 异常:0x800704C3)

上面的尝试是因为在某些情况下我根本无法取消映射现有驱动器或 UNC 连接。

其他时候我的程序收到 UNKNOWN ERROR,远程系统上的安全日志报告错误 675,代码 0x19,即 KDC_ERR_PREAUTH_REQUIRED。

我需要一种更简单且不易出错的方法来做到这一点!

【问题讨论】:

您对此有什么解决方案吗?实际上,当您调用 GroupDirectoryentry.Invoke("Members") 时,如果我们处理 GroupDirectoryentry 对象,它不会释放与远程机器事件的连接。 UserDirectoryentry.Invoke("Groups") 调用也会出现同样的问题。 【参考方案1】:

davidg 走在了正确的轨道上,我将答案归功于他。

但所需的 WMI 查询并不那么简单,因为我不仅需要整台机器的用户列表,还需要用户子集和组,无论是本地还是域,是本地管理员组的成员。作为记录,该 WMI 查询是:

SELECT PartComponent FROM Win32_GroupUser WHERE GroupComponent = "Win32_Group.Domain='thehostname',Name='thegroupname'"

这里是完整的代码 sn-p:

public string GroupMembers(string targethost, string groupname, string targetusername, string targetpassword)
        
            StringBuilder result = new StringBuilder(); 
            try
            
                ConnectionOptions Conn = new ConnectionOptions();
                if (targethost != Environment.MachineName) //WMI errors if creds given for localhost
                
                    Conn.Username = targetusername; //can be null
                    Conn.Password = targetpassword; //can be null
                
                Conn.Timeout = TimeSpan.FromSeconds(2);
                ManagementScope scope = new ManagementScope("\\\\" + targethost + "\\root\\cimv2", Conn);
                scope.Connect();
                StringBuilder qs = new StringBuilder();
                qs.Append("SELECT PartComponent FROM Win32_GroupUser WHERE GroupComponent = \"Win32_Group.Domain='");
                qs.Append(targethost);
                qs.Append("',Name='");
                qs.Append(groupname);
                qs.AppendLine("'\"");
                ObjectQuery query = new ObjectQuery(qs.ToString());
                ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
                ManagementObjectCollection queryCollection = searcher.Get();
                foreach (ManagementObject m in queryCollection)
                
                    ManagementPath path = new ManagementPath(m["PartComponent"].ToString());                                        
                     
                        String[] names = path.RelativePath.Split(',');
                        result.Append(names[0].Substring(names[0].IndexOf("=") + 1).Replace("\"", " ").Trim() + "\\"); 
                        result.AppendLine(names[1].Substring(names[1].IndexOf("=") + 1).Replace("\"", " ").Trim());                    
                    
                
                return result.ToString();
            
            catch (Exception e)
            
                Console.WriteLine("Error. Message: " + e.Message);
                return "fail";
            
        

所以,如果我调用 Groupmembers("Server1", "Administrators", "myusername", "mypassword");我得到一个返回的字符串:

服务器 1\管理员 MYDOMAIN\域管理员

实际的 WMI 返回更像这样:

\\SERVER1\root\cimv2:Win32_UserAccount.Domain="SERVER1",Name="Administrator"

...如您所见,我不得不做一些字符串操作来美化它。

【讨论】:

用户注意:您需要将 System.Management.dll 添加到您的项目引用中才能使用上述代码。【参考方案2】:

使用 WMI 应该很容易做到这一点。这里有一些文档的指针:

WMI Documentation for Win32_UserAccount

即使您以前没有使用 WMI 的经验,也应该很容易将页面底部的 VB 脚本代码转换为一些 .NET 代码。

希望这有帮助!

【讨论】:

【参考方案3】:

我建议使用 Win32 API 函数NetLocalGroupGetMembers。这比试图找出疯狂的 LDAP 语法要简单得多,这对于此处推荐的其他一些解决方案是必需的。只要您通过调用“LoginUser”模拟您要运行检查的用户,就不会遇到任何安全问题。

您可以找到用于模拟here 的示例代码。

如果您需要帮助了解如何从 C# 调用“NetLocalGroupGetMembers”,我建议您查看 Jared Parson 的 PInvoke 助手,您可以在 codeplex 中 download。

如果您在 IIS 中运行的 ASP.NET 应用程序中运行代码,并且想要模拟访问该网站的用户以进行调用,那么您可能需要为生产授予“委托授权”权限网络服务器。

如果您在桌面上运行,那么使用活动用户的安全凭证应该不是问题。

您的网络管理员可能已经撤销了您尝试访问的特定机器对“安全对象”的访问权限。不幸的是,所有network management api 功能都需要访问权限才能正常工作。如果是这种情况,那么您将需要为您想要执行的任何用户授予对“安全对象”的访问权限。但是,使用默认的 Windows 安全设置,所有经过身份验证的用户都应该具有访问权限。

我希望这会有所帮助。

-斯科特

【讨论】:

【参考方案4】:

您应该能够使用 System.DirectoryServices.DirectoryEntry 执行此操作。如果您在远程运行它时遇到问题,也许您可​​以在远程机器上安装一些东西,以便通过某种 RPC 向您提供数据,例如远程处理或 Web 服务。但我认为你正在尝试的东西应该可以远程实现,而不会太花哨。

【讨论】:

【参考方案5】:

如果 Windows 不允许您通过其登录机制进行连接,我认为您唯一的选择是在具有开放端口的远程计算机上运行某些东西(如前所述,直接或通过远程处理或 Web 服务)。

【讨论】:

以上是关于使用 c# 枚举远程系统上的 Windows 用户组成员的主要内容,如果未能解决你的问题,请参考以下文章

ActiveDirectory 和模拟中远程服务器上的 C# 文件系统

C#开源项目:SiMay远程控制管理系统

在 Windows 应用程序中模拟连接到远程机器(c#)

访问远程机器上的文件

将大型虚拟文件从 C# 拖放到 Windows 资源管理器

在 C# / Python 中重命名远程文件服务器上的文件