سؤال

لقد كنت أحاول إنشاء عملية جديدة ضمن سياق مستخدم معين باستخدام ملف CreateProcessAsUser وظيفة Windows API، ولكن يبدو أنها تواجه مشكلة أمنية سيئة إلى حد ما ...

قبل أن أشرح أكثر، إليك الكود الذي أستخدمه حاليًا لبدء العملية الجديدة (عملية وحدة التحكم - PowerShell على وجه التحديد، على الرغم من أنه لا يهم).

    private void StartProcess()
    {
        bool retValue;

        // Create startup info for new console process.
        var startupInfo = new STARTUPINFO();
        startupInfo.cb = Marshal.SizeOf(startupInfo);
        startupInfo.dwFlags = StartFlags.STARTF_USESHOWWINDOW;
        startupInfo.wShowWindow = _consoleVisible ? WindowShowStyle.Show : WindowShowStyle.Hide;
        startupInfo.lpTitle = this.ConsoleTitle ?? "Console";

        var procAttrs = new SECURITY_ATTRIBUTES();
        var threadAttrs = new SECURITY_ATTRIBUTES();
        procAttrs.nLength = Marshal.SizeOf(procAttrs);
        threadAttrs.nLength = Marshal.SizeOf(threadAttrs);

        // Log on user temporarily in order to start console process in its security context.
        var hUserToken = IntPtr.Zero;
        var hUserTokenDuplicate = IntPtr.Zero;
        var pEnvironmentBlock = IntPtr.Zero;
        var pNewEnvironmentBlock = IntPtr.Zero;

        if (!WinApi.LogonUser("UserName", null, "Password",
            LogonType.Interactive, LogonProvider.Default, out hUserToken))
            throw new Win32Exception(Marshal.GetLastWin32Error(), "Error logging on user.");

        var duplicateTokenAttrs = new SECURITY_ATTRIBUTES();
        duplicateTokenAttrs.nLength = Marshal.SizeOf(duplicateTokenAttrs);
        if (!WinApi.DuplicateTokenEx(hUserToken, 0, ref duplicateTokenAttrs,
            SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary,
            out hUserTokenDuplicate))
            throw new Win32Exception(Marshal.GetLastWin32Error(), "Error duplicating user token.");

        try
        {
            // Get block of environment vars for logged on user.
            if (!WinApi.CreateEnvironmentBlock(out pEnvironmentBlock, hUserToken, false))
                throw new Win32Exception(Marshal.GetLastWin32Error(),
                    "Error getting block of environment variables for user.");

            // Read block as array of strings, one per variable.
            var envVars = ReadEnvironmentVariables(pEnvironmentBlock);

            // Append custom environment variables to list.
            foreach (var var in this.EnvironmentVariables)
                envVars.Add(var.Key + "=" + var.Value);

            // Recreate environment block from array of variables.
            var newEnvironmentBlock = string.Join("\0", envVars.ToArray()) + "\0";
            pNewEnvironmentBlock = Marshal.StringToHGlobalUni(newEnvironmentBlock);

            // Start new console process.
            retValue = WinApi.CreateProcessAsUser(hUserTokenDuplicate, null, this.CommandLine,
                ref procAttrs, ref threadAttrs, false, CreationFlags.CREATE_NEW_CONSOLE |
                CreationFlags.CREATE_SUSPENDED | CreationFlags.CREATE_UNICODE_ENVIRONMENT,
                pNewEnvironmentBlock, null, ref startupInfo, out _processInfo);
            if (!retValue) throw new Win32Exception(Marshal.GetLastWin32Error(),
                "Unable to create new console process.");
        }
        catch
        {
            // Catch any exception thrown here so as to prevent any malicious program operating
            // within the security context of the logged in user.

            // Clean up.
            if (hUserToken != IntPtr.Zero)
            {
                WinApi.CloseHandle(hUserToken);
                hUserToken = IntPtr.Zero;
            }

            if (hUserTokenDuplicate != IntPtr.Zero)
            {
                WinApi.CloseHandle(hUserTokenDuplicate);
                hUserTokenDuplicate = IntPtr.Zero;
            }

            if (pEnvironmentBlock != IntPtr.Zero)
            {
                WinApi.DestroyEnvironmentBlock(pEnvironmentBlock);
                pEnvironmentBlock = IntPtr.Zero;
            }

            if (pNewEnvironmentBlock != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(pNewEnvironmentBlock);
                pNewEnvironmentBlock = IntPtr.Zero;
            }

            throw;
        }
        finally
        {
            // Clean up.
            if (hUserToken != IntPtr.Zero)
                WinApi.CloseHandle(hUserToken);

            if (hUserTokenDuplicate != IntPtr.Zero)
                WinApi.CloseHandle(hUserTokenDuplicate);

            if (pEnvironmentBlock != IntPtr.Zero)
                WinApi.DestroyEnvironmentBlock(pEnvironmentBlock);

            if (pNewEnvironmentBlock != IntPtr.Zero)
                Marshal.FreeHGlobal(pNewEnvironmentBlock);
        }

        _process = Process.GetProcessById(_processInfo.dwProcessId);
    }

