在 Windows 10 服务中模拟当前用户

Posted

技术标签:

【中文标题】在 Windows 10 服务中模拟当前用户【英文标题】:Impersonate current user in windows 10 service 【发布时间】:2017-11-06 17:39:10 【问题描述】:

以下是映射家庭驱动器和 W 驱动器的 Windows 服务。如果我在用户下执行代码,一切正常。由于这是在本地系统下运行的服务,我使用了一个模拟类来调用它。在桌面上创建驱动器的快捷方式时,模拟工作完美,但它不会使用 net use 命令映射驱动器。我希望驱动器被映射为非持久性的。我在下面附上了我的完整代码以及模拟类。有没有办法使用模拟在当前用户下非持久映射这些驱动器?

namespace HdriveMapService2

public partial class Service1 : ServiceBase

    public Service1()
    
        InitializeComponent();
    

    protected override void OnStart(string[] args)
    


        using (ImpersonationUtils.ImpersonateCurrentUser())


            //check if Hdrive exists

            if (!Directory.Exists(@"H:\"))

        
            //ping Hdrive server

            try
               //find current domain controller
                using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
                

                    string controller = context.ConnectedServer;

                    //ping current domain controller
                    Ping ping = new Ping();
                    PingReply pingReply = ping.Send(controller);

                    if (pingReply.Status == IPStatus.Success)

                    



                            try

                            

                                    //get current username

                                    string username = Environment.UserName;

                                    Console.WriteLine(username);
                                    Console.ReadLine();

                                    //Lookup current username in AD

                                    DirectoryEntry myLdapConnection = createDirectoryEntry();
                                    DirectorySearcher search = new DirectorySearcher(myLdapConnection);
                                    search.Filter = "(cn=" + username + ")";


                                    //Search for User's Home Directory 

                                    string[] requiredProperties = new string[]  "homeDirectory" ;
                                    foreach (String property in requiredProperties)
                                        search.PropertiesToLoad.Add(property);
                                    SearchResult result = search.FindOne();

                                    // If home directory info is not blank

                                    if (result != null)

                                    


                                        //pass the homedirectory path into a variable

                                        string path = "";
                                        foreach (String property in requiredProperties)
                                            foreach (Object myCollection in result.Properties[property])
                                                path = (myCollection.ToString());


                                    //map Hdrive (non persistent map)




                                        //create a desktop shortcut to Hdrive

                                        var wsh = new IWshShell_Class();
                                        IWshRuntimeLibrary.IWshShortcut shortcut = wsh.CreateShortcut(
                                        Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\H Drive.lnk") as IWshRuntimeLibrary.IWshShortcut;
                                        shortcut.TargetPath = path;
                                        shortcut.Save();

                                        //map Wdrive (non persistent map)



                                        //create a desktop shortcut to Wdrive

                                        var wsh2 = new IWshShell_Class();
                                        IWshRuntimeLibrary.IWshShortcut shortcut2 = wsh2.CreateShortcut(
                                        Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\W Drive.lnk") as IWshRuntimeLibrary.IWshShortcut;
                                        shortcut2.TargetPath = @"\\path\path-groups";
                                        shortcut2.Save();

                                        System.Diagnostics.Process.Start("net.exe", "use /persistent:NO H: " + path);
                                        System.Diagnostics.Process.Start("net.exe", @"use /persistent:NO W: \\path\path-groups");

                                

                                

                            catch (Exception)

                            
                                //do nothing
                            
                    
                
            

            catch (Exception)
            
                //do nothing
            
        
  


//Impersonation Class
public static class ImpersonationUtils

private const int SW_SHOW = 5;
private const int TOKEN_QUERY = 0x0008;
private const int TOKEN_DUPLICATE = 0x0002;
private const int TOKEN_ASSIGN_PRIMARY = 0x0001;
private const int STARTF_USESHOWWINDOW = 0x00000001;
private const int STARTF_FORCEONFEEDBACK = 0x00000040;
private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const int TOKEN_IMPERSONATE = 0x0004;
private const int TOKEN_QUERY_SOURCE = 0x0010;
private const int TOKEN_ADJUST_PRIVILEGES = 0x0020;
private const int TOKEN_ADJUST_GROUPS = 0x0040;
private const int TOKEN_ADJUST_DEFAULT = 0x0080;
private const int TOKEN_ADJUST_SESSIONID = 0x0100;
private const int STANDARD_RIGHTS_REQUIRED = 0x000F0000;
private const int TOKEN_ALL_ACCESS =
    STANDARD_RIGHTS_REQUIRED |
    TOKEN_ASSIGN_PRIMARY |
    TOKEN_DUPLICATE |
    TOKEN_IMPERSONATE |
    TOKEN_QUERY |
    TOKEN_QUERY_SOURCE |
    TOKEN_ADJUST_PRIVILEGES |
    TOKEN_ADJUST_GROUPS |
    TOKEN_ADJUST_DEFAULT |
    TOKEN_ADJUST_SESSIONID;

[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION

    public IntPtr hProcess;
    public IntPtr hThread;
    public int dwProcessId;
    public int dwThreadId;


[StructLayout(LayoutKind.Sequential)]
private struct SECURITY_ATTRIBUTES

    public int nLength;
    public IntPtr lpSecurityDescriptor;
    public bool bInheritHandle;


[StructLayout(LayoutKind.Sequential)]
private struct STARTUPINFO

    public int cb;
    public string lpReserved;
    public string lpDesktop;
    public string lpTitle;
    public int dwX;
    public int dwY;
    public int dwXSize;
    public int dwYSize;
    public int dwXCountChars;
    public int dwYCountChars;
    public int dwFillAttribute;
    public int dwFlags;
    public short wShowWindow;
    public short cbReserved2;
    public IntPtr lpReserved2;
    public IntPtr hStdInput;
    public IntPtr hStdOutput;
    public IntPtr hStdError;


private enum SECURITY_IMPERSONATION_LEVEL

    SecurityAnonymous,
    SecurityIdentification,
    SecurityImpersonation,
    SecurityDelegation



private enum TOKEN_TYPE

    TokenPrimary = 1,
    TokenImpersonation


[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool CreateProcessAsUser(
    IntPtr hToken,
    string lpApplicationName,
    string lpCommandLine,
    ref SECURITY_ATTRIBUTES lpProcessAttributes,
    ref SECURITY_ATTRIBUTES lpThreadAttributes,
    bool bInheritHandles,
    int dwCreationFlags,
    IntPtr lpEnvironment,
    string lpCurrentDirectory,
    ref STARTUPINFO lpStartupInfo,
    out PROCESS_INFORMATION lpProcessInformation);

[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool DuplicateTokenEx(
    IntPtr hExistingToken,
    int dwDesiredAccess,
    ref SECURITY_ATTRIBUTES lpThreadAttributes,
    int ImpersonationLevel,
    int dwTokenType,
    ref IntPtr phNewToken);

[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool OpenProcessToken(
    IntPtr ProcessHandle,
    int DesiredAccess,
    ref IntPtr TokenHandle);

[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(
        ref IntPtr lpEnvironment,
        IntPtr hToken,
        bool bInherit);

[DllImport("userenv.dll", SetLastError = true)]
private static extern bool DestroyEnvironmentBlock(
        IntPtr lpEnvironment);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(
    IntPtr hObject);

private static void LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock, int sessionId)

    var pi = new PROCESS_INFORMATION();
    var saProcess = new SECURITY_ATTRIBUTES();
    var saThread = new SECURITY_ATTRIBUTES();
    saProcess.nLength = Marshal.SizeOf(saProcess);
    saThread.nLength = Marshal.SizeOf(saThread);

    var si = new STARTUPINFO();
    si.cb = Marshal.SizeOf(si);
    si.lpDesktop = @"WinSta0\Default";
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
    si.wShowWindow = SW_SHOW;

    if (!CreateProcessAsUser(
        token,
        null,
        cmdLine,
        ref saProcess,
        ref saThread,
        false,
        CREATE_UNICODE_ENVIRONMENT,
        envBlock,
        null,
        ref si,
        out pi))
    
        throw new Win32Exception(Marshal.GetLastWin32Error(), "CreateProcessAsUser failed");
    


internal static void LaunchAsCurrentUser(Process process)

    throw new NotImplementedException();


internal static void LaunchAsCurrentUser(string v1, string v2)

    throw new NotImplementedException();


private static IDisposable Impersonate(IntPtr token)

    var identity = new WindowsIdentity(token);
    return identity.Impersonate();


private static IntPtr GetPrimaryToken(Process process)

    var token = IntPtr.Zero;
    var primaryToken = IntPtr.Zero;

    if (OpenProcessToken(process.Handle, TOKEN_DUPLICATE, ref token))
    
        var sa = new SECURITY_ATTRIBUTES();
        sa.nLength = Marshal.SizeOf(sa);

        if (!DuplicateTokenEx(
            token,
            TOKEN_ALL_ACCESS,
            ref sa,
            (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
            (int)TOKEN_TYPE.TokenPrimary,
            ref primaryToken))
        
            throw new Win32Exception(Marshal.GetLastWin32Error(), "DuplicateTokenEx failed");
        

        CloseHandle(token);
    
    else
    
        throw new Win32Exception(Marshal.GetLastWin32Error(), "OpenProcessToken failed");
    

    return primaryToken;


private static IntPtr GetEnvironmentBlock(IntPtr token)

    var envBlock = IntPtr.Zero;
    if (!CreateEnvironmentBlock(ref envBlock, token, false))
    
        throw new Win32Exception(Marshal.GetLastWin32Error(), "CreateEnvironmentBlock failed");
    
    return envBlock;


public static void LaunchAsCurrentUser(string cmdLine)

    var process = Process.GetProcessesByName("explorer")[0];
    if (process != null)
    
        var token = GetPrimaryToken(process);
        if (token != IntPtr.Zero)
        
            var envBlock = GetEnvironmentBlock(token);
            if (envBlock != IntPtr.Zero)
            
                LaunchProcessAsUser(cmdLine, token, envBlock, process.SessionId);
                if (!DestroyEnvironmentBlock(envBlock))
                
                    throw new Win32Exception(Marshal.GetLastWin32Error(), "DestroyEnvironmentBlock failed");
                
            

            CloseHandle(token);
        
    


public static IDisposable ImpersonateCurrentUser()

    var process = Process.GetProcessesByName("explorer")[0];
    if (process != null)
    
        var token = GetPrimaryToken(process);
        if (token != IntPtr.Zero)
        
            return Impersonate(token);
        
    

    throw new Exception("Could not find explorer.exe");
  

【问题讨论】:

为什么不想使用\\server\share 路径? 因为您(例如)无法在它们上运行批处理文件......有些东西只适用于系统认为是本地驱动器的东西。 【参考方案1】:
    您正在模拟哪个当前用户?该服务在本地系统下启动,可能在后台 - 当前用户不是同一个本地系统帐户吗? 要将 UNC 路径映射到驱动器,服务帐户(假设它是本地系统)需要访问这些 UNC 共享的权限。有吗?

我建议您添加一些调试跟踪并查看您的代码实际上在哪个帐户下运行,然后验证该帐户是否至少可以查看这些 UNC 路径/共享。

【讨论】:

当前活跃用户、当前用户帐号和本地系统不同。我希望主要从只有当前用户可以访问的活动目录中映射用户的主驱动器。模拟类能够在桌面上映射当前驱动器的快捷方式,但不能将其映射为映射驱动器。 如果您的服务作为本地系统启动(在非交互式会话中),然后模拟当前登录的用户,则可能存在委派问题。尝试字符串级别 = WindowsIdentity.GetCurrent().ImpersonationLevel); - 您可能需要委托才能访问远程共享。 感谢您的回复,我应该把这段代码放在哪里?在模拟类中? 在使用 (ImpersonationUtils.ImpersonateCurrentUser()) 之后 - 如果您的级别是模拟而不是委托,代码将在本地模拟,但对于远程主机,它仍然是本地系统(本地计算机名称, 真的)。我不确定在 WinApi 中究竟在哪里指定了委派与模拟,你需要用谷歌搜索一下。 看起来我无法让委派工作。寻找解决方法。【参考方案2】:

我采取了不同的方法来解决这个问题。我将驱动器映射编译为可执行文件。用计时器构建了一个 Windows 服务。在服务设置项目中将可执行文件添加为文件,并使服务调用在模拟下可执行,一切正常。如果有人有任何问题或正在为类似的事情而苦苦挣扎,这可能会有所帮助。如果需要,我很乐意提供详细信息。

谢谢,

【讨论】:

不是一个有用的答案。如果 Windows 启动并且用户登录,您的代码可能会运行。但是如果没有用户登录或两个用户远程登录会发生什么情况?查询explorer.exe 是一种简单但不可靠的方法。 我愿意接受有关更可靠解决方案的建议。请让我知道你是否有任何。谢谢 Win32 API 如msdn.microsoft.com/en-us/library/aa383833(v=vs.85).aspx 可以可靠地返回信息。您可以自己 PInvoke 它们,也可以使用封装它们的开源库。 我去看看,谢谢!

以上是关于在 Windows 10 服务中模拟当前用户的主要内容,如果未能解决你的问题,请参考以下文章

如何获取当前登录电脑的windows账户信息

手机变电脑windows模拟器下载不用注册

Python - 模拟当前登录的用户(来自系统用户)

windows怎么查看当前用户名

如何开启Windows远程桌面服务

如何开启Windows远程桌面服务