From 544083e8170ef02a339f4cb1f03eccb29605b0bc Mon Sep 17 00:00:00 2001 From: he3als Date: Mon, 26 Feb 2024 18:13:26 +0000 Subject: [PATCH] feat(SelectWindowsImage): return winver & check it --- src/Actions/.NET.cs | 29 +-- .../ConsoleTUI/FrameWriteMethods.cs | 1 + src/Globals.cs | 2 +- src/Misc/SelectWindowsImage.cs | 243 ++++++++++++++---- src/NSudo.cs | 17 +- src/app.manifest | 7 + 6 files changed, 230 insertions(+), 69 deletions(-) diff --git a/src/Actions/.NET.cs b/src/Actions/.NET.cs index 356e93d..8d34840 100644 --- a/src/Actions/.NET.cs +++ b/src/Actions/.NET.cs @@ -15,23 +15,23 @@ namespace amecs.Actions { public class _NET { - private static string isoPath; - private static string mountedPath; + private static string _mountedPath; + private static string _isoPath; private static void Unmount() { - if (isoPath == "none") + if (_isoPath == "none") return; - SelectWindowsImage.DismountIso(isoPath); + SelectWindowsImage.DismountIso(_isoPath); } public static bool Install() { - (mountedPath, isoPath) = SelectWindowsImage.GetMediaPath(); - if (mountedPath == null) return false; + (_mountedPath, _isoPath, _, _, _) = SelectWindowsImage.GetMediaPath(true); + if (_mountedPath == null) return false; - if (!Directory.Exists(mountedPath + @"\sources\sxs") || !Directory.GetFiles(mountedPath + @"\sources\sxs", "*netfx3*").Any()) + if (!Directory.Exists(_mountedPath + @"\sources\sxs") || !Directory.GetFiles(_mountedPath + @"\sources\sxs", "*netfx3*").Any()) { Unmount(); Console.WriteLine(); @@ -52,9 +52,9 @@ namespace amecs.Actions using (var session = DismApi.OpenOnlineSession()) { var stdout = GetStdHandle(-11); - bool indicatorStopped = false; + var indicatorStopped = false; var maxHashTags = (ConsoleTUI.OpenFrame.DisplayWidth - 5); - DismApi.EnableFeatureByPackagePath(session, "NetFX3", null, true, true, new List() { mountedPath + @"\sources\sxs" }, delegate(DismProgress progress) + DismApi.EnableFeatureByPackagePath(session, "NetFX3", null, true, true, new List() { _mountedPath + @"\sources\sxs" }, delegate(DismProgress progress) { inProgress = true; if (!indicatorStopped) @@ -65,12 +65,11 @@ namespace amecs.Actions } indicatorStopped = true; - var progressPerc = progress.Current / 10; - var currentHashTags = (int)Math.Ceiling(Math.Min(((double)progressPerc / 100) * maxHashTags, maxHashTags)); - var spaces = maxHashTags - currentHashTags + (4 - progressPerc.ToString().Length); - var sb = new StringBuilder(new string('#', currentHashTags) + new string(' ', spaces) + progressPerc + "%"); - uint throwaway; - WriteConsoleOutputCharacter(stdout, sb, (uint)sb.Length, new Languages.COORD((short)ConsoleTUI.OpenFrame.DisplayOffset, (short)Console.CursorTop), out throwaway); + var progressPercentage = progress.Current / 10; + var currentHashTags = (int)Math.Ceiling(Math.Min(((double)progressPercentage / 100) * maxHashTags, maxHashTags)); + var spaces = maxHashTags - currentHashTags + (4 - progressPercentage.ToString().Length); + var sb = new StringBuilder(new string('#', currentHashTags) + new string(' ', spaces) + progressPercentage + "%"); + WriteConsoleOutputCharacter(stdout, sb, (uint)sb.Length, new Languages.COORD((short)ConsoleTUI.OpenFrame.DisplayOffset, (short)Console.CursorTop), out _); inProgress = false; }); session.Close(); diff --git a/src/Ameliorated.ConsoleUtils/ConsoleTUI/FrameWriteMethods.cs b/src/Ameliorated.ConsoleUtils/ConsoleTUI/FrameWriteMethods.cs index c39429f..7831bdc 100644 --- a/src/Ameliorated.ConsoleUtils/ConsoleTUI/FrameWriteMethods.cs +++ b/src/Ameliorated.ConsoleUtils/ConsoleTUI/FrameWriteMethods.cs @@ -323,6 +323,7 @@ namespace Ameliorated.ConsoleUtils ConsoleUtils.ResetColor(); } + // TODO: Fix this splitting lines when a piece of text can fit on one line private List CenterLines(string text, LineCenterOptions options, int maxWidth = 0) { var _maxWidth = DisplayWidth; diff --git a/src/Globals.cs b/src/Globals.cs index 7bad17d..62fa23c 100644 --- a/src/Globals.cs +++ b/src/Globals.cs @@ -36,6 +36,6 @@ namespace amecs return true; } - public static readonly int WinVer = Int32.Parse(Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue("CurrentBuildNumber").ToString()); + public static readonly int WinVer = int.Parse(Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue("CurrentBuildNumber").ToString()); } } \ No newline at end of file diff --git a/src/Misc/SelectWindowsImage.cs b/src/Misc/SelectWindowsImage.cs index 9448efa..4e27fef 100644 --- a/src/Misc/SelectWindowsImage.cs +++ b/src/Misc/SelectWindowsImage.cs @@ -1,26 +1,25 @@ using System; using System.Diagnostics; +using System.IO; +using System.Linq; using System.Security; using System.Windows.Forms; using Ameliorated.ConsoleUtils; +using Microsoft.Dism; // Asks user to select Windows installation media, and mounts it if applicable // Returns path to where it's mounted namespace amecs.Misc { - /// - /// Asks user to select Windows installation media, and mounts it if applicable - /// Returns path to where it's mounted - /// - public class SelectWindowsImage + public static class SelectWindowsImage { - private static string file; + private static string _fileViolationTest; private static bool CheckFileViolation(string inputFile) { try { - file = inputFile; + _fileViolationTest = inputFile; } catch (SecurityException e) { @@ -32,7 +31,22 @@ namespace amecs.Misc return false; } - public static bool DismountIso(string path) + public static string GetWindowsVersion(float majorMinor, int isoBuild) + { + return (majorMinor, isoBuild) switch + { + (6, _) => "Windows Vista", + (6.1f, _) => "Windows 7", + (6.2f, _) => "Windows 8", + (6.3f, _) => "Windows 8.1", + (10, var a) when a < 19041 => "Windows 10 (Old)", + (10, var a) when a >= 22000 => "Windows 11", + (10, _) => "Windows 10", + _ => "Unknown" + }; + } + + public static bool DismountIso(string imagePath) { var startInfo = new ProcessStartInfo { @@ -40,7 +54,7 @@ namespace amecs.Misc UseShellExecute = false, FileName = "PowerShell.exe", WindowStyle = ProcessWindowStyle.Hidden, - Arguments = $"-NoP -C \"Dismount-DiskImage '{path}'\"", + Arguments = $"-NoP -C \"Dismount-DiskImage '{imagePath}'\"", RedirectStandardOutput = true }; @@ -50,11 +64,24 @@ namespace amecs.Misc return true; } - public static (string MountedPath, string IsoPath) GetMediaPath() + private static string _mountedPath; + private static string _isoPath; + private static string _isoWinVer; + private static int _isoBuild; + + /// + /// Asks user to select Windows installation media, mounts it if applicable, and checks its version + /// + /// If true when ISO and host versions mismatch, prompts user that things can break if they continue + /// If true and the ISO build can't be retrieved, prompts a user with an error + public static ( + string MountedPath, string IsoPath, string Winver, int? Build, bool? VersionsMatch + ) GetMediaPath(bool winVersionsMustMatch = false, bool isoBuildMustBeReturned = false) { + var error = ((string)null, "none", (string)null, (int?)null, (bool?)null); var choice = new ChoicePrompt() { Text = "To continue, Windows installation media is needed.\r\nDo you have a Windows USB instead of an ISO file? (Y/N): " }.Start(); - if (choice == null) return (null, "none"); + if (!choice.HasValue) return error; // Folder/drive chosen var usingFolder = choice == 0; @@ -64,56 +91,170 @@ namespace amecs.Misc { InputPath = Globals.UserFolder }; - - if (dlg.ShowDialog(IntPtr.Zero, false).GetValueOrDefault()) - return CheckFileViolation(dlg.ResultPath) ? (null, "none") : (dlg.ResultPath, "none"); - - Console.WriteLine(); - ConsoleTUI.OpenFrame.Close("\r\nYou must select a folder or drive containing Windows installation media.", - new ChoicePrompt() { AnyKey = true, Text = "Press any key to return to the Menu: " }); - return (null, "none"); - } - - // Mounting the ISO - var dialog = new OpenFileDialog(); - dialog.Filter = "ISO Files (*.ISO)| *.ISO"; - dialog.Multiselect = false; - dialog.InitialDirectory = Globals.UserFolder; - NativeWindow window = new NativeWindow(); - window.AssignHandle(Process.GetCurrentProcess().MainWindowHandle); - if (dialog.ShowDialog(window) == DialogResult.OK) - { - if (CheckFileViolation(dialog.FileName)) return (null, "none"); - Console.WriteLine(); - ConsoleTUI.OpenFrame.WriteCentered("\r\nMounting ISO"); + if (dlg.ShowDialog(IntPtr.Zero).GetValueOrDefault()) + { + if (CheckFileViolation(dlg.ResultPath)) + return error; + + _mountedPath = dlg.ResultPath; + } + else + { + Console.WriteLine(); + ConsoleTUI.OpenFrame.Close("\r\nYou must select a folder or drive containing Windows installation media.", + new ChoicePrompt() { AnyKey = true, Text = "Press any key to return to the Menu: " }); + return error; + } } else { - Console.WriteLine(); - ConsoleTUI.OpenFrame.Close("\r\nYou must select an ISO.", - new ChoicePrompt() { AnyKey = true, Text = "Press any key to return to the Menu: " }); - return (null, "none"); - } + // Mounting the ISO + var dialog = new OpenFileDialog(); + dialog.Filter = "ISO Files (*.ISO)| *.ISO"; + dialog.Multiselect = false; + dialog.InitialDirectory = Globals.UserFolder; - using (new ConsoleUtils.LoadingIndicator(true)) + var window = new NativeWindow(); + window.AssignHandle(Process.GetCurrentProcess().MainWindowHandle); + if (dialog.ShowDialog(window) == DialogResult.OK) + { + _isoPath = dialog.FileName; + if (CheckFileViolation(_isoPath)) return error; + Console.WriteLine(); + ConsoleTUI.OpenFrame.WriteCentered("\r\nMounting ISO"); + } + else + { + Console.WriteLine(); + ConsoleTUI.OpenFrame.Close("\r\nYou must select an ISO.", + new ChoicePrompt() { AnyKey = true, Text = "Press any key to return to the Menu: " }); + return error; + } + + using (new ConsoleUtils.LoadingIndicator(true)) + { + var startInfo = new ProcessStartInfo + { + CreateNoWindow = false, + UseShellExecute = false, + FileName = "PowerShell.exe", + WindowStyle = ProcessWindowStyle.Hidden, + Arguments = $"-NoP -C \"(Mount-DiskImage '{_isoPath}' -PassThru | Get-Volume).DriveLetter + ':\'\"", + RedirectStandardOutput = true + }; + + var proc = Process.Start(startInfo); + if (proc == null) return error; + proc.WaitForExit(); + + _mountedPath = proc.StandardOutput.ReadLine(); + } + } + + // Check WIM version + var wimOrEsdPath = new[] { $@"{_mountedPath}\sources\install.esd", $@"{_mountedPath}\sources\install.wim" }.FirstOrDefault(File.Exists); + if (!string.IsNullOrEmpty(wimOrEsdPath)) { - var startInfo = new ProcessStartInfo + try { - CreateNoWindow = false, - UseShellExecute = false, - FileName = "PowerShell.exe", - WindowStyle = ProcessWindowStyle.Hidden, - Arguments = $"-NoP -C \"(Mount-DiskImage '{file}' -PassThru | Get-Volume).DriveLetter + ':\'\"", - RedirectStandardOutput = true - }; + DismApi.Initialize(DismLogLevel.LogErrors); + + string previousIndexVersion = null; + string isoFullVersion = null; + var multiVersion = false; + + var imageInfos = DismApi.GetImageInfo(wimOrEsdPath); + foreach (var imageInfo in imageInfos) + { + isoFullVersion = imageInfo.ProductVersion.ToString(); + if (isoFullVersion != previousIndexVersion && previousIndexVersion != null) + { + // If it's multi-version, WinVer will be "Unknown" as well + multiVersion = true; + isoFullVersion = "0.0.0.0"; + break; + } + previousIndexVersion = isoFullVersion; + } + + switch (multiVersion) + { + case true when isoBuildMustBeReturned: + ConsoleTUI.OpenFrame.Close( + $"Multiple Windows versions were found in the Windows image, can't determine which Windows build it is. Please use an unmodified Windows ISO.", + ConsoleColor.Red, Console.BackgroundColor, + new ChoicePrompt() { AnyKey = true, Text = "Press any key to return to the Menu: " }); + return error; + case true when winVersionsMustMatch: + ConsoleTUI.OpenFrame.Close( + $"Multiple Windows versions were found in the Windows image, can't determine which Windows build it is. If your Windows version doesn't match the ISO, there will be problems.", + ConsoleColor.Red, Console.BackgroundColor, + new ChoicePrompt() { AnyKey = true, Text = "Press any key to continue anyways: " }); + + Program.Frame.Clear(); + ConsoleTUI.OpenFrame.WriteCentered("\r\nContinuing without version check\r\n"); + break; + } - var proc = Process.Start(startInfo); - if (proc == null) return (null, "none"); - proc.WaitForExit(); + var buildSplit = isoFullVersion.Split('.'); + _isoBuild = int.Parse(buildSplit[2]); + _isoWinVer = GetWindowsVersion(float.Parse($"{buildSplit[0]}.{buildSplit[1]}"), _isoBuild); + } + catch (Exception e) + { + Console.WriteLine(); + ConsoleTUI.OpenFrame.Close( + "Error checking ISO version: " + e.Message.TrimEnd('\n').TrimEnd('\r'), + ConsoleColor.Red, Console.BackgroundColor, + new ChoicePrompt() { AnyKey = true, Text = "Press any key to return to the Menu: " }); + return error; + } + finally + { + try + { + DismApi.Shutdown(); + } + catch + { + // do nothing + } + } - return (proc.StandardOutput.ReadLine(), file); + // Check the current OS version + var hostVersion = Environment.OSVersion.Version; + var hostWinver = GetWindowsVersion(float.Parse($"{hostVersion.Major}.{hostVersion.Minor}"), hostVersion.Build); + + // If it all matches + if (hostWinver == _isoWinVer && winVersionsMustMatch) return (_mountedPath, _isoPath, _isoWinVer, _isoBuild, true); + // If ISO version doesn't match host version, and winVersionsMustMatch is true + if (hostWinver != _isoWinVer && winVersionsMustMatch) + { + if (!string.IsNullOrEmpty(_isoPath)) DismountIso(_isoPath); + ConsoleTUI.OpenFrame.Close( + $"You're on {hostWinver}, but the selected image is {_isoWinVer}. You can only use an ISO that matches your Windows version.", + ConsoleColor.Red, Console.BackgroundColor, + new ChoicePrompt() { AnyKey = true, Text = "Press any key to return to the Menu: " }); + return error; + } + // If ISO version doesn't match host version, and winVersionsMustMatch is true + if (hostWinver != _isoWinVer) return (_mountedPath, _isoPath, _isoWinVer, _isoBuild, false); } + + var noWimText = isoBuildMustBeReturned + ? "Press any key to return to the Menu" + : "Press any key to continue anyways"; + + ConsoleTUI.OpenFrame.Close( + $"No Windows installation image was found inside the selected Windows media. No version check can be done, things might break.", + ConsoleColor.Red, Console.BackgroundColor, + new ChoicePrompt() { AnyKey = true, Text = $"{noWimText}: " }); + + Program.Frame.Clear(); + ConsoleTUI.OpenFrame.WriteCentered("\r\nContinuing without version check\r\n"); + + return isoBuildMustBeReturned ? error : (_mountedPath, _isoPath, null, null, null); } } } \ No newline at end of file diff --git a/src/NSudo.cs b/src/NSudo.cs index 1d052de..81456c7 100644 --- a/src/NSudo.cs +++ b/src/NSudo.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using System.Security; using System.Security.Principal; using System.Threading; +using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; namespace amecs @@ -505,8 +506,20 @@ namespace amecs RtlAdjustPrivilege((int)luid, true, true, out throwaway); } - public class Win32 { - + public static void RunAsUser(Action action) + { + var token = NSudo.GetUserToken(); + Task.Run((Action)Delegate.Combine((Action)(() => { NSudo.GetUserPrivilege(token); }), + action)).Wait(); + Marshal.FreeHGlobal(token); + } + + private static async Task RunAsUserAsync(Action action) + { + var token = NSudo.GetUserToken(); + await Task.Run((Action)Delegate.Combine((Action)(() => { NSudo.GetUserPrivilege(token); }), + action)); + Marshal.FreeHGlobal(token); } } } \ No newline at end of file diff --git a/src/app.manifest b/src/app.manifest index 6975dd0..48a545d 100644 --- a/src/app.manifest +++ b/src/app.manifest @@ -7,4 +7,11 @@ + + + + + + \ No newline at end of file