2012-02-01 51 views
12

我正在使用來自Windows服務的CreateProcessAsUser(請我們可以留在話題上,並假設我有一個很好的理由來做這個)。與其他人在這裏問的相反,我在活動終端會話(會話1)中獲得了一個窗口,而不是與服務(會話0)相同的會話 - 這是不受歡迎的。CreateProcessAsUser在活動會話中創建窗口

我撥Scott Allen's code;並提出以下幾點。值得注意的變化是「恢復自我」,「CREATE_NO_WINDOW」和命令行參數支持。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Security; 
using System.Runtime.InteropServices; 
using System.Diagnostics; 
using System.Security.Principal; 
using System.ComponentModel; 
using System.IO; 

namespace SourceCode.Runtime.ChildProcessService 
{ 
    [SuppressUnmanagedCodeSecurity] 
    class NativeMethods 
    { 
     [StructLayout(LayoutKind.Sequential)] 
     public struct STARTUPINFO 
     { 
      public Int32 cb; 
      public string lpReserved; 
      public string lpDesktop; 
      public string lpTitle; 
      public Int32 dwX; 
      public Int32 dwY; 
      public Int32 dwXSize; 
      public Int32 dwXCountChars; 
      public Int32 dwYCountChars; 
      public Int32 dwFillAttribute; 
      public Int32 dwFlags; 
      public Int16 wShowWindow; 
      public Int16 cbReserved2; 
      public IntPtr lpReserved2; 
      public IntPtr hStdInput; 
      public IntPtr hStdOutput; 
      public IntPtr hStdError; 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     public struct PROCESS_INFORMATION 
     { 
      public IntPtr hProcess; 
      public IntPtr hThread; 
      public Int32 dwProcessID; 
      public Int32 dwThreadID; 
     } 

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

     public enum SECURITY_IMPERSONATION_LEVEL 
     { 
      SecurityAnonymous, 
      SecurityIdentification, 
      SecurityImpersonation, 
      SecurityDelegation 
     } 

     public enum TOKEN_TYPE 
     { 
      TokenPrimary = 1, 
      TokenImpersonation 
     } 

     public const int GENERIC_ALL_ACCESS = 0x10000000; 
     public const int CREATE_NO_WINDOW = 0x08000000; 

     [ 
      DllImport("kernel32.dll", 
       EntryPoint = "CloseHandle", SetLastError = true, 
       CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall) 
     ] 
     public static extern bool CloseHandle(IntPtr handle); 

     [ 
      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, Int32 dwCreationFlags, IntPtr lpEnvrionment, 
           string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, 
           ref PROCESS_INFORMATION lpProcessInformation); 

     [ 
      DllImport("advapi32.dll", 
       EntryPoint = "DuplicateTokenEx") 
     ] 
     public static extern bool 
      DuplicateTokenEx(IntPtr hExistingToken, Int32 dwDesiredAccess, 
          ref SECURITY_ATTRIBUTES lpThreadAttributes, 
          Int32 ImpersonationLevel, Int32 dwTokenType, 
          ref IntPtr phNewToken); 

