2013-03-13 3 views
5

В настоящее время у меня есть служба Windows, работающая под учетной записью системы. Моя проблема в том, что мне нужно запустить определенные процессы из службы в качестве текущего зарегистрированного пользователя. У меня есть код и т. Д., Чтобы получить текущий вход в систему/активный сеанс.Запустить процесс из службы Windows в качестве текущего пользователя

Моя проблема заключается в том, что мне нужно порождать процесс как зарегистрированного пользователя, но не будет знать учетные данные пользователя и т.д.

Сервис .net скомпилированные сервис и я ожидаю, что мне нужно использовать некоторые методы PInvoke к получить дескриптор одного из текущих процессов пользователей, чтобы дублировать его и обедать как процесс с помощью дескриптора.

К сожалению, я не могу найти хорошую документацию/решение о том, как ее реализовать?

Если кто-то может дать мне руководство/пример, я был бы очень признателен.

* Обновлено * Я думаю, что я объяснил это неправильно и вам нужно reajust в соответствии с тем, что я на самом деле требую. Я не обязательно хочу запускать новый процесс, я просто хочу выдавать себя за пользователя. Я был настолько обернут взглядом на CreateProcess и т. Д. Я провел по пути создания нового процесса как текущего зарегистрированного пользователя (что не совсем то, что я хочу сделать).

В свою очередь, я просто хочу запустить код под текущим контекстом пользователя (олицетворять текущего пользователя)?

+0

Что делать, если пользователь не в сети? –

+0

Я думаю, вы пытаетесь запустить службу окна как учетную запись администратора. – Pranav1688

ответ

9

Один из вариантов - иметь фоновое приложение, которое автоматически запускается, когда пользователь входит в систему и слушает команды из вашей службы через WCF, или бережливость, или просто контролируя некоторые файлы и команду чтения оттуда.

Другой вариант - сделать то, что вы изначально запросили - запустить с помощью API окон. Но код довольно пугающий. Вот пример, который вы можете использовать. Он будет выполнять любую командную строку под активным сеансом пользователя, с помощью метода CreateProcessInConsoleSession:

internal class ApplicationLauncher 
{ 
    public enum TOKEN_INFORMATION_CLASS 
    { 
     TokenUser = 1, 
     TokenGroups, 
     TokenPrivileges, 
     TokenOwner, 
     TokenPrimaryGroup, 
     TokenDefaultDacl, 
     TokenSource, 
     TokenType, 
     TokenImpersonationLevel, 
     TokenStatistics, 
     TokenRestrictedSids, 
     TokenSessionId, 
     TokenGroupsAndPrivileges, 
     TokenSessionReference, 
     TokenSandBoxInert, 
     TokenAuditPolicy, 
     TokenOrigin, 
     MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum 
    } 

    public const int READ_CONTROL = 0x00020000; 

    public const int STANDARD_RIGHTS_REQUIRED = 0x000F0000; 

    public const int STANDARD_RIGHTS_READ = READ_CONTROL; 
    public const int STANDARD_RIGHTS_WRITE = READ_CONTROL; 
    public const int STANDARD_RIGHTS_EXECUTE = READ_CONTROL; 

    public const int STANDARD_RIGHTS_ALL = 0x001F0000; 

    public const int SPECIFIC_RIGHTS_ALL = 0x0000FFFF; 

    public const int TOKEN_ASSIGN_PRIMARY = 0x0001; 
    public const int TOKEN_DUPLICATE = 0x0002; 
    public const int TOKEN_IMPERSONATE = 0x0004; 
    public const int TOKEN_QUERY = 0x0008; 
    public const int TOKEN_QUERY_SOURCE = 0x0010; 
    public const int TOKEN_ADJUST_PRIVILEGES = 0x0020; 
    public const int TOKEN_ADJUST_GROUPS = 0x0040; 
    public const int TOKEN_ADJUST_DEFAULT = 0x0080; 
    public const int TOKEN_ADJUST_SESSIONID = 0x0100; 

    public const int TOKEN_ALL_ACCESS_P = (STANDARD_RIGHTS_REQUIRED | 
              TOKEN_ASSIGN_PRIMARY | 
              TOKEN_DUPLICATE | 
              TOKEN_IMPERSONATE | 
              TOKEN_QUERY | 
              TOKEN_QUERY_SOURCE | 
              TOKEN_ADJUST_PRIVILEGES | 
              TOKEN_ADJUST_GROUPS | 
              TOKEN_ADJUST_DEFAULT); 

