2013-03-13 151 views
5

我目前有一个在系统帐户下运行的Windows服务。我的问题是,我需要从服务内部启动某些进程作为当前登录的用户。我有所有的代码等来获取当前登录的用户/活动会话。以当前用户身份从Windows服务运行进程

我的问题是,我需要生成一个进程作为登录的用户,但不会知道用户凭证等

该服务是.NET编译的服务,我希望,我需要使用一些的PInvoke方法得到一个当前用户进程的句柄,以便将句柄复制和午餐当作流程进行处理。

不幸的是我找不到任何有关如何实施它的好文档/解决方案?

如果有人能够给我一些指导/例子,我会高度赞赏它。

*更新* 我想我已经解释错误,需要根据我实际需要重新调整。我不一定想要启动一个新流程,我只是想模拟登录用户。我一直都在看着CreateProcess等,结束了自己的创建一个新的过程作为当前登录用户(这不是特别我想做的)的路径。

反过来,我只是想在当前用户上下文下运行一些代码(模拟当前登录用户)?

+0

如果没有用户登录会怎么样? – 2013-03-13 11:20:44

+0

我想你尝试以管理员帐户运行窗口服务。 – Pranav1688 2013-03-13 11:25:53

回答

9

一种选择是在后台应用程序自动启动时,用户登录并通过WCF或节俭监听来自服务的命令,或者只监视某个文件并从那里读取命令。

另一个选择是做你最初要求的 - 使用Windows API启动。但是代码非常可怕。这是一个可以使用的示例。它将执行下当前活动的用户会话的任何命令行,与CreateProcessInConsoleSession方法:

internal class ApplicationLauncher 
{ 
    public enum TOKEN_INFORMATION_CLASS 
    { 
     TokenUser = 1, 
     TokenGroups, 
     TokenPrivileges, 
     TokenOwner, 
     TokenPrimaryGroup, 
     TokenDefaultDacl, 
     TokenSource, 
     TokenType, 
     TokenImpersonationLevel, 
     TokenStatistics, 
     TokenRestrictedSids, 
     TokenSessionId, 
     TokenGroupsAndPrivileges, 
     TokenSessionReference, 
     TokenSandBoxInert, 
     TokenAuditPolicy, 
     TokenOrigin, 
     MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum 
    } 

    public const int READ_CONTROL = 0x00020000; 

    public const int STANDARD_RIGHTS_REQUIRED = 0x000F0000; 

    public const int STANDARD_RIGHTS_READ = READ_CONTROL; 
    public const int STANDARD_RIGHTS_WRITE = READ_CONTROL; 
    public const int STANDARD_RIGHTS_EXECUTE = READ_CONTROL; 

    public const int STANDARD_RIGHTS_ALL = 0x001F0000; 

    public const int SPECIFIC_RIGHTS_ALL = 0x0000FFFF; 

    public const int TOKEN_ASSIGN_PRIMARY = 0x0001; 
    public const int TOKEN_DUPLICATE = 0x0002; 
    public const int TOKEN_IMPERSONATE = 0x0004; 
    public const int TOKEN_QUERY = 0x0008; 
    public const int TOKEN_QUERY_SOURCE = 0x0010; 
    public const int TOKEN_ADJUST_PRIVILEGES = 0x0020; 
    public const int TOKEN_ADJUST_GROUPS = 0x0040; 
    public const int TOKEN_ADJUST_DEFAULT = 0x0080; 
    public const int TOKEN_ADJUST_SESSIONID = 0x0100; 

    public const int TOKEN_ALL_ACCESS_P = (STANDARD_RIGHTS_REQUIRED | 
              TOKEN_ASSIGN_PRIMARY | 
              TOKEN_DUPLICATE | 
              TOKEN_IMPERSONATE | 
              TOKEN_QUERY | 
              TOKEN_QUERY_SOURCE | 
              TOKEN_ADJUST_PRIVILEGES | 
              TOKEN_ADJUST_GROUPS | 
              TOKEN_ADJUST_DEFAULT); 

    public const int TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID; 

    public const int TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY; 

