Aufruf CreateProcessAsUser aus C#

Ich habe versucht, erstellen Sie einen neuen Prozess unter dem Kontext eines bestimmten Benutzers mithilfe der CreateProcessAsUser Funktion der Windows API, aber scheint zu sein, läuft in eine ziemlich unangenehme Frage der Sicherheit...

Bevor ich nicht weiter erklären, hier ist der code, den ich verwende, um zu starten den neuen Prozess (eine Konsole Prozess - PowerShell-um genau zu sein, aber es sollte keine Rolle spielen).

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

Wegen der Frage hier, ignorieren Sie den code der Umgang mit den Umgebungsvariablen (die ich getestet habe, dass der Abschnitt selbständig und es scheint zu funktionieren.)

Nun, der Fehler den ich bekomme, ist folgende (geworfen auf die Zeile nach dem Aufruf CreateProcessAsUSer):

"Ein erforderliches Recht nicht durch den AUFTRAGGEBER" (error code 1314)

(Die Fehlermeldung wurde entdeckt, durch das entfernen der Nachricht parameter aus der Win32Exception Konstruktor. Zugegeben, mein Fehler-handling-code hier vielleicht nicht die beste, aber das ist eine etwas irrelevante Frage. Sie sind herzlich willkommen zu kommentieren, wenn Sie möchten, jedoch.) Ich bin wirklich ziemlich verwirrt, als die Ursache dieses vagen Fehler in dieser situation. MSDN-Dokumentation und verschiedenen Forums-threads haben nur dann gegeben, mir so viel Beratung, und vor allem, dass die Ursachen für solche Fehler zu sein scheinen vielfältig, ich habe keine Ahnung, welchen Teil des Codes ich ändern muss. Vielleicht ist es einfach nur einen einzigen parameter, die ich ändern müssen, aber ich konnte machen, die falsch/nicht genug WinAPI-Aufrufe für alle, die ich kenne. Was mich verwirrt sehr, dass die bisherige version des Codes verwendet, die die einfachen CreateProcess - Funktion (äquivalent mit Ausnahme der Benutzer-parameter "token") funktionierte perfekt in Ordnung. So wie ich das verstehe, ist es lediglich erforderlich, rufen Sie den Anmelde-Benutzer-Funktion zu erhalten, das entsprechende token Griff und dann duplizieren Sie es so, dass es übergeben werden kann, um CreateProcessAsUser.

Irgendwelche Vorschläge für änderungen des Kodex sowie Erläuterungen wäre sehr willkommen.

Hinweise

Ich habe in Erster Linie unter Bezugnahme auf die MSDN-Doku (sowie PInvoke.net für die C# - Funktion/Strebe/enum-Deklarationen). Auf den folgenden Seiten in allem zu haben scheinen, eine Menge von Informationen in den Erläuterungen der Abschnitte, von denen einige eventuell wichtig sein und entzieht mir:

Bearbeiten

Ich habe einfach versucht, aus Mitch ' s Vorschlag, aber leider ist der alte Fehler wurde gerade durch eine neue ersetzt: "Das system kann nicht die angegebene Datei gefunden werden." (Fehlercode 2)

Vorherigen Aufruf CreateProcessAsUser wurde durch das folgende ersetzt:

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

Beachten Sie, dass dieser code nicht mehr verwendet die doppelte token aber eher das original, als die MSDN docs erscheinen zu empfehlen.

Und hier ist noch ein Versuch mit CreateProcessWithLogonW. Der Fehler dieses mal ist "Anmeldung fehlgeschlagen: unbekannter Benutzername oder Falsches Kennwort" (Fehler-code 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);

Ich habe auch versucht mit Benutzername im UPN-format ("Alex@Alex-PC") und die übergabe der domain unabhängig, wie das zweite argument, alles ohne Erfolg (gleiche Fehlermeldung).

  • Nicht immer geben Sie Ihre eigene Beschreibung zu Win32Exception. Übergeben Sie stattdessen einen Standard-konstruiert Win32Exception als die InnerException-von einigen Ausnahme-Typ besser geeignet für deine Funktion, deine Beschreibung geht in die äußere Ausnahme. So bekommt der Anrufer sowohl Ihre Informationen (was fehlgeschlagen ist) sowie die Win32-Fehler code (wie war es, fail) und den unverfälschten passende Fehlermeldung.
  • Gibt es eine Möglichkeit zu einem stdin/stdout aus diesem Prozess?
InformationsquelleAutor Noldorin | 2009-03-20
Schreibe einen Kommentar