2012-02-01 17 views
12

मैं एक विंडोज सेवा (से CreateProcessAsUser का उपयोग कर रहा हूं, कृपया हम विषय पर रह सकते हैं और मान लें कि मेरे पास ऐसा करने का बहुत अच्छा कारण है)। हर कोई जो यहां पूछ रहा है उसके विपरीत मुझे सेवा (सत्र 0) के समान सत्र के बजाय मेरे सक्रिय टर्मिनल सत्र (सत्र 1) में एक विंडो मिल रही है - जो अवांछनीय है।CreateProcessAsUser सक्रिय सत्र में विंडो बनाना

मैंने Scott Allen's code को विनियमित किया; और निम्नलिखित के साथ आया था। उल्लेखनीय परिवर्तन "स्वयं को वापस", "CREATE_NO_WINDOW" और कमांड लाइन का समर्थन करते हैं। और मैं एक कार्यकर्ता प्रक्रिया के रूप में एक सांत्वना आवेदन बूटिंग रहा हूँ -

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

अब जब कि सेवा 'डोमेन \ MyService' के तहत चल रहा है और मैं वर्तमान के रूप में 'डोमेन \ प्रशासक' में लॉग इन कर रहा हूँ मान। जब मैं सेवा का उपयोग करने के लिए एक क्लाइंट अनुप्रयोग का उपयोग करें (सेवा कंसोल मोड यानी यह सत्र 0 में है में शुरू नहीं किया गया है) और विधि है कि CreateProcessAsUser वर्कर प्रोसेस मेरे डेस्कटॉप पर दिखाई देता है invokes निष्पादित।

अब मैं इसे विंडोज़ एप्लिकेशन बना सकता हूं जिसमें कंसोल विंडो के निर्माण के लिए कोई खिड़की नहीं है; हालांकि, दिन के अंत में यह अभी भी सत्र 1 में बनाया जा रहा है।

कोई विचार क्यों सेवा के समान सत्र में कंसोल एप्लिकेशन नहीं बनाया जा रहा है?

+0

ऐसा लगता है कि यह [इस काले जादू] (http://alex-ionescu.com/?p=60) के साथ कुछ हो सकता है, लेकिन मैं समझ नहीं है कि यह कैसे छोड़। –

+0

डेस्कटॉप के रूप में "सेवा -0 × 0-3e7 $ \ डिफ़ॉल्ट" का उपयोग करने का प्रयास किया - जो एप्लिकेशन को क्रैश करने का कारण बनता है। –

+0

विंडोज का कौन सा संस्करण? क्या आपने lpDeskTop को शून्य पर छोड़ने का प्रयास किया है? –

उत्तर

6

आप शायद पहले से ही जानते हैं, सत्र 0 के अलगाव सुरक्षा कारणों के लिए है और आप यहां http://msdn.microsoft.com/en-us/windows/hardware/gg463353.aspx

क्यों अपने कंसोल अनुप्रयोग सक्रिय सत्र (जैसे सत्र 1 में बनाया गया है के संबंध में इसके बारे में अधिक पढ़ सकते हैं), यह वास्तव में सीधे आपके उपयोगकर्ता टोकन से जुड़ा हुआ है। जब आप वर्तमान उपयोगकर्ता टोकन के लिए पूछते हैं, तो यह टोकन स्वचालित रूप से सत्र आईडी जानकारी के साथ ले जाता है - इस मामले में यह लॉगिन टर्मिनल सेवा सत्र (सत्र 1) है। यह सत्र आईडी टोकन द्वारा संदर्भित है जिसे डुप्लिकेट टोकनएक्स में दोहराया जाता है और फिर CreateProcessAsUser कॉल में उपयोग किया जाता है। आदेश सत्र 0 में अपने कंसोल आवेदन के निर्माण के लिए मजबूर करने के लिए, आप की तरह नीचे

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

यहाँ CreateProcessAsUser कॉल करने से पहले SetTokenInformation एपीआई (advapi32.dll), आपके hDupedToken में पारित करने के लिए एक स्पष्ट कॉल करने के लिए की आवश्यकता होगी SetTokenInformation http://msdn.microsoft.com/en-us/library/windows/desktop/aa379591(v=vs.85).aspx

+0

मैंने सोचा कि यह ऐसा कुछ बेवकूफ हो सकता है। मैं इसे एक शॉट देने जा रहा हूँ। –

+0

सहायता के लिए बहुत धन्यवाद - मुझे केवल 'SE_TCB_NAME' दाएं (' SetTokenInformation' के लिए आवश्यक) प्राप्त करने में समस्याएं आ रही हैं। मैंने अपना सेवा खाता "ऑपरेटिंग सिस्टम के हिस्से के रूप में कार्य" सही (साथ ही शॉटगन बीमा के लिए सेवा) दिया है। मैंने 'OpenThreadToken' का उपयोग' TOKEN_ALL_ACCESS' के साथ करने का प्रयास किया है - इसका कोई फायदा नहीं हुआ। आपने +250 अर्जित किया है - मुझे उम्मीद है कि इस संबंध में मेरी मदद करने के लिए आपको थोड़ा और विशेषज्ञता होगी। –

+0

आपको बक्षीस मिलती है। एक और बक्षीस शुरू करने का समय;)। –