من أجل المشكلة هنا، تجاهل الكود الذي يتعامل مع متغيرات البيئة (لقد اختبرت هذا القسم بشكل مستقل ويبدو أنه يعمل.)

الآن، الخطأ الذي أحصل عليه هو ما يلي (تم طرحه على السطر بعد الاتصال بـ CreateProcessAsUSer):

"الامتياز المطلوب لا يحتفظ به العميل" (رمز الخطأ 1314)

(تم اكتشاف رسالة الخطأ عن طريق إزالة معلمة الرسالة من مُنشئ Win32Exception.من المسلم به أن خطأي في التعامل مع التعليمات البرمجية هنا قد لا يكون الأفضل، ولكن هذا أمر غير ذي صلة إلى حد ما.ومع ذلك، فنحن نرحب بك للتعليق عليه إذا كنت ترغب في ذلك.) أنا حقًا في حيرة من أمري بشأن سبب هذا الخطأ الغامض في هذه الحالة.لم تقدم لي وثائق MSDN وسلاسل المنتديات المختلفة سوى الكثير من النصائح، وخاصة بالنظر إلى أن أسباب مثل هذه الأخطاء تبدو متنوعة على نطاق واسع، ليس لدي أي فكرة عن قسم التعليمات البرمجية الذي أحتاج إلى تعديله.ربما يكون ذلك مجرد معلمة واحدة أحتاج إلى تغييرها، ولكن من الممكن أن أقوم باستدعاءات WinAPI الخاطئة/غير الكافية لكل ما أعرفه.ما يحيرني كثيرًا هو أن الإصدار السابق من الكود يستخدم الملف العادي CreateProcess تعمل الوظيفة (المكافئة باستثناء معلمة الرمز المميز للمستخدم) بشكل جيد تمامًا.كما أفهم، من الضروري فقط استدعاء وظيفة مستخدم تسجيل الدخول لتلقي مقبض الرمز المميز المناسب ثم تكراره بحيث يمكن تمريره إلى CreateProcessAsUser.

أي اقتراحات لإجراء تعديلات على الكود بالإضافة إلى التوضيحات ستكون موضع ترحيب كبير.

ملحوظات