    public const int TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID; 

    public const int TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY; 

    public const int TOKEN_WRITE = STANDARD_RIGHTS_WRITE | 
            TOKEN_ADJUST_PRIVILEGES | 
            TOKEN_ADJUST_GROUPS | 
            TOKEN_ADJUST_DEFAULT; 

    public const int TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE; 

    public const uint MAXIMUM_ALLOWED = 0x2000000; 

    public const int CREATE_NEW_PROCESS_GROUP = 0x00000200; 
    public const int CREATE_UNICODE_ENVIRONMENT = 0x00000400; 

    public const int IDLE_PRIORITY_CLASS = 0x40; 
    public const int NORMAL_PRIORITY_CLASS = 0x20; 
    public const int HIGH_PRIORITY_CLASS = 0x80; 
    public const int REALTIME_PRIORITY_CLASS = 0x100; 

    public const int CREATE_NEW_CONSOLE = 0x00000010; 

    public const string SE_DEBUG_NAME = "SeDebugPrivilege"; 
    public const string SE_RESTORE_NAME = "SeRestorePrivilege"; 
    public const string SE_BACKUP_NAME = "SeBackupPrivilege"; 

    public const int SE_PRIVILEGE_ENABLED = 0x0002; 

    public const int ERROR_NOT_ALL_ASSIGNED = 1300; 

    private const uint TH32CS_SNAPPROCESS = 0x00000002; 

    public static int INVALID_HANDLE_VALUE = -1; 

    [DllImport("advapi32.dll", SetLastError = true)] 
    public static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpname, 
     [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid); 

    [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, int dwCreationFlags, IntPtr lpEnvironment, 
     String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); 

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    public static extern bool DuplicateToken(IntPtr ExistingTokenHandle, 
     int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle); 

