commit 332368d755ed3bc657e96e1f5ee03307e073c5b4 Author: Styris Date: Wed Mar 29 03:39:04 2023 -0700 Start diff --git a/src/ame-upgrade-script/.idea/.idea.ame-upgrade-preparation-tool/.idea/workspace.xml b/src/ame-upgrade-script/.idea/.idea.ame-upgrade-preparation-tool/.idea/workspace.xml new file mode 100644 index 0000000..804e512 --- /dev/null +++ b/src/ame-upgrade-script/.idea/.idea.ame-upgrade-preparation-tool/.idea/workspace.xml @@ -0,0 +1,67 @@ + + + + ame-upgrade-preparation-tool/ame-upgrade-preparation-tool.csproj + + + + + + + + + + + + + + + + + + + 1680046494161 + + + + + + + + + \ No newline at end of file diff --git a/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/.name b/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/.name new file mode 100644 index 0000000..7b014b5 --- /dev/null +++ b/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/.name @@ -0,0 +1 @@ +ame_upgrade_script \ No newline at end of file diff --git a/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/encodings.xml b/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/indexLayout.xml b/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/projectSettingsUpdater.xml b/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/projectSettingsUpdater.xml new file mode 100644 index 0000000..4bb9f4d --- /dev/null +++ b/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/projectSettingsUpdater.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/workspace.xml b/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/workspace.xml new file mode 100644 index 0000000..a6b7f4a --- /dev/null +++ b/src/ame-upgrade-script/.idea/.idea.ame_upgrade_script/.idea/workspace.xml @@ -0,0 +1,67 @@ + + + + ame-upgrade-preparation-tool/ame-upgrade-preparation-tool.csproj + + + + + + + + + + + + + + + + + + + 1680046494161 + + + + + + + + + \ No newline at end of file diff --git a/src/ame-upgrade-script/ConsoleTUI.cs b/src/ame-upgrade-script/ConsoleTUI.cs new file mode 100644 index 0000000..80d9ca6 --- /dev/null +++ b/src/ame-upgrade-script/ConsoleTUI.cs @@ -0,0 +1,289 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using Microsoft.Win32; + +namespace Ameliorated.ConsoleUtils +{ + public static partial class ConsoleTUI + { + private const int MF_BYCOMMAND = 0x00000000; + private const int SC_CLOSE = 0xF060; + private const int SC_MINIMIZE = 0xF020; + private const int SC_MAXIMIZE = 0xF030; + private const int SC_SIZE = 0xF000; //resize + + private const uint CHECK_QUICK_EDIT = 0x0040; + private const int ENABLE_QUICK_EDIT = 0x40 | 0x80; + + // STD_INPUT_HANDLE (DWORD): -10 is the standard input device. + private const int STD_INPUT_HANDLE = -10; + private static string PreviousTitle; + private static int PreviousBufferHeight = 26; + private static int PreviousBufferWidth = 80; + private static int PreviousSizeHeight = 26; + private static int PreviousSizeWidth = 80; + + private static bool IsInitialized; + private static int InitializedWidth = 80; + + public static void ShowErrorBox(string message, string caption) + { + NativeWindow window = new NativeWindow(); + window.AssignHandle(Process.GetCurrentProcess().MainWindowHandle); + MessageBox.Show(window, message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + public enum BackdropType + { + None = 1, + Mica = 2, + Acrylic = 3, + Tabbed = 4 + } + public static readonly int WinVer = Int32.Parse(Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue("CurrentBuildNumber").ToString()); + [DllImport("dwmapi.dll")] + private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int pvAttribute, int cbAttribute); + + public static void Initialize(string title, int width = 80, int height = 26, bool resize = false, bool quickedit = false) + { + if (width < 2) throw new ArgumentException("Width must be greater than one."); + + IsInitialized = true; + PreviousSizeHeight = Console.WindowHeight; + PreviousSizeWidth = Console.WindowWidth; + PreviousBufferHeight = Console.BufferHeight; + PreviousBufferWidth = Console.BufferWidth; + + Console.SetWindowSize(width, height); + Console.SetBufferSize(width, height); + Console.SetWindowSize(width, height); + + InitializedWidth = width; + + Console.Clear(); + + Console.CursorVisible = false; + + PreviousTitle = Console.Title; + Console.Title = title; + + try + { + if ((Console.CursorLeft == 0 && Console.CursorTop == 0) || ParentProcess.ProcessName.Equals("Explorer", StringComparison.OrdinalIgnoreCase)) + { + var bd = (int)BackdropType.Mica; + var trueA = 0x01; + + if (WinVer >= 22523) + { + var handle = Process.GetCurrentProcess().MainWindowHandle; + DwmSetWindowAttribute(handle, 38, ref bd, Marshal.SizeOf()); + DwmSetWindowAttribute(handle, 20, ref trueA, Marshal.SizeOf()); + } + } + } catch (Exception e) { } + + if (!resize) + try + { + DisableResize(); + } catch (Exception e) + { + //ConsoleUtils.WriteError("Error disabling window resize - " + e.Message); + } + + if (!quickedit) + try + { + DisableQuickEdit(); + } catch (Exception e) + { + //ConsoleUtils.WriteError("Error disabling quickedit - " + e.Message); + } + } + + public static void Close() + { + if (!IsInitialized) throw new MethodAccessException("Console TUI must be initialized before calling other TUI functions."); + IsInitialized = false; + + var parent = ParentProcess.ProcessName; + if (parent.Equals("Explorer", StringComparison.CurrentCultureIgnoreCase)) return; + + try + { + EnableResize(); + } catch (Exception e) + { + //ConsoleUtils.WriteError("Error enabling window resize - " + e.Message); + } + + try + { + EnableQuickEdit(); + } catch (Exception e) + { + //ConsoleUtils.WriteError("Error enabling quickedit - " + e.Message); + } + + Console.CursorVisible = true; + Console.Clear(); + Console.Title = PreviousTitle; + + Console.SetWindowSize(PreviousSizeWidth, PreviousSizeHeight); + Console.SetBufferSize(PreviousBufferWidth, PreviousBufferHeight); + } + + [DllImport("user32.dll")] + public static extern int DeleteMenu(IntPtr hMenu, int nPosition, int wFlags); + + [DllImport("user32.dll")] + private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); + + [DllImport("kernel32.dll", ExactSpelling = true)] + private static extern IntPtr GetConsoleWindow(); + + private static void DisableResize() + { + var handle = GetConsoleWindow(); + var sysMenu = GetSystemMenu(handle, false); + + if (handle != IntPtr.Zero) + { + //DeleteMenu(sysMenu, SC_CLOSE, MF_BYCOMMAND); + //DeleteMenu(sysMenu, SC_MINIMIZE, MF_BYCOMMAND); + DeleteMenu(sysMenu, SC_MAXIMIZE, MF_BYCOMMAND); + DeleteMenu(sysMenu, SC_SIZE, MF_BYCOMMAND); //resize + } + } + + private static void EnableResize() + { + var handle = GetConsoleWindow(); + GetSystemMenu(handle, true); + } + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr GetStdHandle(int nStdHandle); + + [DllImport("kernel32.dll")] + private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); + + [DllImport("kernel32.dll")] + private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); + + private static void DisableQuickEdit() + { + var consoleHandle = GetStdHandle(STD_INPUT_HANDLE); + + // get current console mode + uint consoleMode; + GetConsoleMode(consoleHandle, out consoleMode); + + // set the new mode + SetConsoleMode(consoleHandle, consoleMode &= ~CHECK_QUICK_EDIT); + } + + private static void EnableQuickEdit() + { + var consoleHandle = GetStdHandle(STD_INPUT_HANDLE); + + // get current console mode + uint consoleMode; + GetConsoleMode(consoleHandle, out consoleMode); + + // set the new mode + SetConsoleMode(consoleHandle, consoleMode | ENABLE_QUICK_EDIT); + } + } + + + + public class ChoicePrompt : Prompt + { + public string Choices { get; set; } = "YN"; + public bool BeepSound { get; set; } = true; + public bool CaseSensitive { get; set; } = false; + public bool AllowEscape { get; set; } = true; + public bool AnyKey { get; set; } = false; + + public ConsoleColor? TextForeground { get; set; } + + private bool _bindToOpenFrame; + + + public new int? Start() + { + if (Choices.Length < 1 && !AnyKey) throw new ArgumentException("There must be at least 1 choice."); + + Console.Write(Text); + + var cursorVisibility = Console.CursorVisible; + int? result; + + while (true) + { + Console.CursorVisible = true; + var key = Console.ReadKey(true); + if (AnyKey) + { + Console.CursorVisible = cursorVisibility; + return key.KeyChar; + } + + if (key.Key == ConsoleKey.Escape && AllowEscape) + { + Console.CursorVisible = cursorVisibility; + return null; + } + + if (CaseSensitive) + result = Choices.IndexOf(key.KeyChar.ToString(), StringComparison.Ordinal); + else + result = Choices.IndexOf(key.KeyChar.ToString(), StringComparison.OrdinalIgnoreCase); + + if (result >= 0) + { + + + if (!CaseSensitive) Console.Write(key.KeyChar.ToString().ToUpper()); + else Console.Write(key.KeyChar.ToString()); + + break; + } + else if (BeepSound) Console.Beep(); + } + + Console.CursorVisible = cursorVisibility; + Console.WriteLine(); + + return result.Value; + } + } + + + public abstract class Prompt + { + /// + /// Text to be displayed before the input. + /// + + public string Text { get; set; } = ""; + + public int? MaxLength { get; set; } + + /// + /// (Optional) + /// + + public ConsoleColor? InputForeground { get; set; } = null; + + public ConsoleColor? InputBackground { get; set; } = null; + + public bool BindToOpenFrame { get; set; } = true; + public bool AllowEscape { get; set; } = true; + + } +} \ No newline at end of file diff --git a/src/ame-upgrade-script/NSudo.cs b/src/ame-upgrade-script/NSudo.cs new file mode 100644 index 0000000..0c51c2a --- /dev/null +++ b/src/ame-upgrade-script/NSudo.cs @@ -0,0 +1,510 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Principal; +using System.Threading; +using Microsoft.Win32.SafeHandles; + +namespace amecs +{ + [StructLayout(LayoutKind.Sequential)] + + public class NSudo + { + private struct SECURITY_ATTRIBUTES + { + public int nLength; + public unsafe byte* lpSecurityDescriptor; + public int bInheritHandle; + } + private enum SECURITY_IMPERSONATION_LEVEL + { + SecurityAnonymous, + SecurityIdentification, + SecurityImpersonation, + SecurityDelegation + } + private enum TOKEN_TYPE { + TokenPrimary = 1, + TokenImpersonation + } + + [StructLayout(LayoutKind.Sequential)] + private struct LUID { + public uint LowPart; + public uint HighPart; + } + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct LUID_AND_ATTRIBUTES { + public LUID Luid; + public UInt32 Attributes; + } + + private struct TOKEN_PRIVILEGES { + public int PrivilegeCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst=1)] + public LUID_AND_ATTRIBUTES[] Privileges; + } + + + private static UInt32 MAXIMUM_ALLOWED = (UInt32)TokenAccessLevels.MaximumAllowed; + + [DllImport("advapi32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool OpenProcessToken(IntPtr ProcessHandle, + UInt32 DesiredAccess, out IntPtr TokenHandle); + + [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] + private static extern bool DuplicateTokenEx( + IntPtr hExistingToken, + uint dwDesiredAccess, + IntPtr lpTokenAttributes, + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, + TOKEN_TYPE TokenType, + out IntPtr phNewToken ); + [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] + private static extern bool DuplicateTokenEx( + IntPtr hExistingToken, + uint dwDesiredAccess, + ref SECURITY_ATTRIBUTES lpTokenAttributes, + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, + TOKEN_TYPE TokenType, + out IntPtr phNewToken ); + [DllImport("advapi32.dll")] + static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpName, + ref LUID lpLuid); + internal const int SE_PRIVILEGE_ENABLED = 0x00000002; + + // Use this signature if you do not want the previous state + [DllImport("advapi32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, + [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges, + ref TOKEN_PRIVILEGES NewState, + UInt32 Zero, + IntPtr Null1, + IntPtr Null2); + + [System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)] + private static extern bool SetThreadToken(IntPtr pHandle, + IntPtr hToken); + + + + + [DllImport("wtsapi32.dll", SetLastError=true)] + static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token); + + [DllImport("advapi32.dll", SetLastError = true)] + static extern Boolean SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, + ref UInt32 TokenInformation, UInt32 TokenInformationLength); + + + [DllImport("userenv.dll", SetLastError=true)] + static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit ); + public static bool GetUserPrivilege(IntPtr Token) + { + IntPtr NewToken; + DuplicateTokenEx(Token, MAXIMUM_ALLOWED, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenImpersonation, out NewToken); + SetThreadToken(IntPtr.Zero, NewToken); + return true; + } + + [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)] + static extern bool CreateProcessAsUser( + IntPtr hToken, + string lpApplicationName, + string lpCommandLine, + ref SECURITY_ATTRIBUTES lpProcessAttributes, + ref SECURITY_ATTRIBUTES lpThreadAttributes, + bool bInheritHandles, + uint dwCreationFlags, + IntPtr lpEnvironment, + string lpCurrentDirectory, + ref STARTUPINFO lpStartupInfo, + out PROCESS_INFORMATION lpProcessInformation); + + [Flags] + enum CreationFlags + { + CREATE_SUSPENDED = 0x00000004, + CREATE_UNICODE_ENVIRONMENT = 0x00000400, + CREATE_NO_WINDOW = 0x08000000, + CREATE_NEW_CONSOLE = 0x00000010 + } + [DllImport("advapi32.dll", SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool LogonUser( + [MarshalAs(UnmanagedType.LPStr)] string pszUserName, + [MarshalAs(UnmanagedType.LPStr)] string pszDomain, + [MarshalAs(UnmanagedType.LPStr)] string pszPassword, + int dwLogonType, + int dwLogonProvider, + ref IntPtr phToken); + + public static int? RunProcessAsUser(IntPtr Token, string Executable, string Arguments, uint timeout = 0xFFFFFFFF) + { + GetAssignPrivilege(); + GetQuotaPrivilege(); + + var startupInfo = new STARTUPINFO(); + startupInfo.cb = Marshal.SizeOf(startupInfo); + startupInfo.dwFlags = 0x00000001; + startupInfo.wShowWindow = 1; + + + 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 hUserTokenDuplicate = IntPtr.Zero; + var pEnvironmentBlock = IntPtr.Zero; + + DuplicateTokenEx(Token, MAXIMUM_ALLOWED, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, out hUserTokenDuplicate); + + CreateEnvironmentBlock(out pEnvironmentBlock, Token, false); + + PROCESS_INFORMATION _processInfo; + if (!CreateProcessAsUser(hUserTokenDuplicate, null, String.IsNullOrEmpty(Arguments) ? $"\"{Executable}\"" : $"\"{Executable}\" {Arguments}", + ref procAttrs, ref threadAttrs, false, (uint)CreationFlags.CREATE_NO_WINDOW | + (uint)CreationFlags.CREATE_UNICODE_ENVIRONMENT, + pEnvironmentBlock, null, ref startupInfo, out _processInfo)) return null; + + + + return 0; + /* + uint dwCreationFlags = (uint)CreationFlags.CREATE_UNICODE_ENVIRONMENT; + + + startupInfo.cb = Marshal.SizeOf(startupInfo); + + + + SECURITY_ATTRIBUTES throwaway = new SECURITY_ATTRIBUTES(); + SECURITY_ATTRIBUTES throwaway2 = new SECURITY_ATTRIBUTES(); + Console.WriteLine(Marshal.GetLastWin32Error() + "-3"); + + Console.WriteLine(CreateProcessAsUser(hUserToken, String.Empty, "\"C:\\Windows\\notepad.exe\"", ref throwaway, ref throwaway2, false, 0, IntPtr.Zero, String.Empty, ref StartupInfo, out ProcessInfo)); + Console.WriteLine(Marshal.GetLastWin32Error() + "-4"); + + return Process.GetProcessById(ProcessInfo.dwProcessId); + */ + } + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode); + [DllImport("kernel32.dll", SetLastError=true)] + static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct STARTUPINFO + { + public Int32 cb; + public IntPtr lpReserved; + + public IntPtr lpDesktop; + public IntPtr lpTitle; + public Int32 dwX; + public Int32 dwY; + public Int32 dwXSize; + public Int32 dwYSize; + public Int32 dwXCountChars; + public Int32 dwYCountChars; + public Int32 dwFillAttribute; + public Int32 dwFlags; + public Int16 wShowWindow; + public Int16 cbReserved2; + public IntPtr lpReserved2; + public IntPtr hStdInput; + public IntPtr hStdOutput; + public IntPtr hStdError; + } + [StructLayout(LayoutKind.Sequential)] + internal struct PROCESS_INFORMATION + { + public IntPtr hProcess; + public IntPtr hThread; + public int dwProcessId; + public int dwThreadId; + } + public static IntPtr GetUserToken() + { + IntPtr Token; + + WTSQueryUserToken((uint)SessionID, out Token); + return Token; + } + + private static int SessionID = -1; + public static bool GetSystemPrivilege() + { + IntPtr CurrentProcessToken; + OpenProcessToken(Process.GetCurrentProcess().Handle, MAXIMUM_ALLOWED, out CurrentProcessToken); + IntPtr DuplicatedCurrentProcessToken; + DuplicateTokenEx(CurrentProcessToken, MAXIMUM_ALLOWED, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenImpersonation, out DuplicatedCurrentProcessToken); + LUID_AND_ATTRIBUTES RawPrivilege = new LUID_AND_ATTRIBUTES(); + LookupPrivilegeValue(IntPtr.Zero, "SeDebugPrivilege", ref RawPrivilege.Luid); + RawPrivilege.Attributes = SE_PRIVILEGE_ENABLED; + + TOKEN_PRIVILEGES TokenPrivilege = new TOKEN_PRIVILEGES(); + TokenPrivilege.Privileges = new LUID_AND_ATTRIBUTES[] { RawPrivilege }; + TokenPrivilege.PrivilegeCount = 1; + AdjustTokenPrivileges(DuplicatedCurrentProcessToken, false, ref TokenPrivilege, 0, IntPtr.Zero, IntPtr.Zero); + + SetThreadToken(IntPtr.Zero, DuplicatedCurrentProcessToken); + + SessionID = GetActiveSession(); + + IntPtr OriginalProcessToken = new IntPtr(-1); + CreateSystemToken((int)MAXIMUM_ALLOWED, SessionID, ref OriginalProcessToken); + + IntPtr SystemToken; + DuplicateTokenEx(OriginalProcessToken, MAXIMUM_ALLOWED, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenImpersonation, out SystemToken); + + SetThreadToken(IntPtr.Zero, SystemToken); + + return true; + } + + [DllImport("advapi32.dll", SetLastError=true)] + static extern bool GetTokenInformation( + IntPtr TokenHandle, + TOKEN_INFORMATION_CLASS TokenInformationClass, + IntPtr TokenInformation, + int TokenInformationLength, + out int ReturnLength); + + enum TOKEN_INFORMATION_CLASS + { + TokenUser = 1, + TokenGroups, + TokenPrivileges, + TokenOwner, + TokenPrimaryGroup, + TokenDefaultDacl, + TokenSource, + TokenType, + TokenImpersonationLevel, + TokenStatistics, + TokenRestrictedSids, + TokenSessionId, + TokenGroupsAndPrivileges, + TokenSessionReference, + TokenSandBoxInert, + TokenAuditPolicy, + TokenOrigin + } + + private static int GetActiveSession() + { + IntPtr pSessionInfo = IntPtr.Zero; + Int32 Count = 0; + var retval = WTSEnumerateSessions((IntPtr)null, 0, 1, ref pSessionInfo, ref Count); + Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO)); + + Int64 current = (Int64)pSessionInfo; + + int result = -1; + if (retval != 0) + { + for (int i = 0; i < Count; i++) + { + WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO)); + current += dataSize; + + if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive) + { + result = si.SessionID; + break; + } + } + WTSFreeMemory(pSessionInfo); + } + + return result; + } + + private static void CreateSystemToken(int DesiredAccess, int dwSessionID, ref IntPtr TokenHandle) + { + int dwLsassPID = -1; + int dwWinLogonPID = -1; + WTS_PROCESS_INFO[] pProcesses; + IntPtr pProcessInfo = IntPtr.Zero; + + int dwProcessCount = 0; + + if (WTSEnumerateProcesses((IntPtr)null, 0, 1, ref pProcessInfo, ref dwProcessCount)) + { + IntPtr pMemory = pProcessInfo; + pProcesses = new WTS_PROCESS_INFO[dwProcessCount]; + + for (int i = 0; i < dwProcessCount; i++) + { + pProcesses[i] = (WTS_PROCESS_INFO)Marshal.PtrToStructure(pProcessInfo, typeof(WTS_PROCESS_INFO)); + pProcessInfo = (IntPtr)((long)pProcessInfo + Marshal.SizeOf(pProcesses[i])); + + var processName = Marshal.PtrToStringAnsi(pProcesses[i].ProcessName); + ConvertSidToStringSid(pProcesses[i].UserSid, out string sid); + + string strSid; + + if (processName == null || pProcesses[i].UserSid == default || sid != "S-1-5-18") + continue; + + if ((-1 == dwLsassPID) && (0 == pProcesses[i].SessionID) && (processName == "lsass.exe")) + { + dwLsassPID = pProcesses[i].ProcessID; + continue; + } + + if ((-1 == dwWinLogonPID) && (dwSessionID == pProcesses[i].SessionID) && (processName == "winlogon.exe")) + { + dwWinLogonPID = pProcesses[i].ProcessID; + continue; + } + } + + WTSFreeMemory(pMemory); + } + + bool Result = false; + + IntPtr SystemProcessHandle = IntPtr.Zero; + + + try + { + SystemProcessHandle = Process.GetProcessById(dwLsassPID).Handle; + } catch + { + SystemProcessHandle = Process.GetProcessById(dwWinLogonPID).Handle; + } + IntPtr SystemTokenHandle = IntPtr.Zero; + if (OpenProcessToken(SystemProcessHandle, TOKEN_DUPLICATE, out SystemTokenHandle)) + { + Result = DuplicateTokenEx(SystemTokenHandle, (uint)DesiredAccess, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, out TokenHandle); + CloseHandle(SystemTokenHandle); + } + + CloseHandle(SystemProcessHandle); + + // return Result; + return; + } + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr OpenProcess( + uint processAccess, + bool bInheritHandle, + uint processId + ); + public const UInt32 TOKEN_DUPLICATE = 0x0002; + + [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)] + static extern bool ConvertSidToStringSid(IntPtr pSid, out string strSid); + + + [DllImport("kernel32.dll", SetLastError=true)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool CloseHandle(IntPtr hObject); + + [DllImport("wtsapi32.dll", SetLastError=true)] + static extern int WTSEnumerateSessions( + System.IntPtr hServer, + int Reserved, + int Version, + ref System.IntPtr ppSessionInfo, + ref int pCount); + + + [StructLayout(LayoutKind.Sequential)] + private struct WTS_SESSION_INFO + { + public Int32 SessionID; + + [MarshalAs(UnmanagedType.LPStr)] + public String pWinStationName; + + public WTS_CONNECTSTATE_CLASS State; + } + public enum WTS_CONNECTSTATE_CLASS + { + WTSActive, + WTSConnected, + WTSConnectQuery, + WTSShadow, + WTSDisconnected, + WTSIdle, + WTSListen, + WTSReset, + WTSDown, + WTSInit + } + [DllImport("wtsapi32.dll")] + static extern void WTSFreeMemory(IntPtr pMemory); + + [DllImport("wtsapi32.dll", SetLastError=true)] + static extern bool WTSEnumerateProcesses( + IntPtr serverHandle, // Handle to a terminal server. + Int32 reserved, // must be 0 + Int32 version, // must be 1 + ref IntPtr ppProcessInfo, // pointer to array of WTS_PROCESS_INFO + ref Int32 pCount // pointer to number of processes + ); + struct WTS_PROCESS_INFO + { + public int SessionID; + public int ProcessID; + //This is a pointer to string... + public IntPtr ProcessName; + public IntPtr UserSid; + } + + + + [DllImport("ntdll.dll", SetLastError = true)] + static extern IntPtr RtlAdjustPrivilege(int Privilege, bool bEnablePrivilege, bool IsThreadPrivilege, out bool PreviousValue); + [DllImport("advapi32.dll")] + static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpName, ref UInt64 lpLuid); + public static void GetOwnershipPrivilege() + { + ulong luid = 0; + bool throwaway; + LookupPrivilegeValue(IntPtr.Zero, "SeTakeOwnershipPrivilege", ref luid); + RtlAdjustPrivilege((int)luid, true, true, out throwaway); + } + public static void GetAssignPrivilege() + { + ulong luid = 0; + bool throwaway; + LookupPrivilegeValue(IntPtr.Zero, "SeAssignPrimaryTokenPrivilege", ref luid); + RtlAdjustPrivilege((int)luid, true, true, out throwaway); + } + public static void GetQuotaPrivilege() + { + ulong luid = 0; + bool throwaway; + LookupPrivilegeValue(IntPtr.Zero, "SeIncreaseQuotaPrivilege", ref luid); + RtlAdjustPrivilege((int)luid, true, true, out throwaway); + } + + public static void GetShutdownPrivilege() + { + ulong luid = 0; + bool throwaway; + LookupPrivilegeValue(IntPtr.Zero, "SeShutdownPrivilege", ref luid); + RtlAdjustPrivilege((int)luid, true, true, out throwaway); + } + + public class Win32 { + + } + } +} \ No newline at end of file diff --git a/src/ame-upgrade-script/ParentProcess.cs b/src/ame-upgrade-script/ParentProcess.cs new file mode 100644 index 0000000..b9cbe29 --- /dev/null +++ b/src/ame-upgrade-script/ParentProcess.cs @@ -0,0 +1,74 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Ameliorated.ConsoleUtils +{ + public static class ParentProcess + { + private static readonly uint TH32CS_SNAPPROCESS = 2; + + public static string ProcessName = Get().ProcessName; + + public static Process Get() + { + try + { + var iParentPid = 0; + var iCurrentPid = Process.GetCurrentProcess().Id; + + var oHnd = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if (oHnd == IntPtr.Zero) + return null; + + var oProcInfo = new PROCESSENTRY32(); + + oProcInfo.dwSize = + (uint)Marshal.SizeOf(typeof(PROCESSENTRY32)); + + if (Process32First(oHnd, ref oProcInfo) == false) + return null; + + do + { + if (iCurrentPid == oProcInfo.th32ProcessID) + iParentPid = (int)oProcInfo.th32ParentProcessID; + } while (iParentPid == 0 && Process32Next(oHnd, ref oProcInfo)); + + if (iParentPid > 0) + return Process.GetProcessById(iParentPid); + return null; + } catch (Exception e) + { + return null; + } + } + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID); + + [DllImport("kernel32.dll")] + private static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32 lppe); + + [DllImport("kernel32.dll")] + private static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32 lppe); + + [StructLayout(LayoutKind.Sequential)] + public struct PROCESSENTRY32 + { + public uint dwSize; + public uint cntUsage; + public uint th32ProcessID; + public IntPtr th32DefaultHeapID; + public uint th32ModuleID; + public uint cntThreads; + public uint th32ParentProcessID; + public int pcPriClassBase; + public uint dwFlags; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szExeFile; + } + } +} \ No newline at end of file diff --git a/src/ame-upgrade-script/Program.cs b/src/ame-upgrade-script/Program.cs new file mode 100644 index 0000000..6504bda --- /dev/null +++ b/src/ame-upgrade-script/Program.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.AccessControl; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using amecs; +using Ameliorated.ConsoleUtils; +using Microsoft.Win32; + +namespace ame_upgrade_preparation_tool +{ + internal class Program + { + private static void RunAsUser(Action action) + { + var token = NSudo.GetUserToken(); + Task.Run((Action)Delegate.Combine((Action)(() => { NSudo.GetUserPrivilege(token); }), + action)).Wait(); + Marshal.FreeHGlobal(token); + } + public static async Task Main(string[] args) + { + ConsoleTUI.Initialize("AME Upgrade Script"); + + Console.WriteLine("\r\nChecking drives..."); + + var drives = DriveInfo.GetDrives(); + + List driveLetters = new List(); + foreach (var drive in drives) + { + try + { + if (Directory.GetFiles(drive.RootDirectory.FullName).Contains(Path.Combine(drive.RootDirectory.FullName, "setup.exe")) && Directory.GetDirectories(drive.RootDirectory.FullName).Contains(Path.Combine(drive.RootDirectory.FullName, "sources"))) + { + try + { + string extension = Directory.GetFiles(Path.Combine(drive.RootDirectory.FullName, "sources")).Contains(Path.Combine(drive.RootDirectory.FullName, "sources", "install.wim")) ? ".wim" : Directory.GetFiles(Path.Combine(drive.RootDirectory.FullName, "sources")).Contains(Path.Combine(drive.RootDirectory.FullName, "sources", "install.esd")) ? ".esd" : null; + + if (string.IsNullOrEmpty(extension)) + continue; + /* + var startInfo = new ProcessStartInfo + { + CreateNoWindow = true, + UseShellExecute = false, + WindowStyle = ProcessWindowStyle.Normal, + RedirectStandardError = true, + RedirectStandardOutput = true, + FileName = "DISM", + Arguments = "/Get-WimInfo /WimFile:\"" + Path.Combine(drive.RootDirectory.FullName, "sources", "install" + extension) + '"' + }; + + var exeProcess = new Process + { + StartInfo = startInfo, + EnableRaisingEvents = true + }; + string versionString = null; + bool Win11 = false; + exeProcess.OutputDataReceived += delegate(object sender, DataReceivedEventArgs outLine) + { + if (!String.IsNullOrEmpty(outLine.Data)) + { + var outputString = outLine.Data; + if (outputString.Contains("Version: ")) + { + versionString = outputString.Substring(outputString.IndexOf(' ') + 1); + } + else if (outputString.Contains("Windows 11")) + { + Win11 = true; + } + } + }; + exeProcess.Start(); + + exeProcess.BeginOutputReadLine(); + bool exited = exeProcess.WaitForExit(5000); + + exeProcess.CancelOutputRead(); + + if (versionString == null) + { + await Task.Delay(500); + } + */ + driveLetters.Add(drive.RootDirectory.FullName); + } catch (Exception e) + { + driveLetters.Add(drive.RootDirectory.FullName); + } + } + } catch + { + + } + } + + if (driveLetters.Count < 1) + { + Console.WriteLine("No mounted Windows ISO was detected.\r\n"); + new ChoicePrompt() { Text = "Press any key to Exit...", AnyKey = true}.Start(); + Environment.Exit(0); + } + + if (driveLetters.Count > 1) + { + Console.WriteLine("Multiple ISOs detected, please dismount one.\r\n"); + new ChoicePrompt() { Text = "Press any key to Exit...", AnyKey = true}.Start(); + Environment.Exit(0); + } + + var choice = new ChoicePrompt() { AllowEscape = false, Text = "\r\nThis will cause partial de-amelioration. Continue? (Y/N): " }.Start(); + + if (choice.Value == 2) + Environment.Exit(0); + + string Username = null; + string UserDomain = null; + string UserSID = null; + try + { + NSudo.GetSystemPrivilege(); + + RunAsUser(() => + { + Username = WindowsIdentity.GetCurrent().Name.Split('\\').Last(); + UserDomain = WindowsIdentity.GetCurrent().Name.Split('\\').FirstOrDefault(); + UserSID = WindowsIdentity.GetCurrent().User.ToString(); + }); + } catch (Exception e) + { + } + + bool isSystem = WindowsIdentity.GetCurrent().IsSystem; + + try + { + string ID = null; + + var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"); + foreach (var item in key.GetSubKeyNames()) + { + try + { + if (((string)key.OpenSubKey(item).GetValue("DisplayName")).Equals("Open-Shell")) + { + ID = item; + } + } catch (Exception e) + { + + } + } + + if (ID != null) + { + Console.WriteLine("\r\nUninstalling Open-Shell..."); + + var proc = Process.Start("MsiExec.exe", $"/X{ID} /quiet"); + proc.WaitForExit(); + + if (UserSID != null) + { + try + { + var appData = (string)Registry.Users.OpenSubKey(UserSID + @"\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders").GetValue("AppData"); + + if (Directory.Exists(Path.Combine(appData, "OpenShell"))) + Directory.Delete(Path.Combine(appData, "OpenShell"), true); + } catch (Exception e) + { + Console.WriteLine("Error: " + e); + } + } + await Task.Delay(5000); + + if (!Process.GetProcessesByName("explorer").Any()) + { + if (isSystem) + NSudo.RunProcessAsUser(NSudo.GetUserToken(), "explorer.exe", ""); + else + Process.Start("explorer.exe"); + } + } + } catch (Exception e) + { + Console.WriteLine("Error: " + e.Message); + } + + + Console.WriteLine("\r\nReverting settings..."); + try + { + var polKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer", true); + if (polKey.GetValueNames().Contains("SettingsPageVisibility", StringComparer.InvariantCultureIgnoreCase)) + { + polKey.DeleteValue("SettingsPageVisibility"); + } + + if (UserSID != null) + { + new Reg.Value() { KeyName = @$"HKU\{UserSID}\SOFTWARE\Policies\Microsoft\Windows\Explorer", ValueName = "DisableNotificationCenter", Operation = Reg.RegistryValueOperation.Delete }.Apply(); + new Reg.Value() { KeyName = @$"HKU\{UserSID}\SOFTWARE\Classes\CLSID\{{27DD0F8B-3E0E-4ADC-A78A-66047E71ADC5}}\InprocServer32", ValueName = "", Type = Reg.RegistryValueType.REG_SZ, Data = ""}.Apply(); + } + } catch (Exception e) + { + Console.WriteLine("Error: " + e.Message); + } + + Thread.Sleep(3000); + Console.WriteLine("\r\nApplying preparation settings..."); + try + { + new Reg.Key() { KeyName = @"HKLM\SYSTEM\Setup\LabConfig", Operation = RegistryOperation.Add }.Apply(); + new Reg.Value() { KeyName = @"HKLM\SYSTEM\Setup\LabConfig", ValueName = "BypassTPMCheck", Type = Reg.RegistryValueType.REG_DWORD, Data = 1 }.Apply(); + new Reg.Value() { KeyName = @"HKLM\SYSTEM\Setup\LabConfig", ValueName = "BypassCPUCheck", Type = Reg.RegistryValueType.REG_DWORD, Data = 1 }.Apply(); + new Reg.Value() { KeyName = @"HKLM\SYSTEM\Setup\LabConfig", ValueName = "BypassStorageCheck", Type = Reg.RegistryValueType.REG_DWORD, Data = 1 }.Apply(); + new Reg.Value() { KeyName = @"HKLM\SYSTEM\Setup\LabConfig", ValueName = "BypassSecureBootCheck", Type = Reg.RegistryValueType.REG_DWORD, Data = 1 }.Apply(); + new Reg.Value() { KeyName = @"HKLM\SYSTEM\Setup\LabConfig", ValueName = "BypassRAMCheck", Type = Reg.RegistryValueType.REG_DWORD, Data = 1 }.Apply(); + new Reg.Key() { KeyName = @"HKLM\SYSTEM\Setup\MoSetup", Operation = RegistryOperation.Add }.Apply(); + new Reg.Value() { KeyName = @"HKLM\SYSTEM\Setup\MoSetup", ValueName = "AllowUpgradesWithUnsupportedTPMOrCPU", Type = Reg.RegistryValueType.REG_DWORD, Data = 1 }.Apply(); + } catch (Exception e) + { + Console.WriteLine("Error: " + e.Message); + } + + Thread.Sleep(1500); + + var fc = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("\r\nCompleted configuration"); + + Console.ForegroundColor = fc; + + Console.WriteLine("\r\nStarting Windows Setup..."); + try + { + Process.Start(Path.Combine(driveLetters.First(), "setup.exe"), "/Auto Upgrade /DynamicUpdate Disable"); + } catch (Exception e) + { + fc = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Error launching Windows Setup: " + e.Message); + Console.ForegroundColor = fc; + new ChoicePrompt() { Text = "Press any key to Exit...", AnyKey = true}.Start(); + Environment.Exit(0); + } + + fc = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("\r\nSetup launch successful, exiting..."); + + Thread.Sleep(6000); + } + } +} \ No newline at end of file diff --git a/src/ame-upgrade-script/Properties/AssemblyInfo.cs b/src/ame-upgrade-script/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a2c7742 --- /dev/null +++ b/src/ame-upgrade-script/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ame_upgrade_preparation_tool")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ame_upgrade_preparation_tool")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("DD855BAB-FA5B-458D-8F42-F2F5E7866D0E")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/src/ame-upgrade-script/Registry.cs b/src/ame-upgrade-script/Registry.cs new file mode 100644 index 0000000..52f6848 --- /dev/null +++ b/src/ame-upgrade-script/Registry.cs @@ -0,0 +1,375 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.Win32; + +namespace amecs +{ + public enum RegistryOperation + { + Delete = 0, + Add = 1 + } + + public class Reg + { + public class Key + { + public string KeyName { get; set; } + + //public Scope Scope { get; set; } = Scope.AllUsers; + public RegistryOperation Operation { get; set; } = RegistryOperation.Delete; + + private List GetRoots() + { + var hive = KeyName.Split('\\').GetValue(0).ToString().ToUpper(); + var list = new List(); + + list.Add(hive switch + { + "HKCU" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), + "HKEY_CURRENT_USER" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), + "HKLM" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), + "HKEY_LOCAL_MACHINE" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), + "HKCR" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), + "HKEY_CLASSES_ROOT" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), + "HKU" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), + "HKEY_USERS" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), + _ => throw new ArgumentException($"Key '{KeyName}' does not specify a valid registry hive.") + }); + return list; + } + + public string GetSubKey() => KeyName.Substring(KeyName.IndexOf('\\') + 1); + + public bool IsEqual() + { + try + { + var roots = GetRoots(); + + foreach (var _root in roots) + { + var root = _root; + var subKey = GetSubKey(); + var openedSubKey = root.OpenSubKey(subKey); + + if (Operation == RegistryOperation.Delete && openedSubKey != null) + { + return false; + } + + if (Operation == RegistryOperation.Add && openedSubKey == null) + { + return false; + } + } + } catch (Exception e) + { + return false; + } + + return true; + } + + public bool Apply() + { + var roots = GetRoots(); + + foreach (var _root in roots) + { + var root = _root; + var subKey = GetSubKey(); + var openedSubKey = root.OpenSubKey(subKey); + if (openedSubKey != null) openedSubKey.Close(); + + + if (Operation == RegistryOperation.Add && openedSubKey == null) + { + root.CreateSubKey(subKey)?.Close(); + } + + if (Operation == RegistryOperation.Delete) + { + root.DeleteSubKeyTree(subKey, false); + } + + root.Close(); + } + + return true; + } + } + + + public enum RegistryValueOperation + { + Delete = 0, + Add = 1, + + // This indicates to skip the action if the specified value does not already exist + Set = 2 + } + + public enum RegistryValueType + { + REG_SZ = RegistryValueKind.String, + REG_MULTI_SZ = RegistryValueKind.MultiString, + REG_EXPAND_SZ = RegistryValueKind.ExpandString, + REG_DWORD = RegistryValueKind.DWord, + REG_QWORD = RegistryValueKind.QWord, + REG_BINARY = RegistryValueKind.Binary, + REG_NONE = RegistryValueKind.None, + REG_UNKNOWN = RegistryValueKind.Unknown + } + + public class Value + { + public string KeyName { get; set; } + + public string ValueName { get; set; } = ""; + + public object? Data { get; set; } + + public RegistryValueType Type { get; set; } + + //public Scope Scope { get; set; } = Scope.AllUsers; + + public RegistryValueOperation Operation { get; set; } = RegistryValueOperation.Add; + + private List GetRoots() + { + var hive = KeyName.Split('\\').GetValue(0).ToString().ToUpper(); + var list = new List(); + + list.Add(hive switch + { + "HKCU" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), + "HKEY_CURRENT_USER" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), + "HKLM" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), + "HKEY_LOCAL_MACHINE" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), + "HKCR" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), + "HKEY_CLASSES_ROOT" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), + "HKU" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), + "HKEY_USERS" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), + _ => throw new ArgumentException($"Key '{KeyName}' does not specify a valid registry hive.") + }); + return list; + } + + public string GetSubKey() => KeyName.Substring(KeyName.IndexOf('\\') + 1); + + public object? GetCurrentValue(RegistryKey root) + { + var subkey = GetSubKey(); + return Registry.GetValue(root.Name + "\\" + subkey, ValueName, null); + } + + public static byte[] StringToByteArray(string hex) + { + return Enumerable.Range(0, hex.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) + .ToArray(); + } + + public bool IsEqual() + { + try + { + var roots = GetRoots(); + + foreach (var _root in roots) + { + var root = _root; + var subKey = GetSubKey(); + + var openedSubKey = root.OpenSubKey(subKey); + + if (openedSubKey == null && (Operation == RegistryValueOperation.Set || Operation == RegistryValueOperation.Delete)) + continue; + if (openedSubKey == null) return false; + + var value = openedSubKey.GetValue(ValueName); + + if (value == null) + { + if (Operation == RegistryValueOperation.Set || Operation == RegistryValueOperation.Delete) + continue; + + return false; + } + + if (Operation == RegistryValueOperation.Delete) return false; + + if (Data == null) return false; + + + bool matches; + try + { + matches = Type switch + { + RegistryValueType.REG_SZ => + Data.ToString() == value.ToString(), + RegistryValueType.REG_EXPAND_SZ => + // RegistryValueOptions.DoNotExpandEnvironmentNames above did not seem to work. + Environment.ExpandEnvironmentVariables(Data.ToString()) == value.ToString(), + RegistryValueType.REG_MULTI_SZ => + Data.ToString() == "" ? ((string[])value).SequenceEqual(new string[] { }) : ((string[])value).SequenceEqual(Data.ToString().Split(new string[] { "\\0" }, StringSplitOptions.None)), + RegistryValueType.REG_DWORD => + unchecked((int)Convert.ToUInt32(Data)) == (int)value, + RegistryValueType.REG_QWORD => + Convert.ToUInt64(Data) == (ulong)value, + RegistryValueType.REG_BINARY => + ((byte[])value).SequenceEqual(StringToByteArray(Data.ToString())), + RegistryValueType.REG_NONE => + ((byte[])value).SequenceEqual(new byte[0]), + RegistryValueType.REG_UNKNOWN => + Data.ToString() == value.ToString(), + _ => throw new ArgumentException("Impossible.") + }; + } catch (InvalidCastException) + { + matches = false; + } + + if (!matches) return false; + } + } catch (Exception e) + { + return false; + } + + return true; + } + + public bool Apply() + { + var roots = GetRoots(); + + foreach (var _root in roots) + { + var root = _root; + var subKey = GetSubKey(); + + if (GetCurrentValue(root) == Data) continue; + + var opened = root.OpenSubKey(subKey); + if (opened == null && Operation == RegistryValueOperation.Set) continue; + if (opened == null && Operation == RegistryValueOperation.Add) root.CreateSubKey(subKey)?.Close(); + if (opened != null) opened.Close(); + + if (Operation == RegistryValueOperation.Delete) + { + var key = root.OpenSubKey(subKey, true); + key?.DeleteValue(ValueName); + key?.Close(); + root.Close(); + continue; + } + + if (Type == RegistryValueType.REG_BINARY) + { + var data = StringToByteArray(Data.ToString()); + + Registry.SetValue(root.Name + "\\" + subKey, ValueName, data, (RegistryValueKind)Type); + } + else if (Type == RegistryValueType.REG_DWORD) + { + // DWORD values using the highest bit set fail without this, for example '2962489444'. + // See https://stackoverflow.com/questions/6608400/how-to-put-a-dword-in-the-registry-with-the-highest-bit-set; + var value = unchecked((int)Convert.ToUInt32(Data)); + Registry.SetValue(root.Name + "\\" + subKey, ValueName, value, (RegistryValueKind)Type); + } + else if (Type == RegistryValueType.REG_QWORD) + { + Registry.SetValue(root.Name + "\\" + subKey, ValueName, Convert.ToUInt64(Data), (RegistryValueKind)Type); + } + else if (Type == RegistryValueType.REG_NONE) + { + byte[] none = new byte[0]; + + Registry.SetValue(root.Name + "\\" + subKey, ValueName, none, (RegistryValueKind)Type); + } + else if (Type == RegistryValueType.REG_MULTI_SZ) + { + string[] data; + if (Data.ToString() == "") data = new string[] { }; + else data = Data.ToString().Split(new string[] { "\\0" }, StringSplitOptions.None); + + Registry.SetValue(root.Name + "\\" + subKey, ValueName, data, (RegistryValueKind)Type); + } + else + { + Registry.SetValue(root.Name + "\\" + subKey, ValueName, Data, (RegistryValueKind)Type); + } + + root.Close(); + } + + return true; + } + } + + + [DllImport("advapi32.dll", SetLastError = true)] + static extern int RegLoadKey(IntPtr hKey, string lpSubKey, string lpFile); + + [DllImport("advapi32.dll", SetLastError = true)] + static extern int RegSaveKey(IntPtr hKey, string lpFile, uint securityAttrPtr = 0); + + [DllImport("advapi32.dll", SetLastError = true)] + static extern int RegUnLoadKey(IntPtr hKey, string lpSubKey); + + [DllImport("ntdll.dll", SetLastError = true)] + static extern IntPtr RtlAdjustPrivilege(int Privilege, bool bEnablePrivilege, bool IsThreadPrivilege, out bool PreviousValue); + + [DllImport("advapi32.dll")] + static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref UInt64 lpLuid); + + [DllImport("advapi32.dll")] + static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpName, ref UInt64 lpLuid); + + public static void LoadDefaultUserHive() + { + var parentKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); + + IntPtr parentHandle = parentKey.Handle.DangerousGetHandle(); + AcquirePrivileges(); + RegLoadKey(parentHandle, "DefaultUserHive", Environment.ExpandEnvironmentVariables(@"%SYSTEMDRIVE%\Users\Default\NTUSER.dat")); + parentKey.Close(); + } + + public static void UnloadDefaultUserHive() + { + var parentKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); + AcquirePrivileges(); + RegUnLoadKey(parentKey.Handle.DangerousGetHandle(), "DefaultUserHive"); + parentKey.Close(); + } + + public static void AcquirePrivileges() + { + ulong luid = 0; + bool throwaway; + LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid); + RtlAdjustPrivilege((int)luid, true, true, out throwaway); + LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid); + RtlAdjustPrivilege((int)luid, true, true, out throwaway); + } + + public static void ReturnPrivileges() + { + ulong luid = 0; + bool throwaway; + LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid); + RtlAdjustPrivilege((int)luid, false, true, out throwaway); + LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid); + RtlAdjustPrivilege((int)luid, false, true, out throwaway); + } + } +} \ No newline at end of file diff --git a/src/ame-upgrade-script/ame-upgrade-script.csproj b/src/ame-upgrade-script/ame-upgrade-script.csproj new file mode 100644 index 0000000..7025678 --- /dev/null +++ b/src/ame-upgrade-script/ame-upgrade-script.csproj @@ -0,0 +1,70 @@ + + + + + Debug + AnyCPU + {DD855BAB-FA5B-458D-8F42-F2F5E7866D0E} + Exe + Properties + ame_upgrade_script + ame_upgrade_script + v4.7.2 + 512 + true + console.ico + app.manifest + 8 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x64 + embedded + true + bin\Release\ + TRACE + prompt + 4 + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ame-upgrade-script/app.manifest b/src/ame-upgrade-script/app.manifest new file mode 100644 index 0000000..6975dd0 --- /dev/null +++ b/src/ame-upgrade-script/app.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/ame-upgrade-script/bin/Debug/ame_upgrade_preparation_tool.exe b/src/ame-upgrade-script/bin/Debug/ame_upgrade_preparation_tool.exe new file mode 100644 index 0000000..775b519 Binary files /dev/null and b/src/ame-upgrade-script/bin/Debug/ame_upgrade_preparation_tool.exe differ diff --git a/src/ame-upgrade-script/bin/Debug/ame_upgrade_preparation_tool.pdb b/src/ame-upgrade-script/bin/Debug/ame_upgrade_preparation_tool.pdb new file mode 100644 index 0000000..9d17e96 Binary files /dev/null and b/src/ame-upgrade-script/bin/Debug/ame_upgrade_preparation_tool.pdb differ diff --git a/src/ame-upgrade-script/bin/Release/Capture.PNG b/src/ame-upgrade-script/bin/Release/Capture.PNG new file mode 100644 index 0000000..2318794 Binary files /dev/null and b/src/ame-upgrade-script/bin/Release/Capture.PNG differ diff --git a/src/ame-upgrade-script/bin/Release/ame_upgrade_script.exe b/src/ame-upgrade-script/bin/Release/ame_upgrade_script.exe new file mode 100644 index 0000000..35fa3c3 Binary files /dev/null and b/src/ame-upgrade-script/bin/Release/ame_upgrade_script.exe differ diff --git a/src/ame-upgrade-script/bin/Release/windows-setup-requirements.PNG b/src/ame-upgrade-script/bin/Release/windows-setup-requirements.PNG new file mode 100644 index 0000000..55842d0 Binary files /dev/null and b/src/ame-upgrade-script/bin/Release/windows-setup-requirements.PNG differ diff --git a/src/ame-upgrade-script/console.ico b/src/ame-upgrade-script/console.ico new file mode 100644 index 0000000..b8a8113 Binary files /dev/null and b/src/ame-upgrade-script/console.ico differ diff --git a/src/ame-upgrade-script/obj/Debug/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs b/src/ame-upgrade-script/obj/Debug/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs new file mode 100644 index 0000000..3871b18 --- /dev/null +++ b/src/ame-upgrade-script/obj/Debug/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] diff --git a/src/ame-upgrade-script/obj/Debug/ame-upgrade-preparation-tool.csproj.AssemblyReference.cache b/src/ame-upgrade-script/obj/Debug/ame-upgrade-preparation-tool.csproj.AssemblyReference.cache new file mode 100644 index 0000000..920169b Binary files /dev/null and b/src/ame-upgrade-script/obj/Debug/ame-upgrade-preparation-tool.csproj.AssemblyReference.cache differ diff --git a/src/ame-upgrade-script/obj/Debug/ame-upgrade-preparation-tool.csproj.CoreCompileInputs.cache b/src/ame-upgrade-script/obj/Debug/ame-upgrade-preparation-tool.csproj.CoreCompileInputs.cache new file mode 100644 index 0000000..e4e41ab --- /dev/null +++ b/src/ame-upgrade-script/obj/Debug/ame-upgrade-preparation-tool.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +0230e1a8c2cc18fc014551fc4191dbdbe4d55139 diff --git a/src/ame-upgrade-script/obj/Debug/ame-upgrade-preparation-tool.csproj.FileListAbsolute.txt b/src/ame-upgrade-script/obj/Debug/ame-upgrade-preparation-tool.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..243c493 --- /dev/null +++ b/src/ame-upgrade-script/obj/Debug/ame-upgrade-preparation-tool.csproj.FileListAbsolute.txt @@ -0,0 +1,7 @@ +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\bin\Debug\ame_upgrade_preparation_tool.exe +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\bin\Debug\ame_upgrade_preparation_tool.pdb +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\obj\Debug\ame-upgrade-preparation-tool.csproj.AssemblyReference.cache +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\obj\Debug\ame-upgrade-preparation-tool.csproj.SuggestedBindingRedirects.cache +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\obj\Debug\ame-upgrade-preparation-tool.csproj.CoreCompileInputs.cache +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\obj\Debug\ame_upgrade_preparation_tool.exe +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\obj\Debug\ame_upgrade_preparation_tool.pdb diff --git a/src/ame-upgrade-script/obj/Debug/ame-upgrade-preparation-tool.csproj.SuggestedBindingRedirects.cache b/src/ame-upgrade-script/obj/Debug/ame-upgrade-preparation-tool.csproj.SuggestedBindingRedirects.cache new file mode 100644 index 0000000..e69de29 diff --git a/src/ame-upgrade-script/obj/Debug/ame_upgrade_preparation_tool.exe b/src/ame-upgrade-script/obj/Debug/ame_upgrade_preparation_tool.exe new file mode 100644 index 0000000..775b519 Binary files /dev/null and b/src/ame-upgrade-script/obj/Debug/ame_upgrade_preparation_tool.exe differ diff --git a/src/ame-upgrade-script/obj/Debug/ame_upgrade_preparation_tool.pdb b/src/ame-upgrade-script/obj/Debug/ame_upgrade_preparation_tool.pdb new file mode 100644 index 0000000..9d17e96 Binary files /dev/null and b/src/ame-upgrade-script/obj/Debug/ame_upgrade_preparation_tool.pdb differ diff --git a/src/ame-upgrade-script/obj/Release/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs b/src/ame-upgrade-script/obj/Release/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs new file mode 100644 index 0000000..3871b18 --- /dev/null +++ b/src/ame-upgrade-script/obj/Release/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] diff --git a/src/ame-upgrade-script/obj/Release/ame-upgrade-preparation-tool.csproj.AssemblyReference.cache b/src/ame-upgrade-script/obj/Release/ame-upgrade-preparation-tool.csproj.AssemblyReference.cache new file mode 100644 index 0000000..920169b Binary files /dev/null and b/src/ame-upgrade-script/obj/Release/ame-upgrade-preparation-tool.csproj.AssemblyReference.cache differ diff --git a/src/ame-upgrade-script/obj/Release/ame-upgrade-preparation-tool.csproj.CoreCompileInputs.cache b/src/ame-upgrade-script/obj/Release/ame-upgrade-preparation-tool.csproj.CoreCompileInputs.cache new file mode 100644 index 0000000..47f20ac --- /dev/null +++ b/src/ame-upgrade-script/obj/Release/ame-upgrade-preparation-tool.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +d48447f2b70dbd1ba252db0dc53fc93e856d2017 diff --git a/src/ame-upgrade-script/obj/Release/ame-upgrade-preparation-tool.csproj.FileListAbsolute.txt b/src/ame-upgrade-script/obj/Release/ame-upgrade-preparation-tool.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..4fd519f --- /dev/null +++ b/src/ame-upgrade-script/obj/Release/ame-upgrade-preparation-tool.csproj.FileListAbsolute.txt @@ -0,0 +1,5 @@ +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\obj\Release\ame-upgrade-preparation-tool.csproj.AssemblyReference.cache +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\obj\Release\ame-upgrade-preparation-tool.csproj.SuggestedBindingRedirects.cache +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\obj\Release\ame-upgrade-preparation-tool.csproj.CoreCompileInputs.cache +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\bin\Release\ame_upgrade_script.exe +C:\Users\Styris\RiderProjects\ame-upgrade-preparation-tool\ame-upgrade-preparation-tool\obj\Release\ame_upgrade_script.exe diff --git a/src/ame-upgrade-script/obj/Release/ame-upgrade-preparation-tool.csproj.SuggestedBindingRedirects.cache b/src/ame-upgrade-script/obj/Release/ame-upgrade-preparation-tool.csproj.SuggestedBindingRedirects.cache new file mode 100644 index 0000000..e69de29 diff --git a/src/ame-upgrade-script/obj/Release/ame_upgrade_script.exe b/src/ame-upgrade-script/obj/Release/ame_upgrade_script.exe new file mode 100644 index 0000000..35fa3c3 Binary files /dev/null and b/src/ame-upgrade-script/obj/Release/ame_upgrade_script.exe differ diff --git a/src/ame_upgrade_script.sln b/src/ame_upgrade_script.sln new file mode 100644 index 0000000..8a19e28 --- /dev/null +++ b/src/ame_upgrade_script.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ame-upgrade-preparation-tool", "ame-upgrade-preparation-tool\ame-upgrade-preparation-tool.csproj", "{DD855BAB-FA5B-458D-8F42-F2F5E7866D0E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DD855BAB-FA5B-458D-8F42-F2F5E7866D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD855BAB-FA5B-458D-8F42-F2F5E7866D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD855BAB-FA5B-458D-8F42-F2F5E7866D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD855BAB-FA5B-458D-8F42-F2F5E7866D0E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal