L'appel de CreateProcessAsUser à partir de C#
J'ai essayé de créer un nouveau processus dans le contexte d'un utilisateur spécifique en utilisant la CreateProcessAsUser
fonction de l'API Windows, mais semblent être en cours d'exécution dans un assez méchant problème de sécurité...
Avant que j'explique plus loin, voici le code que j'utilise actuellement pour démarrer le nouveau processus (un processus console - PowerShell pour être plus précis, mais il ne devrait pas).
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);
}
Pour le bien de la question ici, ignorer le code de traiter avec les variables d'environnement (je n'ai testé que la section de façon indépendante et il semble fonctionner.)
Maintenant, l'erreur que j'obtiens est le suivant (renvoyée à la ligne suite à l'appel à CreateProcessAsUSer
):
"Un privilège requis n'est pas tenue par le client" (code d'erreur 1314)
(Le message d'erreur a été découvert en supprimant le paramètre de message à partir de la Win32Exception constructeur. Certes, mon code de gestion d'erreur ici, peut-être pas le meilleur, mais c'est un peu hors de propos de la matière. Vous êtes invités à commenter si vous le souhaitez, cependant.) Je suis vraiment confus quant à la cause de cette vague d'erreur dans cette situation. La documentation MSDN et les différentes discussions sur le forum ont seulement me donne beaucoup de conseils, et surtout étant donné que les causes de ces erreurs semblent être très variées, je n'ai aucune idée de quelle section de code que j'ai besoin de modifier. C'est peut-être simplement d'un seul paramètre, j'ai besoin de changer, mais je pourrais faire le mal/pas assez de WinAPI appels pour tout ce que je sais. Ce qui me confond fortement, c'est que la version précédente du code qui utilise la plaine CreateProcess
fonction (équivalent sauf pour le jeton de l'utilisateur en paramètre) a parfaitement fonctionné très bien. Ce que je comprends, il est seulement nécessaire de faire appel à l'ouverture de session de l'utilisateur en fonction de recevoir la marque appropriée de la poignée, puis dupliquez-le de sorte qu'il peut être transmis à CreateProcessAsUser
.
Toutes les suggestions pour modifier le code, ainsi que des explications seraient les bienvenues.
Notes
J'ai été principalement référence à la MSDN docs (ainsi que PInvoke.net pour le C# fonction/montant/enum déclarations). Les pages suivantes, en particulier, semblent avoir beaucoup d'informations dans les notes de sections, de certains de ce qui peut être important de se soustraire à moi:
Modifier
J'ai juste essayé Mitch de la suggestion, mais, malheureusement, la vieille erreur a simplement été remplacé par un nouveau: "Le système ne peut pas trouver le fichier spécifié." (code d'erreur 2)
De l'appel précédent de CreateProcessAsUser
a été remplacé par le suivant:
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);
Noter que ce code n'utilise plus le double jeton mais plutôt l'original, comme le MSDN docs semblent le suggérer.
Et voici une autre tentative à l'aide de CreateProcessWithLogonW
. L'erreur cette fois est "échec d'ouverture de session: nom d'utilisateur inconnu ou mot de passe incorrect" (code d'erreur 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);
J'ai aussi essayé de spécifier le nom d'utilisateur au format UPN ("Alex@Alex-PC") et en passant le domaine de façon indépendante en tant que deuxième argument, en vain (identique d'erreur).
Est-il possible d'obtenir un stdin/stdout de ce processus?
OriginalL'auteur Noldorin | 2009-03-20
Vous devez vous connecter pour publier un commentaire.
Ahh... semble liker j'ai été pris par l'un des plus grands pièges WinAPI interop de programmation. Aussi, en Affichant le code de mon déclarations de fonction aurait été une bonne idée dans ce cas.
De toute façon, tout ce que j'avais à faire était d'ajouter un argument à l'attribut DllImport de la fonction en spécifiant
CharSet = CharSet.Unicode
. Cela a fait le tour pour deux leCreateProcessWithLogonW
etCreateProcessWithTokenW
fonctions. Je suppose que c'enfin, il suffit de me frapper que le W suffixe des noms de fonction visée à l'Unicode et que j'avais besoin de spécifier explicitement ce en C#! Voici les corriger les déclarations de fonction dans le cas où quelqu'un est intéressé:OriginalL'auteur Noldorin
De ici:
Voir ce blog Comment appeler CreateProcessWithLogonW & CreateProcessAsUser .NET
(suite.) Peut-être expérimenter avec certains de la Stratégie de Sécurité Locale règles permettra, à en juger par le paragraphe cité dans ton post.
Note: j'ai mis à jour le post original, pour avoir essayé la solution que vous proposez.
Viens de remarquer que j'ai essayé CreateProcessWithTokenW. Pas de chance avec CreateProcessAsLogonW: erreur 1326 (non valide les informations d'identification d'ouverture de session). N'est-ce pas merveilleux?... un tout à fait unique d'erreur pour chacune de ces trois fonctions.
une erreur de "non valide les informations d'identification d'ouverture de session" semble assez concluante. Êtes-vous sûr que vous avez le Domaine\nom d'utilisateur et le mot de passe correct?
OriginalL'auteur Mitch Wheat
Jonathan Poivrons
fourni ce grand morceau de code qui fixe mes questions
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/0c0ca087-5e7b-4046-93cb-c7b3e48d0dfb?ppud=4
OriginalL'auteur ermagana