在桌面应用程序中通过 *** 模拟用户

Posted

技术标签:

【中文标题】在桌面应用程序中通过 *** 模拟用户【英文标题】:Impersonate user over a *** in a desktop application 【发布时间】:2019-10-20 06:40:14 【问题描述】:

我在尝试在桌面应用程序中模拟 Active Directory 用户时遇到问题。每次我使用 LogOn API 时,结果都是错误的。

用户和域确实存在,因为我还可以在同一应用程序上通过 DirectoryServices.AccountManagement 对用户进行身份验证。

已阅读 Microsoft 站点中有关模拟的文档,甚至已阅读堆栈上的一些帖子。另外,使用SimpleImpersonation 库得到了相同的结果。

public class Demo

    private WindowsImpersonationContext impersonationContext = null;

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern int LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
    private void Enter()
    
        try
        
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;
            string userName = "myValidUser";
            string domain = "my.domain.example";
            string password = "myValidPassword";

            if (LogonUser(userName, domain, password, (int)LogonType.LOGON32_LOGON_INTERACTIVE, (int)LogonProvider.LOGON32_PROVIDER_WINNT35, ref token) != 0)
            
                WindowsIdentity WindowsIdentityPrincipal = new WindowsIdentity(token);
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                
                    WindowsIdentity tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                
                else
                
                    throw new Win32Exception(new Win32Exception(Marshal.GetLastWin32Error()).Message);
                
            
            else
            
                //throws username or pass incorrect
                throw new Win32Exception(new Win32Exception(Marshal.GetLastWin32Error()).Message);
            
        
        catch (Exception exc)
        
            throw exc;
        
    

    public enum LogonProvider
    
        LOGON32_PROVIDER_DEFAULT = 0,
        LOGON32_PROVIDER_WINNT35 = 1,
        LOGON32_PROVIDER_WINNT40 = 2,
        LOGON32_PROVIDER_WINNT50 = 3
    

    private enum LogonType
    
        LOGON32_LOGON_INTERACTIVE = 2,
        LOGON32_LOGON_NETWORK = 3,
        LOGON32_LOGON_BATCH = 4,
        LOGON32_LOGON_SERVICE = 5,
        LOGON32_LOGON_UNLOCK = 7,
        LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
        LOGON32_LOGON_NEW_CREDENTIALS = 9,
    

我不知道它不工作的原因是否是我的计算机在外部网络上运行并通过 *** 连接/验证到公司网络。

编辑 1. 生成的错误代码为 1326(未知用户名或错误 密码)

编辑2.该方法正在尝试获取身份令牌以供以后使用 用于线程模拟。

【问题讨论】:

您可能希望添加返回的实际错误代码。问题不一定只是密码,也可能是由于某种原因不允许您尝试进行的登录类型。不幸的是,从我的快速浏览来看,您的问题更多的是网络安全故障排除,而不是实际的编码问题,但也许错误代码有助于阐明一些问题。 @Maverik 感谢您的建议,我刚刚添加了错误代码 我认为问题在于您尝试以交互式用户身份登录。由于发生登录的计算机不在同一个域中,它无法向域控制器提供登录请求。这里有 2 个选项:使用 logon_new_credentials 作为登录类型。在这种情况下,您将在工作站上充当本地用户,但每个网络请求都将在域帐户下进行模拟。第二个选项(更优选)是将凭据传递给 directoryentry/ldapconnection 构造函数。这将导致网络登录并解决问题 【参考方案1】:

您不能使用 LogonUser 登录到远程计算机。您需要使用 WNetAddConnection2 api 函数。请参考 msdn 文档。

对于登录用户: https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-logonuserw

对于 WNetAddConnection2: https://docs.microsoft.com/en-us/windows/desktop/api/winnetwk/nf-winnetwk-wnetaddconnection2w

这是我写的一个类:

public class RemoteNetworkConnector : IDisposable

    readonly string _networkName;

    public RemoteNetworkConnector(string networkName, NetworkCredential credentials)
    
        _networkName = networkName;

        NetResource netResource = new NetResource
        
            Scope = ResourceScope.GlobalNetwork,
            ResourceType = ResourceType.Disk,
            DisplayType = ResourceDisplaytype.Share,
            RemoteName = networkName
        ;

        var userName = string.IsNullOrEmpty(credentials.Domain)
            ? credentials.UserName
            : string.Format(@"0\1", credentials.Domain, credentials.UserName);

        var connectionResult = WNetAddConnection2(
            netResource,
            credentials.Password,
            userName,
            0);

        if (connectionResult != 0)
        
            throw new Win32Exception(connectionResult, "Error connecting to remote share");
        
    

    ~RemoteNetworkConnector()
    
        Dispose(false);
    

    public void Dispose()
    
        Dispose(true);
        GC.SuppressFinalize(this);
    

    protected virtual void Dispose(bool disposing)
    
        WNetCancelConnection2(_networkName, 0, true);
    

    [DllImport("mpr.dll")]
    private static extern int WNetAddConnection2(NetResource netResource,
        string password, string username, int flags);

    [DllImport("mpr.dll")]
    private static extern int WNetCancelConnection2(string name, int flags,
        bool force);

    [StructLayout(LayoutKind.Sequential)]
    public class NetResource
    
        public ResourceScope Scope;
        public ResourceType ResourceType;
        public ResourceDisplaytype DisplayType;
        public int Usage;
        public string LocalName;
        public string RemoteName;
        public string Comment;
        public string Provider;
    

    public enum ResourceScope : int
    
        Connected = 1,
        GlobalNetwork,
        Remembered,
        Recent,
        Context
    ;

    public enum ResourceType : int
    
        Any = 0,
        Disk = 1,
        Print = 2,
        Reserved = 8,
    

    public enum ResourceDisplaytype : int
    
        Generic = 0x0,
        Domain = 0x01,
        Server = 0x02,
        Share = 0x03,
        File = 0x04,
        Group = 0x05,
        Network = 0x06,
        Root = 0x07,
        Shareadmin = 0x08,
        Directory = 0x09,
        Tree = 0x0a,
        Ndscontainer = 0x0b
    

我希望这会有所帮助。

【讨论】:

我不想直接连接到资源。该方法试图做的是获取令牌,以便以后可以将其用于线程模拟。【参考方案2】:

您可能需要查看LogonUser function 的文档。

如果您的用户名格式为user@domain.example,那么您需要传入:

lpszUserName = "user@domain.example" lpszDomain = null

如果您的用户名格式为domain\user,那么您需要传入:

lpszUserName = "user" lpszDomain = "domain"

以错误的方式处理完全限定的用户名将导致您看到的错误代码。

【讨论】:

以上是关于在桌面应用程序中通过 *** 模拟用户的主要内容,如果未能解决你的问题,请参考以下文章

4┃音视频直播系统之浏览器中通过 WebRTC 进行桌面共享

4┃音视频直播系统之浏览器中通过 WebRTC 进行桌面共享

启动在 Windows 用户登录时模拟的应用程序

在 aspnet 应用程序中通过模拟获取 WindowsIdentity 的不同方法

安全的 iPhone 桌面连接

更改 iOS 模拟器保存的屏幕截图的位置