2013-01-22 1 views
2

Windows 서비스에서 CreateProcessAsUser를 사용하여 현재 활성 사용자에 대한 응용 프로그램을 시작합니다. 지금까지 로컬 드라이브의 응용 프로그램과 잘 작동합니다.CreateProcessAsUser : "5 : Access Denied"네트워크 공유에 액세스하려고 시도했습니다.

그러나 실행 파일이 네트워크 공유에있는 경우 전체 서버 이름 (\ myserver \ path \ app.exe)을 사용하면 5 : ERROR_ACCESS_DENIED가 생성됩니다. 매핑 된 드라이브 (P : \ path \ app.exe)를 대신 사용하면 2 : ERROR_FILE_NOT_FOUND도 생성 할 수 있습니다.

탐색기에서 응용 프로그램을 실행할 수 있습니다. 서비스가 제대로 서버에서 나를 사칭하지 못하여 적절한 토큰 복제본을 얻을 수없는 것 같습니다.

다양한 게시물에서 CreateProcessAsUser의 여러 구현을 시도해 보았습니다. 이것은 나를 위해 새로운 (환각) 물건입니다, 그리고 솔직히, 내가 .NET으로 돌아가는 데 기다릴 수 없어 :) 내가 잘못된 행을 추측하는 것은 주위 여기에 있습니다 :

DuplicateTokenEx(
    hUserToken, 
    (Int32)MAXIMUM_ALLOWED, 
    ref sa, 
    (Int32)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, 
    (Int32)TOKEN_TYPE.TokenPrimary, 
    ref hUserTokenDup); 

CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true); 

Int32 dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT; 

PROCESS_INFORMATION pi; 
STARTUPINFO si = new STARTUPINFO(); 
si.cb = Marshal.SizeOf(si); 
si.lpDesktop = "winsta0\\default"; 

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 
    dwCreationFlags,     // creation flags 
    pEnv,        // pointer to new environment block 
    workingDirectory,     // name of current directory 
    ref si,       // pointer to STARTUPINFO structure 
    out pi);       // receives information about new process 

여기에 전체 샘플 코드, 난 그것은 유용 할 수 있습니다 같아요 :

StringBuilder result = new StringBuilder(); 
Win32API.CreateProcessAsUser(@"C:\Windows\notepad.exe", @"C:\Windows\", out result); 

내 질문 : 무엇을 위해 조정해야

using System; 
using System.Text; 
using System.Security; 
using System.Management; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 

namespace Win32 
{ 
    public class Win32API 
    { 
     [StructLayout(LayoutKind.Sequential)] 
     struct SECURITY_ATTRIBUTES 
     { 
      public Int32 Length; 
      public IntPtr lpSecurityDescriptor; 
      public Boolean bInheritHandle; 
     } 

     enum TOKEN_TYPE 
     { 
      TokenPrimary = 1, 
      TokenImpersonation = 2 
     } 

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

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

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

     const UInt32 MAXIMUM_ALLOWED = 0x2000000; 
     const Int32 CREATE_UNICODE_ENVIRONMENT = 0x00000400; 
     const Int32 NORMAL_PRIORITY_CLASS = 0x20; 
     const Int32 CREATE_NEW_CONSOLE = 0x00000010; 

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

     [DllImport("kernel32.dll")] 
     public static extern UInt32 WTSGetActiveConsoleSessionId(); 

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

     [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] 
     extern static Boolean CreateProcessAsUser(
      IntPtr hToken, 
      String lpApplicationName, 
      String lpCommandLine, 
      ref SECURITY_ATTRIBUTES lpProcessAttributes, 
      ref SECURITY_ATTRIBUTES lpThreadAttributes, 
      Boolean bInheritHandle, 
      Int32 dwCreationFlags, 
      IntPtr lpEnvironment, 
      String lpCurrentDirectory, 
      ref STARTUPINFO lpStartupInfo, 
      out PROCESS_INFORMATION lpProcessInformation); 

     [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] 
     extern static Boolean DuplicateTokenEx(
      IntPtr ExistingTokenHandle, 
      UInt32 dwDesiredAccess, 
      ref SECURITY_ATTRIBUTES lpThreadAttributes, 
      Int32 TokenType, 
      Int32 ImpersonationLevel, 
      ref IntPtr DuplicateTokenHandle); 

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

     [DllImport("userenv.dll", SetLastError = true)] 
     static extern Boolean DestroyEnvironmentBlock(IntPtr lpEnvironment); 

     /// <summary> 
     /// Creates the process in the interactive desktop with credentials of the logged in user. 
     /// </summary> 
     public static Boolean CreateProcessAsUser(String commandLine, String workingDirectory, out StringBuilder output) 
     { 
      Boolean processStarted = false; 
      output = new StringBuilder(); 

      try 
      { 
       UInt32 dwSessionId = WTSGetActiveConsoleSessionId(); 
       output.AppendLine(String.Format("Active console session Id: {0}", dwSessionId)); 

       IntPtr hUserToken = IntPtr.Zero; 
       WTSQueryUserToken(dwSessionId, ref hUserToken); 

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

       IntPtr hUserTokenDup = IntPtr.Zero; 
       DuplicateTokenEx(
        hUserToken, 
        (Int32)MAXIMUM_ALLOWED, 
        ref sa, 
        (Int32)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, 
        (Int32)TOKEN_TYPE.TokenPrimary, 
        ref hUserTokenDup); 


       if (hUserTokenDup != IntPtr.Zero) 
       { 
        output.AppendLine(String.Format("DuplicateTokenEx() OK (hToken: {0})", hUserTokenDup)); 

        Int32 dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE; 

        IntPtr pEnv = IntPtr.Zero; 
        if (CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true)) 
        { 
         dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; 
         output.AppendLine(String.Format("CreateEnvironmentBlock() success.")); 
        } 
        else 
        { 
         output.AppendLine(String.Format("CreateEnvironmentBlock() FAILED (Last Error: {0})", Marshal.GetLastWin32Error())); 
         pEnv = IntPtr.Zero; 
        } 

        // Launch the process in the client's logon session. 
        PROCESS_INFORMATION pi; 

        STARTUPINFO si = new STARTUPINFO(); 
        si.cb = Marshal.SizeOf(si); 
        si.lpDesktop = "winsta0\\default"; 

        output.AppendLine(String.Format("CreateProcess (Path:{0}, CurrDir:{1})", commandLine, workingDirectory)); 

        if (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 
          dwCreationFlags, // creation flags 
          pEnv,    // pointer to new environment block 
          workingDirectory, // name of current directory 
          ref si,    // pointer to STARTUPINFO structure 
          out pi    // receives information about new process 
         )) 
        { 
         processStarted = true; 
         output.AppendLine(String.Format("CreateProcessAsUser() OK (PID: {0})", pi.dwProcessId)); 
        } 
        else 
        { 
         output.AppendLine(String.Format("CreateProcessAsUser() failed (Last Error: {0})", Marshal.GetLastWin32Error())); 
        } 

        if (DestroyEnvironmentBlock(pEnv)) 
        { 
         output.AppendLine("DestroyEnvironmentBlock: Success"); 
        } 
        else 
        { 
         output.AppendLine(String.Format("DestroyEnvironmentBlock() failed (Last Error: {0})", Marshal.GetLastWin32Error())); 
        } 
       } 
       else 
       { 
        output.AppendLine(String.Format("DuplicateTokenEx() failed (Last Error: {0})", Marshal.GetLastWin32Error())); 
       } 
       CloseHandle(hUserTokenDup); 
       CloseHandle(hUserToken); 
      } 
      catch (Exception ex) 
      { 
       output.AppendLine("Exception occurred: " + ex.Message); 
      } 
      return processStarted; 
     } 
    } 
} 