    public const int TOKEN_WRITE = STANDARD_RIGHTS_WRITE | 
            TOKEN_ADJUST_PRIVILEGES | 
            TOKEN_ADJUST_GROUPS | 
            TOKEN_ADJUST_DEFAULT; 

    public const int TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE; 

    public const uint MAXIMUM_ALLOWED = 0x2000000; 

    public const int CREATE_NEW_PROCESS_GROUP = 0x00000200; 
    public const int CREATE_UNICODE_ENVIRONMENT = 0x00000400; 

    public const int IDLE_PRIORITY_CLASS = 0x40; 
    public const int NORMAL_PRIORITY_CLASS = 0x20; 
    public const int HIGH_PRIORITY_CLASS = 0x80; 
    public const int REALTIME_PRIORITY_CLASS = 0x100; 

    public const int CREATE_NEW_CONSOLE = 0x00000010; 

    public const string SE_DEBUG_NAME = "SeDebugPrivilege"; 
    public const string SE_RESTORE_NAME = "SeRestorePrivilege"; 
    public const string SE_BACKUP_NAME = "SeBackupPrivilege"; 

    public const int SE_PRIVILEGE_ENABLED = 0x0002; 

    public const int ERROR_NOT_ALL_ASSIGNED = 1300; 

    private const uint TH32CS_SNAPPROCESS = 0x00000002; 

    public static int INVALID_HANDLE_VALUE = -1; 

    [DllImport("advapi32.dll", SetLastError = true)] 
    public static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpname, 
     [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid); 