0

MarshalAs, StructLayout, और DllImport के पैरासेट नामक पैरासेट के साथ गड़बड़ करने का प्रयास करें। ऐसा करने के लिए आपको MarshalAs को विभिन्न तारों में जोड़ने की आवश्यकता हो सकती है। यूनिकोड से परेशान न करें: आप इसका उपयोग नहीं कर रहे हैं। मैं उन्हें सभी को CharSet.Ansi पर सेट करने की सलाह देता हूं। उन सभी परीक्षणों को चलाएं जिन्हें आपने पहले ही कोशिश की है - यानी, डेस्कटॉप और सभी मजेदार सामान सेट करना। यदि यह दुर्घटनाग्रस्त हो जाता है, तो उन्हें सभी को ऑटो पर स्विच करें। यदि यह अभी भी काम नहीं करता है, तो उन्हें हटा दें।

इस काम में से कोई भी मानते हुए, CreateUserProcessW और CharSet.Unicode पर स्विच करें ताकि आप जान सकें कि आप क्या प्राप्त कर रहे हैं। दूसरे विचार पर, बस इस कदम पर छोड़ दें।

आप स्ट्रिंग्स के लिए MarshalAs साथ UnmanagedType निर्धारित करने की आवश्यकता है, तो आप Ansi के लिए UnmanagedType.LPStr, ऑटो के लिए UnmanagedType.LPTStr, और यूनिकोड के लिए UnmanagedType.LPWStr चाहते हैं। असल में, वैसे भी अपने सभी तारों के लिए यह करो।

+0

मुझे कभी भी इन कार्यों के लिए CharSet सेट नहीं करना पड़ा जब तक वे ए या डब्ल्यू वेरिएंट नहीं थे, इसलिए मैं अपने बेटों को ऑटो पर डाल रहा हूं। – Zenexer

+0

धन्यवाद - मैं इसे आज रात जाने दूंगा और रिपोर्ट करूंगा। –

+0

शुभकामनाएँ! यदि आप महत्वाकांक्षी महसूस कर रहे हैं, तो सीधे यूनिकोड पर जाएं। – Zenexer

1

पर प्रारंभिक पोस्ट को मेरे अंत में एक समाधान समाधान के रूप में लागू करने में सक्षम था, हालांकि, मुझे अपने कंसोल विंडो को छिपाने का कोई तरीका नहीं दिख रहा है।मैंने STARTF_USESHOWWINDOW और SW_HIDE की कोशिश की लेकिन मेरी कमांड विंडो अभी भी पॉप अप हुई। कोई विचार क्यों?

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); 
     } 
    } 
संबंधित मुद्दे