CreateProcessAsUser Création d’une fenêtre dans une session active

J’utilise CreateProcessAsUser à partir d’un service Windows ( pouvons-nous restr sur le sujet et supposer que j’ai une très bonne raison de le faire ). Contrairement à ce que tout le monde demande, je reçois une fenêtre dans ma session de terminal active (session 1) au lieu de la même session que le service (session 0) – ce qui n’est pas souhaitable.

Je me suis approprié le code de Scott Allen ; et est venu avec ce qui suit. Les changements notables sont le “retour à l’auto”, le “CREATE_NO_WINDOW” et le support des arguments en ligne de commande.

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 ssortingng lpReserved; public ssortingng lpDesktop; public ssortingng lpTitle; public Int32 dwX; public Int32 dwY; public Int32 dwXSize; public Int32 dwXCountChars; public Int32 dwYCountChars; public Int32 dwFillAtsortingbute; 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, ssortingng lpApplicationName, ssortingng lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAtsortingbutes, ref SECURITY_ATTRIBUTES lpThreadAtsortingbutes, bool bInheritHandle, Int32 dwCreationFlags, IntPtr lpEnvrionment, ssortingng 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 lpThreadAtsortingbutes, Int32 ImpersonationLevel, Int32 dwTokenType, ref IntPtr phNewToken); public static Process CreateProcessAsUser(ssortingng filename, ssortingng 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, ssortingng.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); } } } } 

Supposons maintenant que le service s’exécute sous ‘Domain \ MyService’ et que je suis actuellement connecté en tant que ‘Domain \ Administrator’ – et que je lance une application de console en tant que processus de travail. Lorsque j’utilise une application cliente pour accéder au service (le service n’est pas démarré en mode console, c’est-à-dire qu’il est en session 0) et que CreateProcessAsUser la méthode qui appelle CreateProcessAsUser le processus de travail apparaît sur mon bureau.

Je pourrais maintenant en faire une application Windows sans fenêtre pour contourner la création de la fenêtre de la console; Cependant, à la fin de la journée, il est toujours créé dans la session 1.

Des idées sur la raison pour laquelle l’application console n’est pas créée dans la même session que le service?

Comme vous le savez probablement déjà, la session 0 est isolée pour des raisons de sécurité. Pour en savoir plus, cliquez ici http://msdn.microsoft.com/en-us/windows/hardware/gg463353.aspx

En ce qui concerne les raisons pour lesquelles votre application console est créée en session active (par exemple, la session 1), cela est en fait lié directement à votre jeton d’utilisateur. Lorsque vous demandez le jeton de l’utilisateur actuel, celui-ci est automatiquement accompagné des informations d’identification de session. Dans ce cas, il s’agit de la session de services de terminal de connexion (session 1). Cet identifiant de session est référencé par le jeton qui est ensuite répliqué dans DuplicateTokenEx puis utilisé dans l’appel CreateProcessAsUser. Pour forcer la création de votre application console au cours de la session 0, vous devez effectuer un appel explicite à l’API SetTokenInformation (advapi32.dll), passé dans votre hDupedToken avant d’appeler CreateProcessAsUser, comme ci-dessous.

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

Voici plus d’informations sur SetTokenInformation http://msdn.microsoft.com/en-us/library/windows/desktop/aa379591(v=vs.85).aspx

J’ai pu implémenter le message initial en tant que solution opérationnelle, mais je n’arrive pas à trouver un moyen de garder ma fenêtre de console masquée. J’ai essayé STARTF_USESHOWWINDOW et SW_HIDE mais ma fenêtre de commande apparaît toujours. Une idée pourquoi?

  public const int STARTF_USESHOWWINDOW = 0x0000000; public const int SW_HIDE = 0; public static Process CreateProcessAsUser(ssortingng filename, ssortingng 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 = Ssortingng.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, ssortingng.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); } } 

Essayez de jouer avec le paramètre nommé MarshalAs de MarshalAs , StructLayout et DllImport . Pour ce faire, vous devrez peut-être append MarshalAs à différentes chaînes. Ne vous embêtez pas avec Unicode: vous ne l’utilisez pas. Je recommande de les définir tous d’abord sur CharSet.Ansi . Exécutez tous les tests que vous avez déjà effectués, à savoir la configuration du bureau et tout ce qui est amusant. Si cela se bloque, mettez-les tous en mode automatique. Si cela ne fonctionne toujours pas, supprimez-les tous.

En supposant que rien ne fonctionne, passez à CreateUserProcessW et CharSet.Unicode afin de savoir ce que vous obtenez. À la reflection, passez directement à cette étape.

Si vous devez définir UnmanagedType avec MarshalAs pour les chaînes, vous souhaitez UnmanagedType.LPStr pour Ansi, UnmanagedType.LPTStr pour Auto et UnmanagedType.LPWStr pour Unicode. En fait, faites cela pour toutes vos chaînes de toute façon.