    [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, 
     CallingConvention = CallingConvention.StdCall)] 
    public static extern bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, 
     ref SECURITY_ATTRIBUTES lpProcessAttributes, 
     ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment, 
     String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); 

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    public static extern bool DuplicateToken(IntPtr ExistingTokenHandle, 
     int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle); 

    [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] 
    public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess, 
     ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType, 
     int ImpersonationLevel, ref IntPtr DuplicateTokenHandle); 

    [DllImport("advapi32.dll", SetLastError = true)] 
    public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, 
     ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength); 

    [DllImport("advapi32.dll", SetLastError = true)] 
    public static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, 
     ref uint TokenInformation, uint TokenInformationLength); 

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

    public static bool CreateProcessInConsoleSession(String CommandLine, bool bElevate) 
    { 

     PROCESS_INFORMATION pi; 

     bool bResult = false; 
     uint dwSessionId, winlogonPid = 0; 
     IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero; 

     Debug.Print("CreateProcessInConsoleSession"); 
     // Log the client on to the local computer. 
     dwSessionId = WTSGetActiveConsoleSessionId(); 

     // Find the winlogon process 
     var procEntry = new PROCESSENTRY32(); 

     uint hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 
     if (hSnap == INVALID_HANDLE_VALUE) 
     { 
      return false; 
     } 

     procEntry.dwSize = (uint) Marshal.SizeOf(procEntry); //sizeof(PROCESSENTRY32); 

     if (Process32First(hSnap, ref procEntry) == 0) 
     { 
      return false; 
     } 

     String strCmp = "explorer.exe"; 
     do 
     { 
      if (strCmp.IndexOf(procEntry.szExeFile) == 0) 
      { 
       // We found a winlogon process...make sure it's running in the console session 
       uint winlogonSessId = 0; 
       if (ProcessIdToSessionId(procEntry.th32ProcessID, ref winlogonSessId) && 
        winlogonSessId == dwSessionId) 
       { 
        winlogonPid = procEntry.th32ProcessID; 
        break; 
       } 
      } 
     } 
     while (Process32Next(hSnap, ref procEntry) != 0); 

     //Get the user token used by DuplicateTokenEx 
     WTSQueryUserToken(dwSessionId, ref hUserToken); 

     var si = new STARTUPINFO(); 
     si.cb = Marshal.SizeOf(si); 
     si.lpDesktop = "winsta0\\default"; 
     var tp = new TOKEN_PRIVILEGES(); 
     var luid = new LUID(); 
     hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid); 

     if (
      !OpenProcessToken(hProcess, 
       TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY 
       | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE, ref hPToken)) 
     { 
      Debug.Print(String.Format("CreateProcessInConsoleSession OpenProcessToken error: {0}", 
       Marshal.GetLastWin32Error())); 
     } 

     if (!LookupPrivilegeValue(IntPtr.Zero, SE_DEBUG_NAME, ref luid)) 
     { 
      Debug.Print(String.Format("CreateProcessInConsoleSession LookupPrivilegeValue error: {0}", 
       Marshal.GetLastWin32Error())); 
     } 

     var sa = new SECURITY_ATTRIBUTES(); 
     sa.Length = Marshal.SizeOf(sa); 

     if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, 
       (int) SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int) TOKEN_TYPE.TokenPrimary, 
       ref hUserTokenDup)) 
     { 
      Debug.Print(
       String.Format(
        "CreateProcessInConsoleSession DuplicateTokenEx error: {0} Token does not have the privilege.", 
        Marshal.GetLastWin32Error())); 
      CloseHandle(hProcess); 
      CloseHandle(hUserToken); 
      CloseHandle(hPToken); 
      return false; 
     } 

     if (bElevate) 
     { 
      //tp.Privileges[0].Luid = luid; 
      //tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 

      tp.PrivilegeCount = 1; 
      tp.Privileges = new int[3]; 
      tp.Privileges[2] = SE_PRIVILEGE_ENABLED; 
      tp.Privileges[1] = luid.HighPart; 
      tp.Privileges[0] = luid.LowPart; 

      //Adjust Token privilege 
      if (
       !SetTokenInformation(hUserTokenDup, TOKEN_INFORMATION_CLASS.TokenSessionId, ref dwSessionId, 
        (uint) IntPtr.Size)) 
      { 
       Debug.Print(
        String.Format(
         "CreateProcessInConsoleSession SetTokenInformation error: {0} Token does not have the privilege.", 
         Marshal.GetLastWin32Error())); 
       //CloseHandle(hProcess); 
       //CloseHandle(hUserToken); 
       //CloseHandle(hPToken); 
       //CloseHandle(hUserTokenDup); 
       //return false; 
      } 
      if (
       !AdjustTokenPrivileges(hUserTokenDup, false, ref tp, Marshal.SizeOf(tp), /*(PTOKEN_PRIVILEGES)*/ 
        IntPtr.Zero, IntPtr.Zero)) 
      { 
       int nErr = Marshal.GetLastWin32Error(); 

       if (nErr == ERROR_NOT_ALL_ASSIGNED) 
       { 
        Debug.Print(
         String.Format(
          "CreateProcessInConsoleSession AdjustTokenPrivileges error: {0} Token does not have the privilege.", 
          nErr)); 
       } 
       else 
       { 
        Debug.Print(String.Format("CreateProcessInConsoleSession AdjustTokenPrivileges error: {0}", nErr)); 
       } 
      } 
     } 

     uint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE; 
     IntPtr pEnv = IntPtr.Zero; 
     if (CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true)) 
     { 
      dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; 
     } 
     else 
     { 
      pEnv = IntPtr.Zero; 
     } 
     // Launch the process in the client's logon session. 
     bResult = CreateProcessAsUser(hUserTokenDup, // client's access token 
      null, // file to execute 
      CommandLine, // command line 
      ref sa, // pointer to process SECURITY_ATTRIBUTES 
      ref sa, // pointer to thread SECURITY_ATTRIBUTES 
      false, // handles are not inheritable 
      (int) dwCreationFlags, // creation flags 
      pEnv, // pointer to new environment block 
      null, // name of current directory 
      ref si, // pointer to STARTUPINFO structure 
      out pi // receives information about new process 
      ); 
     // End impersonation of client. 

     //GetLastError should be 0 
     int iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error(); 

     //Close handles task 
     CloseHandle(hProcess); 
     CloseHandle(hUserToken); 
     CloseHandle(hUserTokenDup); 
     CloseHandle(hPToken); 

     return (iResultOfCreateProcessAsUser == 0) ? true : false; 
    } 

    [DllImport("kernel32.dll")] 
    private static extern int Process32First(uint hSnapshot, ref PROCESSENTRY32 lppe); 

    [DllImport("kernel32.dll")] 
    private static extern int Process32Next(uint hSnapshot, ref PROCESSENTRY32 lppe); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    private static extern uint CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID); 

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

    [DllImport("kernel32.dll")] 
    private static extern uint WTSGetActiveConsoleSessionId(); 

    [DllImport("Wtsapi32.dll")] 
    private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken); 

    [DllImport("kernel32.dll")] 
    private static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId); 

    [DllImport("kernel32.dll")] 
    private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); 

    [DllImport("advapi32", SetLastError = true)] 
    [SuppressUnmanagedCodeSecurity] 
    private static extern bool OpenProcessToken(IntPtr ProcessHandle, // handle to process 
     int DesiredAccess, // desired access to process 
     ref IntPtr TokenHandle); 

    #region Nested type: LUID 

    [StructLayout(LayoutKind.Sequential)] 
    internal struct LUID 
    { 
     public int LowPart; 
     public int HighPart; 
    } 

    #endregion 

    //end struct 

    #region Nested type: LUID_AND_ATRIBUTES 

    [StructLayout(LayoutKind.Sequential)] 
    internal struct LUID_AND_ATRIBUTES 
    { 
     public LUID Luid; 
     public int Attributes; 
    } 

    #endregion 

    #region Nested type: PROCESSENTRY32 

    [StructLayout(LayoutKind.Sequential)] 
    private struct PROCESSENTRY32 
    { 
     public uint dwSize; 
     public readonly uint cntUsage; 
     public readonly uint th32ProcessID; 
     public readonly IntPtr th32DefaultHeapID; 
     public readonly uint th32ModuleID; 
     public readonly uint cntThreads; 
     public readonly uint th32ParentProcessID; 
     public readonly int pcPriClassBase; 
     public readonly uint dwFlags; 

     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] 
     public readonly string szExeFile; 
    } 

    #endregion 

    #region Nested type: PROCESS_INFORMATION 

    [StructLayout(LayoutKind.Sequential)] 
    public struct PROCESS_INFORMATION 
    { 
     public IntPtr hProcess; 
     public IntPtr hThread; 
     public uint dwProcessId; 
     public uint dwThreadId; 
    } 

    #endregion 

    #region Nested type: SECURITY_ATTRIBUTES 

    [StructLayout(LayoutKind.Sequential)] 
    public struct SECURITY_ATTRIBUTES 
    { 
     public int Length; 
     public IntPtr lpSecurityDescriptor; 
     public bool bInheritHandle; 
    } 

    #endregion 

    #region Nested type: SECURITY_IMPERSONATION_LEVEL 

    private enum SECURITY_IMPERSONATION_LEVEL 
    { 
     SecurityAnonymous = 0, 
     SecurityIdentification = 1, 
     SecurityImpersonation = 2, 
     SecurityDelegation = 3, 
    } 

    #endregion 

    #region Nested type: STARTUPINFO 

    [StructLayout(LayoutKind.Sequential)] 
    public struct STARTUPINFO 
    { 
     public int cb; 
     public String lpReserved; 
     public String lpDesktop; 
     public String lpTitle; 
     public uint dwX; 
     public uint dwY; 
     public uint dwXSize; 
     public uint dwYSize; 
     public uint dwXCountChars; 
     public uint dwYCountChars; 
     public uint dwFillAttribute; 
     public uint dwFlags; 
     public short wShowWindow; 
     public short cbReserved2; 
     public IntPtr lpReserved2; 
     public IntPtr hStdInput; 
     public IntPtr hStdOutput; 
     public IntPtr hStdError; 
    } 

    #endregion 

    #region Nested type: TOKEN_PRIVILEGES 

    [StructLayout(LayoutKind.Sequential)] 
    internal struct TOKEN_PRIVILEGES 
    { 
     internal int PrivilegeCount; 
     //LUID_AND_ATRIBUTES 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] 
     internal int[] Privileges; 
    } 

    #endregion 

    #region Nested type: TOKEN_TYPE 

    private enum TOKEN_TYPE 
    { 
     TokenPrimary = 1, 
     TokenImpersonation = 2 
    } 

    #endregion 

    // handle to open access token 
} 
+0

