2013-03-13 91 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

相關問題