2024-08-08 18:00:41 -05:00
|
|
|
|
using LANCommander.SDK.Enums;
|
|
|
|
|
|
using LANCommander.SDK.Helpers;
|
2024-08-05 18:02:01 -05:00
|
|
|
|
using LANCommander.SDK.PowerShell.Cmdlets;
|
2024-08-15 21:33:17 -05:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2023-11-15 22:38:20 -06:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
2024-08-14 13:24:50 -05:00
|
|
|
|
using System.Management.Automation;
|
2024-08-05 18:02:01 -05:00
|
|
|
|
using System.Management.Automation.Runspaces;
|
2023-12-13 23:38:13 -06:00
|
|
|
|
using System.Reflection;
|
2023-11-15 22:38:20 -06:00
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
using System.Text;
|
2024-08-05 18:02:01 -05:00
|
|
|
|
using System.Threading.Tasks;
|
2024-06-11 21:43:12 -05:00
|
|
|
|
using YamlDotNet.Serialization;
|
|
|
|
|
|
using YamlDotNet.Serialization.NamingConventions;
|
2023-11-15 22:38:20 -06:00
|
|
|
|
|
|
|
|
|
|
namespace LANCommander.SDK.PowerShell
|
|
|
|
|
|
{
|
|
|
|
|
|
public class PowerShellScript
|
|
|
|
|
|
{
|
2024-08-08 18:00:41 -05:00
|
|
|
|
public ScriptType Type { get; private set; }
|
2023-11-15 22:38:20 -06:00
|
|
|
|
private string Contents { get; set; } = "";
|
2024-08-08 18:00:41 -05:00
|
|
|
|
public string WorkingDirectory { get; private set; } = "";
|
2023-11-15 22:38:20 -06:00
|
|
|
|
private bool ShellExecute { get; set; } = false;
|
2024-08-08 18:00:41 -05:00
|
|
|
|
public bool RunAsAdmin { get; private set; } = false;
|
2023-11-15 22:38:20 -06:00
|
|
|
|
private bool IgnoreWow64 { get; set; } = false;
|
2024-06-11 21:43:12 -05:00
|
|
|
|
private bool Debug { get; set; } = false;
|
2024-08-08 18:00:41 -05:00
|
|
|
|
public PowerShellVariableList Variables { get; private set; }
|
|
|
|
|
|
public Dictionary<string, string> Arguments { get; private set; }
|
2024-08-05 18:02:01 -05:00
|
|
|
|
|
|
|
|
|
|
private InitialSessionState InitialSessionState { get; set; }
|
2023-11-15 22:38:20 -06:00
|
|
|
|
|
2024-08-14 13:24:50 -05:00
|
|
|
|
private TaskCompletionSource<string> Input { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public Func<System.Management.Automation.PowerShell, Task> OnDebug;
|
2024-08-15 21:33:17 -05:00
|
|
|
|
public Func<LogLevel, string, Task> OnOutput;
|
2024-08-14 13:24:50 -05:00
|
|
|
|
|
2024-08-08 18:00:41 -05:00
|
|
|
|
public PowerShellScript(ScriptType type)
|
2023-11-15 22:38:20 -06:00
|
|
|
|
{
|
2024-08-08 18:00:41 -05:00
|
|
|
|
Type = type;
|
|
|
|
|
|
Variables = new PowerShellVariableList();
|
2023-11-15 22:38:20 -06:00
|
|
|
|
Arguments = new Dictionary<string, string>();
|
2023-11-17 22:35:17 -06:00
|
|
|
|
|
2024-08-05 18:02:01 -05:00
|
|
|
|
InitialSessionState = InitialSessionState.CreateDefault();
|
|
|
|
|
|
|
|
|
|
|
|
InitialSessionState.Commands.Add(new SessionStateCmdletEntry("Convert-AspectRatio", typeof(ConvertAspectRatioCmdlet), null));
|
|
|
|
|
|
InitialSessionState.Commands.Add(new SessionStateCmdletEntry("ConvertFrom-SerializedBase64", typeof(ConvertFromSerializedBase64Cmdlet), null));
|
|
|
|
|
|
InitialSessionState.Commands.Add(new SessionStateCmdletEntry("ConvertTo-SerializedBase64", typeof(ConvertToSerializedBase64Cmdlet), null));
|
|
|
|
|
|
InitialSessionState.Commands.Add(new SessionStateCmdletEntry("ConvertTo-StringBytes", typeof(ConvertToStringBytesCmdlet), null));
|
|
|
|
|
|
InitialSessionState.Commands.Add(new SessionStateCmdletEntry("Edit-PatchBinary", typeof(EditPatchBinaryCmdlet), null));
|
|
|
|
|
|
InitialSessionState.Commands.Add(new SessionStateCmdletEntry("Get-GameManifest", typeof(GetGameManifestCmdlet), null));
|
|
|
|
|
|
InitialSessionState.Commands.Add(new SessionStateCmdletEntry("Get-PrimaryDisplay", typeof(GetPrimaryDisplayCmdlet), null));
|
|
|
|
|
|
InitialSessionState.Commands.Add(new SessionStateCmdletEntry("Update-IniValue", typeof(UpdateIniValueCmdlet), null));
|
|
|
|
|
|
InitialSessionState.Commands.Add(new SessionStateCmdletEntry("Write-GameManifest", typeof(WriteGameManifestCmdlet), null));
|
|
|
|
|
|
InitialSessionState.Commands.Add(new SessionStateCmdletEntry("Write-ReplaceContentInFile", typeof(ReplaceContentInFileCmdlet), null));
|
2023-11-17 21:38:37 -06:00
|
|
|
|
|
|
|
|
|
|
IgnoreWow64Redirection();
|
2023-11-15 22:38:20 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public PowerShellScript UseFile(string path)
|
|
|
|
|
|
{
|
|
|
|
|
|
Contents = File.ReadAllText(path);
|
|
|
|
|
|
|
2024-08-08 18:00:41 -05:00
|
|
|
|
if (Contents.StartsWith("# Requires Admin"))
|
|
|
|
|
|
RunAsAdmin = true;
|
|
|
|
|
|
|
2023-11-15 22:38:20 -06:00
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public PowerShellScript UseInline(string contents)
|
|
|
|
|
|
{
|
|
|
|
|
|
Contents = contents;
|
|
|
|
|
|
|
2024-08-08 18:00:41 -05:00
|
|
|
|
if (Contents.StartsWith("# Requires Admin"))
|
|
|
|
|
|
RunAsAdmin = true;
|
|
|
|
|
|
|
2023-11-15 22:38:20 -06:00
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public PowerShellScript UseWorkingDirectory(string path)
|
|
|
|
|
|
{
|
|
|
|
|
|
WorkingDirectory = path;
|
|
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public PowerShellScript UseShellExecute()
|
|
|
|
|
|
{
|
|
|
|
|
|
ShellExecute = true;
|
|
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public PowerShellScript AddVariable<T>(string name, T value)
|
|
|
|
|
|
{
|
|
|
|
|
|
Variables.Add(new PowerShellVariable(name, value, typeof(T)));
|
|
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public PowerShellScript AddArgument<T>(string name, T value)
|
|
|
|
|
|
{
|
|
|
|
|
|
Arguments.Add(name, $"\"{value}\"");
|
|
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public PowerShellScript AddArgument(string name, int value)
|
|
|
|
|
|
{
|
|
|
|
|
|
Arguments[name] = value.ToString();
|
|
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public PowerShellScript AddArgument(string name, long value)
|
|
|
|
|
|
{
|
|
|
|
|
|
Arguments[name] = value.ToString();
|
|
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-08 18:00:41 -05:00
|
|
|
|
public PowerShellScript IgnoreWow64Redirection()
|
2023-11-15 22:38:20 -06:00
|
|
|
|
{
|
2024-08-08 18:00:41 -05:00
|
|
|
|
IgnoreWow64 = true;
|
2023-11-15 22:38:20 -06:00
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-08 18:00:41 -05:00
|
|
|
|
public PowerShellScript EnableDebug()
|
2023-11-15 22:38:20 -06:00
|
|
|
|
{
|
2024-08-08 18:00:41 -05:00
|
|
|
|
Debug = true;
|
2023-11-15 22:38:20 -06:00
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-08 18:00:41 -05:00
|
|
|
|
public PowerShellScript AsAdmin()
|
2024-06-11 21:43:12 -05:00
|
|
|
|
{
|
2024-08-08 18:00:41 -05:00
|
|
|
|
RunAsAdmin = true;
|
2024-06-11 21:43:12 -05:00
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-05 18:02:01 -05:00
|
|
|
|
public async Task<int> ExecuteAsync()
|
2023-11-15 22:38:20 -06:00
|
|
|
|
{
|
|
|
|
|
|
var scriptBuilder = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
|
|
var wow64Value = IntPtr.Zero;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var variable in Variables)
|
|
|
|
|
|
{
|
2023-11-16 14:34:00 -06:00
|
|
|
|
scriptBuilder.AppendLine($"${variable.Name} = ConvertFrom-SerializedBase64 \"{Serialize(variable.Value)}\"");
|
2023-11-15 22:38:20 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
scriptBuilder.AppendLine(Contents);
|
|
|
|
|
|
|
2024-06-11 21:43:12 -05:00
|
|
|
|
if (Debug)
|
|
|
|
|
|
{
|
2024-07-07 20:37:39 -05:00
|
|
|
|
scriptBuilder.AppendLine("Write-Host '----- DEBUG -----'");
|
|
|
|
|
|
scriptBuilder.AppendLine("Write-Host 'Variables:'");
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var variable in Variables)
|
|
|
|
|
|
{
|
|
|
|
|
|
scriptBuilder.AppendLine($"Write-Host ' ${variable.Name}'");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
scriptBuilder.AppendLine("Write-Host ''");
|
2024-06-11 21:43:12 -05:00
|
|
|
|
|
2024-08-05 18:02:01 -05:00
|
|
|
|
// Process.StartInfo.Arguments += " -NoExit";
|
2024-06-11 21:43:12 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-15 22:38:20 -06:00
|
|
|
|
if (IgnoreWow64)
|
|
|
|
|
|
Wow64DisableWow64FsRedirection(ref wow64Value);
|
|
|
|
|
|
|
2024-08-05 18:02:01 -05:00
|
|
|
|
using (Runspace runspace = RunspaceFactory.CreateRunspace(InitialSessionState))
|
2023-11-15 22:38:20 -06:00
|
|
|
|
{
|
2024-08-05 18:02:01 -05:00
|
|
|
|
runspace.Open();
|
2023-11-15 22:38:20 -06:00
|
|
|
|
|
2024-08-05 18:02:01 -05:00
|
|
|
|
runspace.SessionStateProxy.Path.SetLocation(WorkingDirectory);
|
2023-11-15 22:38:20 -06:00
|
|
|
|
|
2024-08-05 18:02:01 -05:00
|
|
|
|
using (var ps = System.Management.Automation.PowerShell.Create())
|
|
|
|
|
|
{
|
|
|
|
|
|
ps.Runspace = runspace;
|
2023-11-15 22:38:20 -06:00
|
|
|
|
|
2024-08-05 18:02:01 -05:00
|
|
|
|
ps.AddScript(scriptBuilder.ToString());
|
2023-11-15 22:38:20 -06:00
|
|
|
|
|
2024-08-15 21:33:17 -05:00
|
|
|
|
if (Debug)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (OnOutput != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ps.Streams.Information.DataAdded += Information_DataAdded;
|
|
|
|
|
|
ps.Streams.Verbose.DataAdded += Verbose_DataAdded;
|
|
|
|
|
|
ps.Streams.Debug.DataAdded += Debug_DataAdded;
|
|
|
|
|
|
ps.Streams.Warning.DataAdded += Warning_DataAdded;
|
|
|
|
|
|
ps.Streams.Error.DataAdded += Error_DataAdded;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-05 18:02:01 -05:00
|
|
|
|
var results = await ps.InvokeAsync();
|
2024-08-14 13:24:50 -05:00
|
|
|
|
|
|
|
|
|
|
if (Debug)
|
2024-08-15 21:33:17 -05:00
|
|
|
|
{
|
2024-08-14 13:24:50 -05:00
|
|
|
|
await OnDebug?.Invoke(ps);
|
2024-08-15 21:33:17 -05:00
|
|
|
|
}
|
2024-08-05 18:02:01 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-11-15 22:38:20 -06:00
|
|
|
|
|
|
|
|
|
|
if (IgnoreWow64)
|
|
|
|
|
|
Wow64RevertWow64FsRedirection(ref wow64Value);
|
|
|
|
|
|
|
2024-08-05 18:02:01 -05:00
|
|
|
|
return 0;
|
2023-11-15 22:38:20 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-15 21:33:17 -05:00
|
|
|
|
private void Error_DataAdded(object sender, DataAddedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
var record = ((PSDataCollection<ErrorRecord>)sender)[e.Index];
|
|
|
|
|
|
|
|
|
|
|
|
OnOutput?.Invoke(LogLevel.Error, $"{record.InvocationInfo.InvocationName} : {record.Exception.Message}");
|
|
|
|
|
|
OnOutput?.Invoke(LogLevel.Error, record.InvocationInfo.PositionMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Warning_DataAdded(object sender, DataAddedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
var record = ((PSDataCollection<WarningRecord>)sender)[e.Index];
|
|
|
|
|
|
|
|
|
|
|
|
OnOutput?.Invoke(LogLevel.Warning, record.Message);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Debug_DataAdded(object sender, DataAddedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
var record = ((PSDataCollection<DebugRecord>)sender)[e.Index];
|
|
|
|
|
|
|
|
|
|
|
|
OnOutput?.Invoke(LogLevel.Debug, record.Message);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Verbose_DataAdded(object sender, DataAddedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
var record = ((PSDataCollection<VerboseRecord>)sender)[e.Index];
|
|
|
|
|
|
|
|
|
|
|
|
OnOutput?.Invoke(LogLevel.Trace, record.Message);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Information_DataAdded(object sender, DataAddedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
var record = ((PSDataCollection<InformationRecord>)sender)[e.Index];
|
|
|
|
|
|
|
|
|
|
|
|
if (record.MessageData != null && record.MessageData is HostInformationMessage)
|
|
|
|
|
|
OnOutput?.Invoke(LogLevel.Information, (record.MessageData as HostInformationMessage).Message);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-15 22:38:20 -06:00
|
|
|
|
public static string Serialize<T>(T input)
|
|
|
|
|
|
{
|
2024-06-11 21:43:12 -05:00
|
|
|
|
var serializer = new SerializerBuilder()
|
|
|
|
|
|
.WithNamingConvention(new PascalCaseNamingConvention())
|
|
|
|
|
|
.Build();
|
|
|
|
|
|
|
|
|
|
|
|
// Use the YamlDotNet serializer to generate a string for our input. Then convert to base64 so we can put it on one line.
|
|
|
|
|
|
return Convert.ToBase64String(Encoding.UTF8.GetBytes(serializer.Serialize(input)));
|
2023-11-15 22:38:20 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
|
|
|
|
static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr);
|
|
|
|
|
|
|
|
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
|
|
|
|
static extern bool Wow64RevertWow64FsRedirection(ref IntPtr ptr);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|