3

現在アクティブなユーザーのアプリケーションを起動するために、WindowsサービスからCreateProcessAsUserを使用しています。これまでのところ、ローカルドライブ上のアプリケーションでうまく機能します。

ただし、実行可能ファイルがネットワーク共有に存在する場合、完全なサーバー名(\ 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

これが完全なサンプルコードです、私はそれが役に立つかもしれないと思います:

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

これは、次のようなローカル実行可能ファイルでうまく機能します。

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

私の質問:重複トークンを使用してネットワーク共有に適切にアクセスするには、何を調整する必要がありますか?

4

1 に答える 1

2

ゲストアクセスを許可する(つまり、ユーザー名/パスワードがない)共有に対してこれを使用すると、コマンドは正しく機能しますが、使用するために認証が必要な共有に対してこれを使用すると、機能しません。

UIを呼び出すと、リダイレクタが関与します。リダイレクタは、実行に必要なリモートサーバーへの接続を自動的に確立します。

回避策はありますが、実際の解決策は、cmdベースのリレーを使用して実行可能ファイルにアクセスすることです。したがって、コマンドラインでは次のようにします。

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

次に、以下を使用してstartupinfoをウィンドウに変更SW_HIDEします。cmd

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

呼び出しは、コマンドを開始する前にユーザーの環境に完全に入るためのcmdちょっとしたシムです-これは、サーバーにアクセスするためのすべての資格情報を利用します。

念のために言っておきますが、直接呼び出されるアプリケーションを防ぐために、おそらく少しロジックが必要になりSW_HIDEます(たとえば、commandLine文字列の先頭でcmdを確認しますか?)

于 2013-01-24T14:10:19.063 に答える