2012-02-01 16 views
12

Sto utilizzando CreateProcessAsUser da un servizio Windows (, per favore possiamo rimanere in argomento e suppongo di avere una buona ragione per farlo). Contrariamente a quanto chiunque altro sta chiedendo qui, sto ricevendo una finestra nella mia sessione terminale attiva (sessione 1) invece della stessa sessione del servizio (sessione 0) - il che è indesiderabile.CreateProcessAsUser Creazione di una finestra in sessione attiva

Appropriato Scott Allen's code; e si avvicinò con il seguente. Le modifiche importanti sono il "ripristino di sé", il "CREATE_NO_WINDOW" e il supporto degli argomenti della riga di comando.

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); 
      } 
     } 
    } 
} 

Supponiamo ora che il servizio è in esecuzione in 'Domain \ MyService' e sto attualmente loggato come 'Dominio \ Administrator' - e sto avviando un'applicazione console come un processo di lavoro. Quando utilizzo un'applicazione client per accedere al servizio (il servizio non viene avviato in modalità console, ad esempio nella sessione 0) ed esegue il metodo che richiama il processo CreateProcessAsUser sul desktop.

Ora potrei farne un'applicazione Windows senza finestre per fare il passo alla creazione della finestra della console; tuttavia, alla fine della giornata è ancora in fase di creazione nella sessione 1.

Qualche motivo per cui l'applicazione della console non viene creata nella stessa sessione del servizio?

+0

Sembra che potrebbe avere qualcosa a che fare con [questa magia nera] (http://alex-ionescu.com/?p=60), ma non riesco a capire come saltarlo. –

+0

Provato usando "Service-0 × 0-3e7 $ \ Default" come desktop - che causa l'arresto anomalo dell'applicazione. –

+0

Quale versione di Windows? Hai provato a lasciare lpDeskTop su null? –

risposta

6

Come probabilmente sapete già, l'isolamento della sessione 0 è per ragioni di sicurezza e si può leggere di più qui http://msdn.microsoft.com/en-us/windows/hardware/gg463353.aspx

Per quanto riguarda il motivo per cui la console app viene creato in sessione attiva (ad esempio, la sessione 1), questo in realtà è collegato direttamente al token dell'utente. Quando chiedi il token utente corrente, questo token porta automaticamente con sé le informazioni dell'ID di sessione - in questo caso è la sessione di servizi del terminale di accesso (sessione 1). Questo ID di sessione fa riferimento al token che viene quindi replicato in DuplicateTokenEx e quindi utilizzato nella chiamata CreateProcessAsUser. Al fine di imporre la creazione della vostra applicazione console in sessione 0, è necessario effettuare una chiamata esplicita alla API SetTokenInformation (advapi32.dll), approvata nel tuo hDupedToken prima di chiamare CreateProcessAsUser come qui di seguito

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

Qui è più informazioni su SetTokenInformation http://msdn.microsoft.com/en-us/library/windows/desktop/aa379591(v=vs.85).aspx

+0

Ho pensato che potesse essere qualcosa di stupido come questo. Ho intenzione di dare questo colpo. –

+0

Mille grazie per l'aiuto - Ho appena avuto problemi con l'acquisizione del 'SE_TCB_NAME' giusto (necessario per' SetTokenInformation'). Ho concesso al mio account di servizio il diritto "Agisci come parte del sistema operativo" (oltre a SERVICE for shotgun insurance). Ho provato a utilizzare 'OpenThreadToken' con' TOKEN_ALL_ACCESS' - senza alcun risultato. Hai guadagnato il +250 - spero solo che tu abbia un po 'più di esperienza al riguardo per aiutarmi. –

+0

Ottieni la taglia. È ora di iniziare un'altra taglia;). –

0

Provare a fare scherzi con il parametro CharSet denominato MarshalAs, StructLayout e DllImport. Potrebbe essere necessario aggiungere MarshalAs a varie stringhe per fare ciò. Non preoccuparti di Unicode: non lo stai usando. Consiglio di impostarli tutti su CharSet.Ansi. Esegui tutti i test che hai già provato, ovvero, impostando il desktop e tutte quelle cose divertenti. Se si blocca, passa a tutti in automatico. Se continua a non funzionare, rimuovili tutti.

Supponendo che nessuna di queste funzioni, passare a CreateUserProcessW e CharSet.Unicode in modo da sapere cosa stai ottenendo. A pensarci bene, salta a questo passaggio.

Se è necessario impostare la UnmanagedType con MarshalAs per le stringhe, si vuole UnmanagedType.LPStr per Ansi, UnmanagedType.LPTStr per Auto e UnmanagedType.LPWStr per Unicode. In realtà, fallo comunque per tutte le tue corde.

+0

Non ho mai dovuto impostare il CharSet per queste funzioni a meno che non fossero varianti A o W, quindi sto mettendo le mie puntate su Auto. – Zenexer

+0

grazie - ci proveremo stasera e riferirò. –

+0

Buona fortuna! Se ti senti ambizioso, passa direttamente a Unicode. – Zenexer

1

Sono stato in grado di implementare il post iniziale come soluzione di lavoro sulla mia estremità, tuttavia, non riesco a trovare un modo per mantenere la mia finestra della console nascosta.Ho provato STARTF_USESHOWWINDOW e SW_HIDE ma la finestra di comando continua a comparire. Qualche idea del perché?

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); 
     } 
    } 
Problemi correlati