这看起来是我正在寻找..我会读代码很好地评论,看起来很直接.. – user1403598 2013-03-13 11:51:54

+1

有趣的是,示例代码确实*什么[这篇文章在Windows SDK支持团队博客] (http://blogs.msdn.com/b/winsdk/archive/2009/07/14/launching-an-interactive-process-from-windows-service-in-windows-vista-and-later.aspx)说你应该“永不,永不做”:窃取运行在交互式用户桌面上的浏览器进程的访问令牌,打开该进程的访问令牌,以及使用该令牌创建新进程。 – 2013-03-13 12:31:19

+0

这段代码正是我正在寻找的。创建了一个新班级。丢弃了这段代码,发出了呼叫,并且第一次尝试。非常感谢您发布此信息。 – Grayson 2015-01-16 12:54:42

9

由于是与这些类型的有关Windows服务的问题,你在一个单用户操作系统的心态操作很常见。您决定将您的应用程序编写为服务的全部原因是因为您遇到了单用户操作系统的心智模型与多用户操作系统的现实之间的冲突。不幸的是,一项服务并没有解决你所有的问题,现在你正在设法弄清楚如何在最终注定要破解的设计中完成第二步。

事实是,你不能保证有一个“登录用户”。如果没有人登录到工作站,则不会有人登录,但您的服务仍在运行。

即使以某种方式通过确保有人会始终被上(不可能的)记录了过去的这一点,那么你会遇到在那里多个用户登录的情况。那么您的服务应该以哪一项开始?它应该随机选择其中一个吗?

是否有必要区分本地登录到控制台的用户和远程登录的用户?请记住,远程用户不会有本地控制台。

如果你能以某种方式通过所有这些障碍(不幸的是,可能通过将你的头埋在沙中并继续假装Windows是单用户操作系统),你可以利用WTSGetActiveConsoleSessionId函数获得当前会话ID,WTSQueryUserToken函数获取与该会话ID相对应的用户令牌,然后通过CreateProcessAsUser函数在该用户的上下文中启动您的进程。如果有的话。他们有适当的特权。物理控制台未连接到虚拟会话。而且您没有运行允许多个活动控制台会话的服务器SKU。和…

如果您可以决定要使用其帐户来启动辅助过程的特定用户,您可以登录该用户,操作其用户令牌,执行该过程,最后关闭过程并注销用户。 CreateProcessWithLogonUser function为你包装了很多这种苦差事,使得代码更加苗条。但是外观可能是骗人的,而且这仍然有一些安全含义,如果你首先提出这个问题,你可能不会完全理解。你真的不能不理解这样的安全风险。

此外,使用LogonUser登录的用户(使用CreateProcessWithLogonUser函数时自动完成)缺少可在其上启动交互式进程的窗口站和桌面。因此,如果您希望在该用户的环境中启动的过程将显示任何类型的用户界面,那么您的运气不佳。只要Windows尝试访问缺少必要权限的桌面,Windows就会终止您的应用。从Windows服务中,无法获得对您有用的桌面句柄(这对于解释您可能已知的一般规则,即服务无法显示任何类型的UI有很大的帮助)。

+0

我真的很喜欢“最终注定”。但是'CreateProcessWithLogonW'不会帮助他,因为他没有用户凭据。 – 2013-03-13 11:34:41

+0

Windows服务最初设计用于接收传入消息并根据请求处理它们(使用远程应用程序的消息系统插入服务中)。该服务正在故意运行,但现在需要额外的规范。不幸的是,某些Api需要作为当前用户被调用,因此现在是它的一个要求。我了解安全风险,但这只会引用一些为CU/Active用户定义的API。我可以和服务确保有一个用户登录到该机器,如果没有,它会报告回来。 – user1403598 2013-03-13 11:46:31

+0

“报告”是什么?没有用户登录!这些API需要调用什么?为什么他们需要作为特定用户进行调用?这似乎是解决您的问题的真正方法。 – 2013-03-13 11:55:55

相关问题