    [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] 
    public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess, 
     ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType, 
     int ImpersonationLevel, ref IntPtr DuplicateTokenHandle); 

    [DllImport("advapi32.dll", SetLastError = true)] 
    public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, 
     ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength); 

    [DllImport("advapi32.dll", SetLastError = true)] 
    public static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, 
     ref uint TokenInformation, uint TokenInformationLength); 

    [DllImport("userenv.dll", SetLastError = true)] 
    public static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit); 

    public static bool CreateProcessInConsoleSession(String CommandLine, bool bElevate) 
    { 

     PROCESS_INFORMATION pi; 

     bool bResult = false; 
     uint dwSessionId, winlogonPid = 0; 
     IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero; 

     Debug.Print("CreateProcessInConsoleSession"); 
     // Log the client on to the local computer. 
     dwSessionId = WTSGetActiveConsoleSessionId(); 

     // Find the winlogon process 
     var procEntry = new PROCESSENTRY32(); 

     uint hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 
     if (hSnap == INVALID_HANDLE_VALUE) 
     { 
      return false; 
     } 

     procEntry.dwSize = (uint) Marshal.SizeOf(procEntry); //sizeof(PROCESSENTRY32); 

     if (Process32First(hSnap, ref procEntry) == 0) 
     { 
      return false; 
     } 

     String strCmp = "explorer.exe"; 
     do 
     { 
      if (strCmp.IndexOf(procEntry.szExeFile) == 0) 
      { 
       // We found a winlogon process...make sure it's running in the console session 
       uint winlogonSessId = 0; 
       if (ProcessIdToSessionId(procEntry.th32ProcessID, ref winlogonSessId) && 
        winlogonSessId == dwSessionId) 
       { 
        winlogonPid = procEntry.th32ProcessID; 
        break; 
       } 
      } 
     } 
     while (Process32Next(hSnap, ref procEntry) != 0); 

     //Get the user token used by DuplicateTokenEx 
     WTSQueryUserToken(dwSessionId, ref hUserToken); 

     var si = new STARTUPINFO(); 
     si.cb = Marshal.SizeOf(si); 
     si.lpDesktop = "winsta0\\default"; 
     var tp = new TOKEN_PRIVILEGES(); 
     var luid = new LUID(); 
     hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid); 

     if (
      !OpenProcessToken(hProcess, 
       TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY 
       | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE, ref hPToken)) 
     { 
      Debug.Print(String.Format("CreateProcessInConsoleSession OpenProcessToken error: {0}", 
       Marshal.GetLastWin32Error())); 
     } 

     if (!LookupPrivilegeValue(IntPtr.Zero, SE_DEBUG_NAME, ref luid)) 
     { 
      Debug.Print(String.Format("CreateProcessInConsoleSession LookupPrivilegeValue error: {0}", 
       Marshal.GetLastWin32Error())); 
     } 

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

     if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, 
       (int) SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int) TOKEN_TYPE.TokenPrimary, 
       ref hUserTokenDup)) 
     { 
      Debug.Print(
       String.Format(
        "CreateProcessInConsoleSession DuplicateTokenEx error: {0} Token does not have the privilege.", 
        Marshal.GetLastWin32Error())); 
      CloseHandle(hProcess); 
      CloseHandle(hUserToken); 
      CloseHandle(hPToken); 
      return false; 
     } 

     if (bElevate) 
     { 
      //tp.Privileges[0].Luid = luid; 
      //tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 

      tp.PrivilegeCount = 1; 
      tp.Privileges = new int[3]; 
      tp.Privileges[2] = SE_PRIVILEGE_ENABLED; 
      tp.Privileges[1] = luid.HighPart; 
      tp.Privileges[0] = luid.LowPart; 

      //Adjust Token privilege 
      if (
       !SetTokenInformation(hUserTokenDup, TOKEN_INFORMATION_CLASS.TokenSessionId, ref dwSessionId, 
        (uint) IntPtr.Size)) 
      { 
       Debug.Print(
        String.Format(
         "CreateProcessInConsoleSession SetTokenInformation error: {0} Token does not have the privilege.", 
         Marshal.GetLastWin32Error())); 
       //CloseHandle(hProcess); 
       //CloseHandle(hUserToken); 
       //CloseHandle(hPToken); 
       //CloseHandle(hUserTokenDup); 
       //return false; 
      } 
      if (
       !AdjustTokenPrivileges(hUserTokenDup, false, ref tp, Marshal.SizeOf(tp), /*(PTOKEN_PRIVILEGES)*/ 
        IntPtr.Zero, IntPtr.Zero)) 
      { 
       int nErr = Marshal.GetLastWin32Error(); 

       if (nErr == ERROR_NOT_ALL_ASSIGNED) 
       { 
        Debug.Print(
         String.Format(
          "CreateProcessInConsoleSession AdjustTokenPrivileges error: {0} Token does not have the privilege.", 
          nErr)); 
       } 
       else 
       { 
        Debug.Print(String.Format("CreateProcessInConsoleSession AdjustTokenPrivileges error: {0}", nErr)); 
       } 
      } 
     } 

     uint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE; 
     IntPtr pEnv = IntPtr.Zero; 
     if (CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true)) 
     { 
      dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; 
     } 
     else 
     { 
      pEnv = IntPtr.Zero; 
     } 
     // Launch the process in the client's logon session. 
     bResult = CreateProcessAsUser(hUserTokenDup, // client's access token 
      null, // file to execute 
      CommandLine, // command line 
      ref sa, // pointer to process SECURITY_ATTRIBUTES 
      ref sa, // pointer to thread SECURITY_ATTRIBUTES 
      false, // handles are not inheritable 
      (int) dwCreationFlags, // creation flags 
      pEnv, // pointer to new environment block 
      null, // name of current directory 
      ref si, // pointer to STARTUPINFO structure 
      out pi // receives information about new process 
      ); 
     // End impersonation of client. 

     //GetLastError should be 0 
     int iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error(); 

     //Close handles task 
     CloseHandle(hProcess); 
     CloseHandle(hUserToken); 
     CloseHandle(hUserTokenDup); 
     CloseHandle(hPToken); 

     return (iResultOfCreateProcessAsUser == 0) ? true : false; 
    } 

    [DllImport("kernel32.dll")] 
    private static extern int Process32First(uint hSnapshot, ref PROCESSENTRY32 lppe); 

    [DllImport("kernel32.dll")] 
    private static extern int Process32Next(uint hSnapshot, ref PROCESSENTRY32 lppe); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    private static extern uint CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    private static extern bool CloseHandle(IntPtr hSnapshot); 

    [DllImport("kernel32.dll")] 
    private static extern uint WTSGetActiveConsoleSessionId(); 

    [DllImport("Wtsapi32.dll")] 
    private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken); 

    [DllImport("kernel32.dll")] 
    private static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId); 

    [DllImport("kernel32.dll")] 
    private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); 

    [DllImport("advapi32", SetLastError = true)] 
    [SuppressUnmanagedCodeSecurity] 
    private static extern bool OpenProcessToken(IntPtr ProcessHandle, // handle to process 
     int DesiredAccess, // desired access to process 
     ref IntPtr TokenHandle); 

    #region Nested type: LUID 

    [StructLayout(LayoutKind.Sequential)] 
    internal struct LUID 
    { 
     public int LowPart; 
     public int HighPart; 
    } 

    #endregion 

    //end struct 

    #region Nested type: LUID_AND_ATRIBUTES 

    [StructLayout(LayoutKind.Sequential)] 
    internal struct LUID_AND_ATRIBUTES 
    { 
     public LUID Luid; 
     public int Attributes; 
    } 

    #endregion 

    #region Nested type: PROCESSENTRY32 

    [StructLayout(LayoutKind.Sequential)] 
    private struct PROCESSENTRY32 
    { 
     public uint dwSize; 
     public readonly uint cntUsage; 
     public readonly uint th32ProcessID; 
     public readonly IntPtr th32DefaultHeapID; 
     public readonly uint th32ModuleID; 
     public readonly uint cntThreads; 
     public readonly uint th32ParentProcessID; 
     public readonly int pcPriClassBase; 
     public readonly uint dwFlags; 

     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] 
     public readonly string szExeFile; 
    } 

    #endregion 

    #region Nested type: PROCESS_INFORMATION 

    [StructLayout(LayoutKind.Sequential)] 
    public struct PROCESS_INFORMATION 
    { 
     public IntPtr hProcess; 
     public IntPtr hThread; 
     public uint dwProcessId; 
     public uint dwThreadId; 
    } 

    #endregion 

    #region Nested type: SECURITY_ATTRIBUTES 

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

    #endregion 

    #region Nested type: SECURITY_IMPERSONATION_LEVEL 

    private enum SECURITY_IMPERSONATION_LEVEL 
    { 
     SecurityAnonymous = 0, 
     SecurityIdentification = 1, 
     SecurityImpersonation = 2, 
     SecurityDelegation = 3, 
    } 

    #endregion 

    #region Nested type: STARTUPINFO 

    [StructLayout(LayoutKind.Sequential)] 
    public struct STARTUPINFO 
    { 
     public int cb; 
     public String lpReserved; 
     public String lpDesktop; 
     public String lpTitle; 
     public uint dwX; 
     public uint dwY; 
     public uint dwXSize; 
     public uint dwYSize; 
     public uint dwXCountChars; 
     public uint dwYCountChars; 
     public uint dwFillAttribute; 
     public uint dwFlags; 
     public short wShowWindow; 
     public short cbReserved2; 
     public IntPtr lpReserved2; 
     public IntPtr hStdInput; 
     public IntPtr hStdOutput; 
     public IntPtr hStdError; 
    } 

    #endregion 

    #region Nested type: TOKEN_PRIVILEGES 

    [StructLayout(LayoutKind.Sequential)] 
    internal struct TOKEN_PRIVILEGES 
    { 
     internal int PrivilegeCount; 
     //LUID_AND_ATRIBUTES 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] 
     internal int[] Privileges; 
    } 

    #endregion 

    #region Nested type: TOKEN_TYPE 

    private enum TOKEN_TYPE 
    { 
     TokenPrimary = 1, 
     TokenImpersonation = 2 
    } 

    #endregion 

    // handle to open access token 
} 
+0