그것은이 같은 지역의 실행 파일과 함께 좋은 작품 중복 토큰을 사용하여 네트워크 공유에 올바르게 액세스합니까?

+1

이하여 DuplicateTokenEx의 반환 값을 확인하고 CreateProcessAsUser를 사용하여 실패한 이유에 대한 아이디어를 얻지 못하면 마지막 오류를 가져옵니다. –

+0

@ 500-InternalServerError - DuplicateTokenEx는 정상적으로 작동하지만 CreateProcessAsUser는 5 : ERROR_ACCESS_DENIED를 제공합니다. 실제 사용자를 모방하기 위해 내가 바꿀 수있는 옵션에 대한 제안이 있습니까? – Joe

+1

이것이 작동하는 것은 간단하지 않습니다. [이 질문] (http://stackoverflow.com/questions/8081429/createprocessasuser-doesnt-work-when-change-user) 몇 가지 단서를 제공 할 수 있습니다. –

답변

1

게스트 액세스를 허용하는 공유 (사용자 이름/암호 없음)에 대해이 옵션을 사용하면 명령이 올바르게 작동하지만 사용하려면 인증이 필요한 공유에 대해 사용할 때 명령이 작동하지 않습니다.

UI 호출은 관련 리다이렉터를 가져와 실행에 필요한 원격 서버에 자동으로 연결합니다.

CreateProcessAsUser(@"cmd /c ""start \\server\share\binary.exe""", @"C:\Windows", out result); 

는 그 다음 STARTUPINFO을 변경

해결 방법, 마음 당신,하지만 진짜 해결책은 당신이 뭔가를 같이 할 명령 라인, 그래서 실행에 도착하는 cmd 기반 릴레이를 사용하는 것입니다 SW_HIDEcmd 창을 사용하여 :

si.cb = Marshal.SizeOf(si); 
si.lpDesktop = "winsta0\\default"; 
si.dwFlags = 0x1; // STARTF_USESHOWWINDOW 
si.wShowWindow = 0; // SW_HIDE 

cmd 호출이 심의 비트 명령을 시작하기 전에 사용자의 환경에 완전히 얻을 수 있습니다 -이 모든 생명체를 활용한다 서버에 액세스하기위한 edentials.

마음 당신은, 당신은 아마 (예 : 명령 줄 문자열의 시작에서 cmd를 확인?) 직접 호출 응용 프로그램에 대한 SW_HIDE을 방지하기 위해 논리의 조금을해야합니다

+0

감사합니다 Petesh, "cmd"해결 방법이 트릭을 만들었습니다! 그러나 따옴표 구문 CreateProcessAsUser() 동안 내 마지막 작동하지 않았다. lpApplicationName 및 lpCurrentDirectory를 null로 설정하고 lpCommandName을 "cmd/c \\\\ myserver \\ path \\ myapp.exe"로 포맷하면 "시작"과 따옴표가 모두 제거됩니다. – Joe

+1

그래, 인용문은 옳다는 것은 까다 롭습니다. '시작'의 이유는 원격 응용 프로그램이 시작된 후 쉘을 종료하는 것입니다. – Petesh

+0

알기. 도움을 주시면 감사하겠습니다. 나는 공휴일 전부터이 문제에 갇혀 있었다 : P – Joe