passaggio parametri
Closed this issue · 3 comments
ciao, durante l'esecuzione del codice con CreateProcessWithLogonW, ho la necessità di impostare della variabili di ambiente che verranno lette dal processo figlio.
Ho provato a impostare le variabili e a ritornare il puntatore all'environment block in questo modo
lpEnvironment = Marshal.StringToHGlobalUni(environmentBlock);
Passandolo poi al metodo CreateProcessWithLogonW, continuo a ottenere errore 87 (parametro non valido)
Questo il mio codice del fork del tuo progetto:
private void RunasCreateProcessWithLogonW(string username, string domainName, string password, int logonType, uint logonFlags, string commandLine, bool bypassUac, ref STARTUPINFO startupInfo, ref ProcessInformation processInfo, ref int logonTypeNotFiltered)
{
IntPtr lpEnvironment = IntPtr.Zero;
IntPtr hToken = IntPtr.Zero;
if (this.HasEnvironmentVariables())
{
//this.EnvironmentVariables è un dizionario che valorizzo prima di chiamare RunAs
string[] arrayNomiValori = this.EnvironmentVariables.Select(entry => $"{entry.Key}={entry.Value}").ToArray();
// Concatenate the environment variables into a single block
string environmentBlock = string.Join("\0", arrayNomiValori) + "\0";
// Convert the environment block to IntPtr
lpEnvironment = Marshal.StringToHGlobalUni(environmentBlock);
}
if (logonType == LOGON32_LOGON_NEW_CREDENTIALS)
{
if (!CreateProcessWithLogonW(username, domainName, password, LOGON_NETCREDENTIALS_ONLY, null, commandLine, CREATE_NO_WINDOW, lpEnvironment, null, ref startupInfo, out processInfo))
throw new RunasCsException("CreateProcessWithLogonW logon type 9", true);
}
else if (bypassUac)
{
int logonTypeBypassUac;
// the below logon types are not filtered by UAC, we allow login with them. Otherwise stick with NetworkCleartext
if (logonType == LOGON32_LOGON_NETWORK || logonType == LOGON32_LOGON_BATCH || logonType == LOGON32_LOGON_SERVICE || logonType == LOGON32_LOGON_NETWORK_CLEARTEXT)
logonTypeBypassUac = logonType;
else
{
// Console.Out.WriteLine("[*] Warning: UAC Bypass is not compatible with logon type '" + logonType.ToString() + "'. Reverting to the NetworkCleartext logon type '8'. To force a specific logon type, use the flag combination --bypass-uac and --logon-type.");
logonTypeBypassUac = LOGON32_LOGON_NETWORK_CLEARTEXT;
}
if (!CreateProcessWithLogonWUacBypass(logonTypeBypassUac, logonFlags, username, domainName, password, null, commandLine, ref startupInfo, out processInfo, lpEnvironment))
throw new RunasCsException("CreateProcessWithLogonWUacBypass", true);
}
else
{
IntPtr hTokenUacCheck = new IntPtr(0);
if (logonType != LOGON32_LOGON_INTERACTIVE)
if (this.ShowMessages) Console.Out.WriteLine("[*] Warning: The function CreateProcessWithLogonW is not compatible with the requested logon type '" + logonType.ToString() + "'. Reverting to the Interactive logon type '2'. To force a specific logon type, use the flag combination --remote-impersonation and --logon-type.");
// we check if the user has been granted the logon type requested, if not we show a message suggesting which logon type can be used to succesfully logon
CheckAvailableUserLogonType(username, password, domainName, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT);
// we use the logon type 2 - Interactive because CreateProcessWithLogonW internally use this logon type for the logon
if (!LogonUser(username, domainName, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref hTokenUacCheck))
throw new RunasCsException("LogonUser", true);
if (IsLimitedUserLogon(hTokenUacCheck, username, domainName, password, out logonTypeNotFiltered))
if (this.ShowMessages) Console.Out.WriteLine($"[*] Warning: The logon for user '{username}' is limited. Use the flag combination --bypass-uac and --logon-type '{logonTypeNotFiltered}' to obtain a more privileged token.");
CloseHandle(hTokenUacCheck);
if (!CreateProcessWithLogonW(username, domainName, password, logonFlags, null, commandLine, CREATE_NO_WINDOW, lpEnvironment, null, ref startupInfo, out processInfo))
throw new RunasCsException("CreateProcessWithLogonW logon type 2", true);
}
}
L'errore avviene alla chiamata if (!CreateProcessWithLogonW(username, domainName, password, logonFlags, null, commandLine, CREATE_NO_WINDOW, lpEnvironment, null, ref startupInfo, out processInfo))
che solleva eccezione.
Per poter gestire il puntatore al blocco di variabili, ho modificato la tua import dll del metodo CreateProcessWithLogonW che ora accetta IntPtr al posto di int.
Hai qualche idea o suggerimento?
grazie
Ciao,
non ti consiglio di passare le variabili di ambiente direttamente dal codice.
Le variabili di ambiente vengono ereditate dal parent al child di default, quindi potresti metterci un processo intermedio come cmd.exe nella chain di esecuzione e risolveresti. Es: RunasCs.exe user pwd "cmd /c ""set ENVVAR1=value1 && C:\process1.exe"""
In questo modo se il processo process1.exe proverà ad accedere alla variabile di ambiente ENVVAR1 riuscirà a recuperare il suo valore value1.
Ad ogni modo, se hai la necessità di farlo dal codice (e te lo sconsiglio), l'environment block lo stai creando in modo errato. Non puoi crearlo come un dizionario e poi fare il marshaling. Dovresti costruirlo nel seguente modo:
Unicode-Str\0Unicode-Str\0...Unicode-Str\0\0
In una versione precedente di RunasCs c'era una funzione che creava manualmente l'environment block, ma il codice è stato rimosso nell'ultima versione. Puoi prendere spunto da qui --> https://github.com/antonioCoco/RunasCs/blob/v1.4/RunasCs.cs#L306
Ciao,
le modifiche che ho fatto, hanno reso RunAs una dll.
Questo perché in questo modo la posso integrare in progetti più grandi che hanno la necessità di lanciare eseguibili con impersonate.
Per questo, metterci in mezzo un cmd.exe per settare le variabili mi viene difficile.
Quando inizializzo la classe che uso per invocare RunAsCs, posso valorizzargli una proprietà che le assegna un dizionario con le variabili e i valori.
Questo dizionario viene poi convertito con il metodo qui sotto prima di fare il marshalling.
private string GetEnvVariables()
{
if (!HasEnvironmentVariables()) return string.Empty;
string[] arrayNomiValori = this.EnvironmentVariables.Select(entry => $"{entry.Key}={entry.Value}").ToArray();
// Concatenate the environment variables into a single block
string environmentBlock = string.Join("\0", arrayNomiValori) + "\0";
return environmentBlock;
}
Come mai mi sconsigli di passare le variabili da codice?
Questo è un esempio di come lancio RunAs con i parametri:
public static void Main(string[] args)
{
var exeFilePath = args[0];
Dictionary<string, string> myDictionary = new Dictionary<string, string>
{
{ "chiave1", "valore1" },
{ "chiave2", "valore2" },
{ "chiave3", "valore3" }
};
Console.WriteLine("*** Esecuzione con utente di prova ***");
var input = RunAs.ExecuteInputBuilder.Create("test@domain.it", "PwdTest", exeFilePath)
.WithEnvironmentVariables(myDictionary)
.WithLogonType(RunAs.LogonType.Interactive)
.Build();
var result = RunAs.Invoker.Execute(input);
Console.Out.Write(result.Output);
Console.ReadKey();
}
grazie
Fabio
Copia il codice che ti ho mandato, lo trovi qui --> https://github.com/antonioCoco/RunasCs/blob/v1.4/RunasCs.cs#L356-L372
Fa esattamente quel che stai cercando di risolvere. Puoi ignorare la parte di sostituzione della variabile USERPROFILE.
lpEnvironment
la puoi usare per l'environment block.
Issue non relativa a RunasCs.