2012-02-01 11 views
12

Używam CreateProcessAsUser z usługi windows (, prosimy o pozostanie na temat i zakładamy, że mam bardzo dobry powód, aby to zrobić). W przeciwieństwie do tego, o co pytają wszyscy, dostaję okno w mojej aktywnej sesji terminalowej (sesja 1) zamiast tej samej sesji co usługa (sesja 0) - co jest niepożądane.CreateProcessAsUser Tworzenie okna w aktywnej sesji

Przywłaszczyłem Scott Allen's code; i wymyśliło następujące. Godne uwagi zmiany to "powrót do siebie", "CREATE_NO_WINDOW" i wsparcie dla wiersza poleceń.

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

Teraz zakładamy, że usługa jest uruchomiona w ramach „Domain \ MyService” i jestem zalogowany jako „domena \ Administrator” - a ja uruchamiania aplikacji konsoli jako proces roboczy. Kiedy korzystam z aplikacji klienckiej w celu uzyskania dostępu do usługi (usługa nie jest uruchamiana w trybie konsoli, tj. Jest w sesji 0) i wykonuję metodę, która wywołuje CreateProcessAsUser, proces roboczy pojawia się na moim pulpicie.

Teraz mogę zrobić to aplikacji systemu Windows bez okna, aby krok po kroku tworzenia okna konsoli; jednak na koniec dnia jest nadal tworzony w sesji 1.

Jakieś pomysły, dlaczego aplikacja konsolowa nie jest tworzona w tej samej sesji co usługa?

+0

Wygląda na to może mieć coś wspólnego z [ta ciemna magia] (http://alex-ionescu.com/?p=60), ale nie mogę dowiedzieć się, w jaki sposób go pominąć. –

+0

Próbowano użyć "Service-0 × 0-3e7 $ \ Default" jako pulpitu - co powoduje awarię aplikacji. –

+0

Jaką wersję systemu Windows? Czy próbowałeś zostawić lpDeskTop na zero? –

Odpowiedz

6

Jak Państwo zapewne wiedzą już, izolacja Session 0 jest ze względów bezpieczeństwa i można przeczytać więcej na ten temat tutaj http://msdn.microsoft.com/en-us/windows/hardware/gg463353.aspx

Odnośnie dlaczego aplikacja konsoli jest tworzony w aktywnej sesji (np sesji 1), to jest faktycznie linkowane bezpośrednio do twojego tokenu użytkownika. Gdy pytasz o token bieżącego użytkownika, token automatycznie przenosi z nim informacje o identyfikatorze sesji - w tym przypadku jest to sesja usług terminalu logowania (sesja 1). Ten identyfikator sesji jest odwołaniem do tokenu, który następnie został zreplikowany w DuplicateTokenEx, a następnie użyty w wywołaniu CreateProcessAsUser. W celu zmuszenia do tworzenia aplikacji konsoli w sesji 0, trzeba będzie zrobić wyraźne wezwanie do API SetTokenInformation (Advapi32.dll), przeszedł w swoim hDupedToken przed wywołaniem CreateProcessAsUser jak poniżej

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

Tutaj jest więcej informacji na temat SetTokenInformation http://msdn.microsoft.com/en-us/library/windows/desktop/aa379591(v=vs.85).aspx

+0

Myślałem, że to może być coś tak głupiego. Zamierzam dać temu szansę. –

+0

Bardzo dziękuję za pomoc - mam problemy z uzyskaniem prawa "SE_TCB_NAME" (potrzebne do "SetTokenInformation"). Przyznałem moje konto serwisowe jako "Prawo w ramach systemu operacyjnego" (jak również USŁUGA w zakresie ubezpieczenia typu shotgun). Próbowałem używać 'OpenThreadToken' z' TOKEN_ALL_ACCESS' - bezskutecznie. Zdobyłeś +250 - mam tylko nadzieję, że masz trochę więcej doświadczenia w tym zakresie, aby mi pomóc. –

+0

Dostajesz nagrodę. Czas rozpocząć kolejną nagrodę;). –

0

Spróbuj rozmyć się z parametrem o nazwie CharSet o nazwach MarshalAs, StructLayout i DllImport. Aby to zrobić, może być konieczne dodanie MarshalAs do różnych ciągów. Nie przejmuj się Unicode: nie używasz tego. Zalecam najpierw ustawić je wszystkie na CharSet.Ansi. Przeprowadź wszystkie testy, które już wypróbowałeś - czyli ustawienia pulpitu i wszystkie fajne rzeczy. Jeśli ulegnie awarii, przełącz wszystkie na automatyczne. Jeśli to nadal nie działa, usuń je wszystkie.

Zakładając, że nic z tego nie działa, przełącz na CreateUserProcessW i CharSet.Unicode, aby wiedzieć, co otrzymujesz. Zastanówmy się, po prostu przejdź do tego kroku.

Jeśli trzeba ustawić UnmanagedType z MarshalAs ciągów, chcesz UnmanagedType.LPStr dla ANSI UnmanagedType.LPTStr Auto, oraz UnmanagedType.LPWStr dla Unicode. Właściwie to rób to dla wszystkich swoich ciągów.

+0

Nigdy nie musiałem ustawiać CharSet dla tych funkcji, chyba że były to warianty A lub W, więc stawiam zakłady na Auto. – Zenexer

+0

dzięki - Dam ci dzisiaj wieczór i zgłoś się. –

+0

Powodzenia! Jeśli czujesz się ambitny, przejdź od razu do Unicode. – Zenexer

1

Byłem w stanie zaimplementować początkowy wpis jako działające rozwiązanie na moim końcu, jednak nie mogę znaleźć sposobu, aby ukryć okno konsoli.Próbowałem STARTF_USESHOWWINDOW i SW_HIDE, ale moje okno poleceń nadal się wyskakuje. Każdy pomysł, dlaczego?

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); 
     } 
    } 
Powiązane problemy