2012-02-01 16 views
12

Estoy usando CreateProcessAsUser desde un servicio de Windows (por favor podemos permanecer en el tema y asumir que tengo una muy buena razón para hacer esto). Contrario a lo que todos los demás están preguntando, estoy obteniendo una ventana en mi sesión de terminal activa (sesión 1) en lugar de la misma sesión que el servicio (sesión 0), lo cual es indeseable.CreateProcessAsUser Ventana de creación en la sesión activa

Me apropié Scott Allen's code; y se le ocurrió lo siguiente. Cambios notables son el "revertir a sí mismo", el "CREATE_NO_WINDOW" y el soporte de argumentos de línea de comandos.

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

Supongamos ahora que el servicio se está ejecutando en 'Dominio \ MyService' y actualmente estoy conectado como 'Dominio \ Administrador' - y estoy iniciando una aplicación de consola como un proceso de trabajo. Cuando uso una aplicación cliente para acceder al servicio (el servicio no se inicia en modo consola, es decir, está en la sesión 0) y ejecuto el método que invoca el CreateProcessAsUser, aparece el proceso de trabajo en mi escritorio.

Ahora podría convertirlo en una aplicación de Windows sin ventana para dar un paso al costado de la creación de la ventana de la consola; sin embargo, al final del día todavía se está creando en la sesión 1.

¿Alguna idea de por qué la aplicación de la consola no se está creando en la misma sesión que el servicio?

+0

Parece que podría tener algo que ver con [esta magia oscura] (http://alex-ionescu.com/?p=60), pero no puedo encontrar la manera de evitarlo. –

+0

Se intentó usar "Servicio-0 × 0-3e7 $ \ Predeterminado" como el escritorio, lo que hace que la aplicación se cuelgue. –

+0

¿Qué versión de Windows? ¿Has intentado dejar lpDeskTop en null? –

Respuesta

6

Como usted probablemente sabe ya, el aislamiento de la sesión 0 es por razones de seguridad y se puede leer más sobre esto aquí http://msdn.microsoft.com/en-us/windows/hardware/gg463353.aspx

En cuanto a por qué se crea la aplicación de consola en sesión activa (por ejemplo, la sesión 1), esto en realidad se vinculó directamente a tu token de usuario. Cuando solicita un token de usuario actual, este token lleva automáticamente la información de identificación de sesión, en este caso es la sesión de servicios de terminal de inicio de sesión (sesión 1). Esta identificación de sesión es referencia por el token que luego se replica en DuplicateTokenEx y luego se utiliza en la llamada CreateProcessAsUser. Con el fin de forzar la creación de la aplicación de consola en la sesión 0, tendrá que hacer una llamada explícita a la API SetTokenInformation (advapi32.dll), aprobada en su hDupedToken antes de llamar CreateProcessAsUser, como a continuación

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

Aquí es más información sobre SetTokenInformation http://msdn.microsoft.com/en-us/library/windows/desktop/aa379591(v=vs.85).aspx

+0

Pensé que podría ser algo así de estúpido. Voy a dar una oportunidad. –

+0

Muchas gracias por la ayuda. Solo estoy teniendo problemas para adquirir el derecho 'SE_TCB_NAME' (necesario para' SetTokenInformation'). Le he otorgado a mi cuenta de servicio el derecho "Actuar como parte del sistema operativo" (así como el SERVICIO para el seguro de escopeta). He intentado usar 'OpenThreadToken' con' TOKEN_ALL_ACCESS' - en vano. Usted ha obtenido el +250 - Solo espero que tenga un poco más de experiencia en este sentido para ayudarme. –

+0

Obtienes la recompensa. Hora de comenzar otra generosidad;). –

0

Trate de perder el tiempo con el parámetro llamado CharSet de MarshalAs, StructLayout y DllImport. Es posible que deba agregar MarshalAs a varias cadenas para hacer esto. No te molestes con Unicode: no estás usando esto. Recomiendo configurarlos todos a CharSet.Ansi primero. Ejecute todas las pruebas que ya haya probado, es decir, configure el escritorio y todas esas cosas divertidas. Si se bloquea, cámbielos todos a automático. Si todavía no funciona, elimínelos todos.

Suponiendo que nada de esto funciona, cambie a CreateUserProcessW y CharSet.Unicode para que sepa lo que está recibiendo. Pensándolo bien, salte a este paso.

Si necesita ajustar la UnmanagedType con MarshalAs para cuerdas, que desea UnmanagedType.LPStr para ANSI, UnmanagedType.LPTStr para el automóvil, y UnmanagedType.LPWStr para Unicode. En realidad, haz esto para todas tus cadenas de todos modos.

+0

Nunca tuve que configurar el CharSet para estas funciones a menos que fueran variantes A o W, así que estoy apostando a Auto. – Zenexer

+0

gracias - Lo intentaré esta noche e informaré. –

+0

¡Buena suerte! Si te sientes ambicioso, salta directamente a Unicode. – Zenexer

1

Pude implementar la publicación inicial como una solución de trabajo en mi extremo, sin embargo, parece que no puedo encontrar una manera de mantener oculta la ventana de mi consola.Intenté STARTF_USESHOWWINDOW y SW_HIDE pero aún aparece la ventana de mi comando. ¿Alguna idea de por qué?

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