Это выглядит тем, что я ищу .. Я прочитаю код, который приятно прокомментирован и выглядит прямо вперед. – user1403598

+1

Интересный пример кода * точно * что [этот пост в блоге команды поддержки SDK Windows] (http://blogs.msdn.com/b/winsdk/archive/2009/07/14/launching-an-interactive-process-from-windows-service-in-windows-vista-and-later.aspx) говорит вы должны «никогда, никогда и никогда не делать»: кража токена доступа процесса проводника на рабочем столе интерактивного пользователя, открытие токена доступа к процессу и создание нового процесса с помощью этого токена. –

+0

Этот код был именно тем, что я искал. Создал новый класс. Выпустил этот код, сделал вызов, и он сработал с первой попытки. Большое спасибо за публикацию этого. – Grayson

9

Как так часто с этими типами вопросов о службах Windows, вы действующие в мышлении операционной системы однопользовательской , Вся причина, по которой вы решили написать свое приложение в качестве службы, заключалась в том, что вы столкнулись с конфликтами между вашей ментальной моделью однопользовательской ОС и реальностью многопользовательской ОС. К сожалению, служба не решила все ваши проблемы, и теперь вы пытаетесь понять, как выполнить второй шаг в конечном итоге обреченном взломанном дизайне.

То есть, вы не можете быть гарантировано, что является «вошедшего пользователя». Если никто не зашел на рабочую станцию, никто не войдет в систему, но ваша служба все равно будет запущена.

Даже если вы каким-то образом миновали этот счет того, что кто-то всегда войти в систему (невозможно), то вы столкнетесь с ситуацией, когда несколько пользователей вошли в систему. Тогда какой из них должен выполнить ваш сервис? Должен ли он просто выбрать один из них случайным образом?

И нужно ли в вашем случае различать пользователей, зарегистрированных локально на консоли, и тех, кто удален удаленно? Помните, что у удаленных пользователей не будет локальной консоли.

Если вы могли бы как-то преодолеть все эти препятствия (к сожалению, возможно, похоронив голову на песке и продолжая притворяться, что Windows является однопользовательской ОС), вы можете использовать функцию WTSGetActiveConsoleSessionId для получения текущий идентификатор сеанса, функцию WTSQueryUserToken для получения токена пользователя, соответствующего этому идентификатору сеанса, а затем, наконец, функцию CreateProcessAsUser, чтобы запустить ваш процесс в контексте этого пользователя. Если есть. И у них есть соответствующие привилегии. И физическая консоль не привязана к фиктивной сессии. И вы не используете серверный SKU, который позволяет несколько активных сеансов консоли. И & hellip;

Если вы можете выбрать конкретного пользователя, чью учетную запись вы хотите использовать для запуска вспомогательного процесса, вы можете войти в систему с этим пользователем, управлять своим токеном пользователя, выполнить процесс и, наконец, закрыть процесс и выйти из системы , CreateProcessWithLogonUser function обертывает много этой тяжелой работы для вас, делая код намного более стройным. Но внешность может быть обманчивой, и это все еще имеет некоторые последствия безопасности, которые вы, вероятно, не совсем понимаете, если задаете этот вопрос в первую очередь. И вы действительно не можете позволить себе не понимать риски безопасности, подобные этому.

Кроме того, пользователи, которые вошли в систему с помощью LogonUser (что делается автоматически при использовании функции CreateProcessWithLogonUser), нет оконной станции и рабочего стола, на котором они могут запускать интерактивные процессы. Поэтому, если процесс, который вы хотите запустить в контексте этого пользователя, отобразит любой пользовательский интерфейс, вам не повезло. Windows убьет ваше приложение, как только оно попытается получить доступ к рабочему столу, для которого ему не требуются требуемые разрешения. От службы Windows нет способа получить дескриптор рабочего стола, который будет вам полезен (что в значительной степени объясняет общее правило, которое вы, вероятно, уже знаете, что службы не могут отображать какой-либо пользовательский интерфейс).

+0

Мне очень понравилось «в конечном счете обречено». Но 'CreateProcessWithLogonW' не поможет ему, так как у него нет учетных данных пользователя. –

+0

Служба Windows изначально была предназначена для приема входящих сообщений и обработки их по запросу (использование системы сообщений из удаленного приложения в службу). Служба работает как преднамеренно, но теперь требуется дополнительная спецификация. Я уверен, что некоторые Api должны быть вызваны в качестве текущего пользователя и, следовательно, причина, по которой теперь это требование. Я понимаю риски безопасности, но это вызовет только пару API, которые определены для пользователей CU/Active. Я могу, и служба гарантирует, что на компьютере будет зарегистрирован пользователь, если он не отчитается. – user1403598

+0

Что он собирается «сообщать»? Пользователь не зарегистрирован! И каковы эти API, которые нужно вызвать? Почему они требуют вызова как конкретного пользователя? Похоже, в этом и есть реальное решение вашей проблемы. –

Смежные вопросы