     public static Process CreateProcessAsUser(string filename, string args) 
     { 
      var hToken = WindowsIdentity.GetCurrent().Token; 
      var hDupedToken = IntPtr.Zero; 

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

      try 
      { 
       if (!DuplicateTokenEx(
         hToken, 
         GENERIC_ALL_ACCESS, 
         ref sa, 
         (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, 
         (int)TOKEN_TYPE.TokenPrimary, 
         ref hDupedToken 
        )) 
        throw new Win32Exception(Marshal.GetLastWin32Error()); 

       var si = new STARTUPINFO(); 
       si.cb = Marshal.SizeOf(si); 
       si.lpDesktop = ""; 

       var path = Path.GetFullPath(filename); 
       var dir = Path.GetDirectoryName(path); 

       // Revert to self to create the entire process; not doing this might 
       // require that the currently impersonated user has "Replace a process 
       // level token" rights - we only want our service account to need 
       // that right. 
       using (var ctx = WindowsIdentity.Impersonate(IntPtr.Zero)) 
       { 
        if (!CreateProcessAsUser(
              hDupedToken, 
              path, 
              string.Format("\"{0}\" {1}", filename.Replace("\"", "\"\""), args), 
              ref sa, ref sa, 
              false, 0, IntPtr.Zero, 
              dir, ref si, ref pi 
            )) 
         throw new Win32Exception(Marshal.GetLastWin32Error()); 
       } 

       return Process.GetProcessById(pi.dwProcessID); 
      } 
      finally 
      { 
       if (pi.hProcess != IntPtr.Zero) 
        CloseHandle(pi.hProcess); 
       if (pi.hThread != IntPtr.Zero) 
        CloseHandle(pi.hThread); 
       if (hDupedToken != IntPtr.Zero) 
        CloseHandle(hDupedToken); 
      } 
     } 
    } 
} 

現在假設服務是在「域\爲MyService」運行,目前我登錄的域\管理員「 - 和我啓動一個控制檯應用程序的工作進程。當我使用一個客戶端應用程序訪問服務(該服務沒有在控制檯模式,即它是在會話0開始),並執行調用CreateProcessAsUser工作進程出現在我的桌面上的方法。

現在我可以使它沒有窗口的Windows應用程序,以側步控制檯窗口的創建;然而,在一天結束的時候它仍然在會話1

爲什麼控制檯應用程序未在同一會話服務創建任何想法創造出來的?

+0

看起來它可能有一些做的[這個黑暗魔法(http://alex-ionescu.com/?p=60),但我無法弄清楚如何跳過它。 – 2012-02-01 12:46:05

+0

使用「Service-0×0-3e7 $ \ Default」作爲桌面 - 導致應用程序崩潰。 – 2012-02-01 13:08:00

+0

什麼版本的Windows?你有沒有試過把lpDeskTop放在空? – 2012-02-07 13:41:51

回答

6

正如你可能知道已經,會話0的隔離是出於安全原因,你可以在這裏 http://msdn.microsoft.com/en-us/windows/hardware/gg463353.aspx

閱讀更多關於它對於爲什麼在活動會話(如會話1創建控制檯應用程序),這實際上直接鏈接到您的用戶令牌。當您詢問當前用戶令牌時,該令牌會自動攜帶會話ID信息 - 在這種情況下,它是登錄終端服務會話(會話1)。此會話ID由令牌引用,然後在DuplicateTokenEx中複製,然後在CreateProcessAsUser調用中使用該令牌。爲了迫使在會話0的控制檯應用程序的創建,你將需要調用CreateProcessAsUser像下面

.................. 
UInt32 dwSessionId = 0; // set it to session 0 
SetTokenInformation(hDupedToken, TokenInformationClass.TokenSessionId, ref dwSessionId, (UInt32) IntPtr.Size); 
................. 
CreateProcessAsUser(hDupedToken, ....) 

這裏之前做出的SetTokenInformation API(ADVAPI32.DLL),在您的hDupedToken通過顯式調用是更多的信息SetTokenInformation http://msdn.microsoft.com/en-us/library/windows/desktop/aa379591(v=vs.85).aspx

+0

我認爲這可能是愚蠢的。我打算給這個鏡頭。 – 2012-02-08 12:44:26

+0

非常感謝您的幫助 - 我只是在獲取「SE_TCB_NAME」權限(SetTokenInformation需要)時遇到問題。我已授予我的服務帳戶「作爲操作系統的一部分」的權利(以及針對獵槍保險的服務)。我曾嘗試使用帶'TOKEN_ALL_ACCESS'的'OpenThreadToken' - 無濟於事。你已經贏得了250美元 - 我只是希望你在這方面有更多的專業知識幫助我。 – 2012-02-08 16:00:08

+0

你得到賞金。時間開始另一個賞金;)。 – 2012-02-08 22:26:45

0

嘗試使用CharSet命名參數MarshalAs,StructLayoutDllImport。您可能需要將MarshalAs添加到各種字符串才能執行此操作。不要打擾Unicode:你不使用這個。我建議先將它們全部設置爲CharSet.Ansi。運行您已經嘗試過的所有測試 - 即設置桌面和所有有趣的東西。如果它崩潰,將它們全部切換到自動。如果仍然無法使用,請全部刪除。

假設這些都不起作用,切換到CreateUserProcessWCharSet.Unicode,這樣你就知道你在得到什麼。再想一想,就跳到這一步。

如果您需要設置UnmanagedTypeMarshalAs字符串,你要爲UnmanagedType.LPStr ANSI,UnmanagedType.LPTStr爲自動,和UnmanagedType.LPWStr對Unicode。實際上,無論如何都要爲所有的字符串做這個。

+0

我從來不必爲這些函數設置CharSet,除非它們是A或W變體,所以我將我的賭注放在Auto上。 – Zenexer 2012-02-08 08:39:53

+0

謝謝 - 今晚我會放棄並報告。 – 2012-02-08 09:11:41

+0

祝你好運!如果你覺得雄心勃勃,可以直接跳到Unicode。 – Zenexer 2012-02-08 09:13:24

1

我能夠實現初始職位作爲工作解決方案,但我似乎無法找到一種方法來保持我的控制檯窗口隱藏。我嘗試了STARTF_USESHOWWINDOW和SW_HIDE,但我的命令窗口仍然彈出。任何想法爲什麼?

public const int STARTF_USESHOWWINDOW = 0x0000000; 
    public const int SW_HIDE = 0; 


    public static Process CreateProcessAsUser(string filename, string args) 
    { 
     var hToken = WindowsIdentity.GetCurrent().Token; 
     var hDupedToken = IntPtr.Zero; 

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

     try 
     { 
      if (!DuplicateTokenEx(
        hToken, 
        GENERIC_ALL_ACCESS, 
        ref sa, 
        (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, 
        (int)TOKEN_TYPE.TokenPrimary, 
        ref hDupedToken 
       )) 
       throw new Win32Exception(Marshal.GetLastWin32Error()); 

      var si = new STARTUPINFO(); 
      si.cb = Marshal.SizeOf(si); 
      si.lpDesktop = String.Empty; 

      si.dwFlags = STARTF_USESHOWWINDOW; 
      si.wShowWindow = SW_HIDE; 

      var path = Path.GetFullPath(filename); 
      var dir = Path.GetDirectoryName(path); 

      // Revert to self to create the entire process; not doing this might 
      // require that the currently impersonated user has "Replace a process 
      // level token" rights - we only want our service account to need 
      // that right. 
      using (var ctx = WindowsIdentity.Impersonate(IntPtr.Zero)) 
      { 
       UInt32 dwSessionId = 1; // set it to session 0 
       SetTokenInformation(hDupedToken, TOKEN_INFORMATION_CLASS.TokenSessionId, 
        ref dwSessionId, (UInt32)IntPtr.Size); 
       if (!CreateProcessAsUser(
             hDupedToken, 
             path, 
             string.Format("\"{0}\" {1}", filename.Replace("\"", "\"\""), args), 
             ref sa, ref sa, 
             false, 0, IntPtr.Zero, 
             dir, ref si, ref pi 
           )) 
        throw new Win32Exception(Marshal.GetLastWin32Error()); 
      } 

      return Process.GetProcessById(pi.dwProcessID); 
     } 
     finally 
     { 
      if (pi.hProcess != IntPtr.Zero) 
       CloseHandle(pi.hProcess); 
      if (pi.hThread != IntPtr.Zero) 
       CloseHandle(pi.hThread); 
      if (hDupedToken != IntPtr.Zero) 
       CloseHandle(hDupedToken); 
     } 
    }