لقد كنت أشير في المقام الأول إلى مستندات MSDN (بالإضافة إلى PInvoc.net لإعلانات وظيفة/تبختر/تعداد C#).ويبدو أن الصفحات التالية على وجه الخصوص تحتوي على الكثير من المعلومات في أقسام الملاحظات، وقد يكون بعضها مهمًا ويراوغني:

يحرر

لقد جربت للتو اقتراح ميتش، ولكن لسوء الحظ تم استبدال الخطأ القديم بخطأ جديد:"لا يمكن للنظام العثور على الملف المحدد." (رمز الخطأ 2)

النداء السابق ل CreateProcessAsUser تم استبداله بما يلي:

retValue = WinApi.CreateProcessWithTokenW(hUserToken, LogonFlags.WithProfile, null,
    this.CommandLine, CreationFlags.CREATE_NEW_CONSOLE |
    CreationFlags.CREATE_SUSPENDED | CreationFlags.CREATE_UNICODE_ENVIRONMENT,
    pNewEnvironmentBlock, null, ref startupInfo, out _processInfo);

لاحظ أن هذا الرمز لم يعد يستخدم الرمز المميز المكرر بل الأصلي، كما يبدو أن مستندات MSDN تقترح.

وهنا محاولة أخرى باستخدام CreateProcessWithLogonW.الخطأ هذه المرة هو "فشل تسجيل الدخول:اسم مستخدم غير معروف أو كلمة مرور سيئة" (رمز الخطأ 1326)

retValue = WinApi.CreateProcessWithLogonW("Alex", null, "password",
    LogonFlags.WithProfile, null, this.CommandLine,
    CreationFlags.CREATE_NEW_CONSOLE | CreationFlags.CREATE_SUSPENDED |
    CreationFlags.CREATE_UNICODE_ENVIRONMENT, pNewEnvironmentBlock,
    null, ref startupInfo, out _processInfo);

لقد حاولت أيضًا تحديد اسم المستخدم بتنسيق UPN ("Alex@Alex-PC") وتمرير المجال بشكل مستقل كوسيطة ثانية، وكل ذلك دون جدوى (خطأ مماثل).

هل كانت مفيدة؟

المحلول

اه...يبدو أنني قد فاجأتني واحدة من أكبر المشاكل في برمجة التشغيل المتداخل WinAPI.أيضًا، قد يكون نشر الكود الخاص بإعلانات الوظائف الخاصة بي فكرة حكيمة في هذه الحالة.

على أي حال، كل ما كنت بحاجة إلى القيام به هو إضافة وسيطة إلى سمة DllImport الخاصة بالوظيفة المحددة CharSet = CharSet.Unicode.لقد فعلت هذه الحيلة لكلا CreateProcessWithLogonW و CreateProcessWithTokenW المهام.أعتقد أنه أخيرًا لفت انتباهي أن لاحقة W لأسماء الوظائف تشير إلى Unicode وأنني بحاجة إلى تحديد ذلك بشكل صريح في C#!هنا صحيح إعلانات الوظائف في حالة اهتمام أي شخص:

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessWithLogonW(string principal, string authority,
    string password, LogonFlags logonFlags, string appName, string cmdLine,
    CreationFlags creationFlags, IntPtr environmentBlock, string currentDirectory,
    ref STARTUPINFO startupInfo, out PROCESS_INFORMATION processInfo);

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessWithTokenW(IntPtr hToken, LogonFlags dwLogonFlags,
    string lpApplicationName, string lpCommandLine, CreationFlags dwCreationFlags,
    IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo,
    out PROCESS_INFORMATION lpProcessInformation);

نصائح أخرى

من هنا:

عادة ، يجب أن يكون للعملية التي تستدعي وظيفة CreateProcessasuser امتيازات se_assignprimarytoken_name و se_increase_quota_name.إذا فشلت هذه الوظيفة باستخدام error_privilege_not_held (1314) ، فاستخدم وظيفة createProcessWithLogonw بدلاً من ذلك.لا يتطلب CreateProcessWithLogonw أي امتيازات خاصة ، ولكن يجب السماح لحساب المستخدم المحدد بتسجيل الدخول بشكل تفاعلي.بشكل عام ، من الأفضل استخدام CreateProcessWithLogonw لإنشاء عملية مع بيانات اعتماد بديلة.

انظر هذا بلوق وظيفة كيفية الاتصال بـ CreateProcessWithLogonW وCreateProcessAsUser في .NET

قدم Jonathan Peppers هذه القطعة الرائعة من الرمز الذي حدد مشكلاتي

http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/0c0ca087-5e7b-4046-93cb-c7b3e48d0dfb?ppud=4

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top