using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Management.Automation;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.ServiceProcess;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using Microsoft.Win32;
using TrustedUninstaller.Shared.Actions;

namespace TrustedUninstaller.Shared
{
    using SLID = Guid; //SLID id declared as typedef GUID SLID; in slpublic.h 

    public static class WinUtil
    {
        public enum SHSTOCKICONID : uint
        {
            SIID_DOCNOASSOC = 0,
            SIID_DOCASSOC = 1,
            SIID_APPLICATION = 2,
            SIID_FOLDER = 3,
            SIID_FOLDEROPEN = 4,
            SIID_DRIVE525 = 5,
            SIID_DRIVE35 = 6,
            SIID_DRIVEREMOVE = 7,
            SIID_DRIVEFIXED = 8,
            SIID_DRIVENET = 9,
            SIID_DRIVENETDISABLED = 10,
            SIID_DRIVECD = 11,
            SIID_DRIVERAM = 12,
            SIID_WORLD = 13,
            SIID_SERVER = 15,
            SIID_PRINTER = 16,
            SIID_MYNETWORK = 17,
            SIID_FIND = 22,
            SIID_HELP = 23,
            SIID_SHARE = 28,
            SIID_LINK = 29,
            SIID_SLOWFILE = 30,
            SIID_RECYCLER = 31,
            SIID_RECYCLERFULL = 32,
            SIID_MEDIACDAUDIO = 40,
            SIID_LOCK = 47,
            SIID_AUTOLIST = 49,
            SIID_PRINTERNET = 50,
            SIID_SERVERSHARE = 51,
            SIID_PRINTERFAX = 52,
            SIID_PRINTERFAXNET = 53,
            SIID_PRINTERFILE = 54,
            SIID_STACK = 55,
            SIID_MEDIASVCD = 56,
            SIID_STUFFEDFOLDER = 57,
            SIID_DRIVEUNKNOWN = 58,
            SIID_DRIVEDVD = 59,
            SIID_MEDIADVD = 60,
            SIID_MEDIADVDRAM = 61,
            SIID_MEDIADVDRW = 62,
            SIID_MEDIADVDR = 63,
            SIID_MEDIADVDROM = 64,
            SIID_MEDIACDAUDIOPLUS = 65,
            SIID_MEDIACDRW = 66,
            SIID_MEDIACDR = 67,
            SIID_MEDIACDBURN = 68,
            SIID_MEDIABLANKCD = 69,
            SIID_MEDIACDROM = 70,
            SIID_AUDIOFILES = 71,
            SIID_IMAGEFILES = 72,
            SIID_VIDEOFILES = 73,
            SIID_MIXEDFILES = 74,
            SIID_FOLDERBACK = 75,
            SIID_FOLDERFRONT = 76,
            SIID_SHIELD = 77,
            SIID_WARNING = 78,
            SIID_INFO = 79,
            SIID_ERROR = 80,
            SIID_KEY = 81,
            SIID_SOFTWARE = 82,
            SIID_RENAME = 83,
            SIID_DELETE = 84,
            SIID_MEDIAAUDIODVD = 85,
            SIID_MEDIAMOVIEDVD = 86,
            SIID_MEDIAENHANCEDCD = 87,
            SIID_MEDIAENHANCEDDVD = 88,
            SIID_MEDIAHDDVD = 89,
            SIID_MEDIABLURAY = 90,
            SIID_MEDIAVCD = 91,
            SIID_MEDIADVDPLUSR = 92,
            SIID_MEDIADVDPLUSRW = 93,
            SIID_DESKTOPPC = 94,
            SIID_MOBILEPC = 95,
            SIID_USERS = 96,
            SIID_MEDIASMARTMEDIA = 97,
            SIID_MEDIACOMPACTFLASH = 98,
            SIID_DEVICECELLPHONE = 99,
            SIID_DEVICECAMERA = 100,
            SIID_DEVICEVIDEOCAMERA = 101,
            SIID_DEVICEAUDIOPLAYER = 102,
            SIID_NETWORKCONNECT = 103,
            SIID_INTERNET = 104,
            SIID_ZIPFILE = 105,
            SIID_SETTINGS = 106,
            SIID_DRIVEHDDVD = 132,
            SIID_DRIVEBD = 133,
            SIID_MEDIAHDDVDROM = 134,
            SIID_MEDIAHDDVDR = 135,
            SIID_MEDIAHDDVDRAM = 136,
            SIID_MEDIABDROM = 137,
            SIID_MEDIABDR = 138,
            SIID_MEDIABDRE = 139,
            SIID_CLUSTEREDDRIVE = 140,
            SIID_MAX_ICONS = 175
        }

        [Flags]
        public enum SHGSI : uint
        {
            SHGSI_ICONLOCATION = 0,
            SHGSI_ICON = 0x000000100,
            SHGSI_SYSICONINDEX = 0x000004000,
            SHGSI_LINKOVERLAY = 0x000008000,
            SHGSI_SELECTED = 0x000010000,
            SHGSI_LARGEICON = 0x000000000,
            SHGSI_SMALLICON = 0x000000001,
            SHGSI_SHELLICONSIZE = 0x000000004
        }

        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct SHSTOCKICONINFO
        {
            public UInt32 cbSize;
            public IntPtr hIcon;
            public Int32 iSysIconIndex;
            public Int32 iIcon;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260 /*MAX_PATH*/)]
            public string szPath;
        }

        [DllImport("Shell32.dll", SetLastError = false)]
        public static extern Int32 SHGetStockIconInfo(SHSTOCKICONID siid, SHGSI uFlags, ref SHSTOCKICONINFO psii);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool DestroyIcon(IntPtr hIcon);


        public static bool IsAdministrator()
        {
            using var identity = WindowsIdentity.GetCurrent();
            var principal = new WindowsPrincipal(identity);
            return principal.IsInRole(WindowsBuiltInRole.Administrator);
        }

        public static string GetUserName()
        {
            var wi = WindowsIdentity.GetCurrent();
            var groups = from g in wi.Groups
                select new SecurityIdentifier(g.Value)
                    .Translate(typeof(NTAccount)).Value;
            var msAccount = (from g in groups
                where g.StartsWith(@"MicrosoftAccount\")
                select g).FirstOrDefault();
            return msAccount == null ? Environment.UserName : msAccount.Substring(@"MicrosoftAccount\".Length);
        }

        public static bool IsLocalAccount()
        {
            var wi = WindowsIdentity.GetCurrent();
            var groups = from g in wi.Groups
                select new SecurityIdentifier(g.Value)
                    .Translate(typeof(NTAccount)).Value;
            var msAccount = (from g in groups
                where g.StartsWith(@"MicrosoftAccount\")
                select g).FirstOrDefault();
            return msAccount == null;
        }


        public enum SL_GENUINE_STATE
        {
            SL_GEN_STATE_IS_GENUINE = 0,

            // SL_GEN_STATE_INVALID_LICENSE = 1,
            // SL_GEN_STATE_TAMPERED = 2,
            SL_GEN_STATE_LAST = 3
        }

        [DllImport("Slwga.dll", EntryPoint = "SLIsGenuineLocal", CharSet = CharSet.None, ExactSpelling =
            false, SetLastError = false, PreserveSig = true, CallingConvention = CallingConvention.Winapi, BestFitMapping =
            false, ThrowOnUnmappableChar = false)]
        [PreserveSigAttribute()]
        internal static extern uint SLIsGenuineLocal(ref SLID slid, [In, Out] ref SL_GENUINE_STATE genuineState, IntPtr val3);

        public static bool IsGenuineWindows()
        {
            // Microsoft-Windows-Security-SPP GUID
            // http://technet.microsoft.com/en-us/library/dd772270.aspx
            var windowsSlid = new SLID("55c92734-d682-4d71-983e-d6ec3f16059f");
            var genuineState = SL_GENUINE_STATE.SL_GEN_STATE_LAST;
            var resultInt = SLIsGenuineLocal(ref windowsSlid, ref genuineState, IntPtr.Zero);
#if DEBUG
            return true;
#else
            return resultInt == 0 && genuineState == SL_GENUINE_STATE.SL_GEN_STATE_IS_GENUINE;
#endif
        }

        private static IEnumerable<string> GetWindowsGroups(WindowsIdentity id)
        {
            var irc = id.Groups ?? new IdentityReferenceCollection();
            return irc.Select(ir => (NTAccount)ir.Translate(typeof(NTAccount))).Select(acc => acc.Value).ToList();
        }

        public static bool HasWindowsGroup(string groupName)
        {
            var appDomain = Thread.GetDomain();
            appDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
            var currentPrincipal = (WindowsPrincipal)Thread.CurrentPrincipal;
            var groups = GetWindowsGroups((WindowsIdentity)currentPrincipal.Identity);
            return groups.Any(group => group == groupName);
        }

        public static bool IsTrustedInstaller()
        {
            return HasWindowsGroup(@"NT SERVICE\TrustedInstaller");
        }

        public static bool RelaunchAsTrustedInstaller()
        {
            var controller = new ServiceController("TrustedInstaller");
            if (controller.Status != ServiceControllerStatus.Running)
            {
                controller.Start();
                controller.WaitForStatus(ServiceControllerStatus.Running);
            }

            var targetProcess = Process.GetProcessesByName("TrustedInstaller").FirstOrDefault();

            if (targetProcess == null)
            {
                return false;
            }

            var currentProcess = Process.GetCurrentProcess();
            var currentModule = currentProcess.MainModule;

            if (currentModule == null)
            {
                return false;
            }

            var currentExecutable = currentModule.FileName;
            return NativeProcess.StartProcess(currentExecutable, targetProcess.Id, AmeliorationUtil.Playbook.Path);
        }

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern Boolean ChangeServiceConfig(
            IntPtr hService,
            UInt32 nServiceType,
            UInt32 nStartType,
            UInt32 nErrorControl,
            String lpBinaryPathName,
            String lpLoadOrderGroup,
            IntPtr lpdwTagId,
            [In] char[] lpDependencies,
            String lpServiceStartName,
            String lpPassword,
            String lpDisplayName);

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern IntPtr OpenService(
            IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);

        [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode,
            SetLastError = true)]
        public static extern IntPtr OpenSCManager(
            string machineName, string databaseName, uint dwAccess);

        [DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
        public static extern int CloseServiceHandle(IntPtr hSCObject);

        private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
        private const uint SERVICE_QUERY_CONFIG = 0x00000001;
        private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
        private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;


        public static void ChangeStartMode(ServiceController svc, ServiceStartMode mode)
        {
            var scManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
            if (scManagerHandle == IntPtr.Zero)
            {
                throw new ExternalException("Open Service Manager Error");
            }

            var serviceHandle = OpenService(
                scManagerHandle,
                svc.ServiceName,
                SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);

            if (serviceHandle == IntPtr.Zero)
            {
                throw new ExternalException("Open Service Error");
            }

            var result = ChangeServiceConfig(
                serviceHandle,
                SERVICE_NO_CHANGE,
                (uint)mode,
                SERVICE_NO_CHANGE,
                null,
                null,
                IntPtr.Zero,
                null,
                null,
                null,
                null);

            if (result == false)
            {
                var nError = Marshal.GetLastWin32Error();
                var win32Exception = new Win32Exception(nError);
                throw new ExternalException("Could not change service start type: "
                    + win32Exception.Message);
            }

            CloseServiceHandle(serviceHandle);
            CloseServiceHandle(scManagerHandle);
        }

        [StructLayout(LayoutKind.Sequential)]
        struct RM_UNIQUE_PROCESS
        {
            public int dwProcessId;
            public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
        }

        const int RmRebootReasonNone = 0;
        const int CCH_RM_MAX_APP_NAME = 255;
        const int CCH_RM_MAX_SVC_NAME = 63;

        enum RM_APP_TYPE
        {
            RmUnknownApp = 0,
            RmMainWindow = 1,
            RmOtherWindow = 2,
            RmService = 3,
            RmExplorer = 4,
            RmConsole = 5,
            RmCritical = 1000
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        struct RM_PROCESS_INFO
        {
            public RM_UNIQUE_PROCESS Process;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
            public string strAppName;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
            public string strServiceShortName;

            public RM_APP_TYPE ApplicationType;
            public uint AppStatus;
            public uint TSSessionId;
            [MarshalAs(UnmanagedType.Bool)] public bool bRestartable;
        }

        [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
        static extern int RmRegisterResources(uint pSessionHandle,
            UInt32 nFiles,
            string[] rgsFilenames,
            UInt32 nApplications,
            [In] RM_UNIQUE_PROCESS[] rgApplications,
            UInt32 nServices,
            string[] rgsServiceNames);

        [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
        static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);

        [DllImport("rstrtmgr.dll")]
        static extern int RmEndSession(uint pSessionHandle);

        [DllImport("rstrtmgr.dll")]
        static extern int RmGetList(uint dwSessionHandle,
            out uint pnProcInfoNeeded,
            ref uint pnProcInfo,
            [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
            ref uint lpdwRebootReasons);

        /// <summary>
        /// Find out what process(es) have a lock on the specified file.
        /// </summary>
        /// <param name="path">Path of the file.</param>
        /// <returns>Processes locking the file</returns>
        /// <remarks>See also:
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
        /// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
        /// 
        /// </remarks>
        public static List<Process> WhoIsLocking(string path)
        {
            string key = Guid.NewGuid().ToString();
            List<Process> processes = new List<Process>();

            int res = RmStartSession(out uint handle, 0, key);
            if (res != 0)
            {
                ErrorLogger.WriteToErrorLog("Could not begin restart session. Unable to determine file locker.",
                    Environment.StackTrace, $"Error while attempting to get locking processes of file {path}");
                throw new Exception("Could not begin restart session.  Unable to determine file locker.");
            }

            try
            {
                const int ERROR_MORE_DATA = 234;
                uint pnProcInfoNeeded = 0,
                    pnProcInfo = 0,
                    lpdwRebootReasons = RmRebootReasonNone;

                string[] resources = new string[] { path }; // Just checking on one resource.

                res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);

                if (res != 0) throw new Exception("Could not register resource.");

                //Note: there's a race condition here -- the first call to RmGetList() returns
                //      the total number of process. However, when we call RmGetList() again to get
                //      the actual processes this number may have increased.
                res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);

                if (res == ERROR_MORE_DATA)
                {
                    // Create an array to store the process results
                    RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
                    pnProcInfo = pnProcInfoNeeded;

                    // Get the list
                    res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
                    if (res == 0)
                    {
                        processes = new List<Process>((int)pnProcInfo);

                        // Enumerate all of the results and add them to the 
                        // list to be returned
                        for (int i = 0; i < pnProcInfo; i++)
                        {
                            try
                            {
                                processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
                            }
                            // catch the error -- in case the process is no longer running
                            catch (ArgumentException) { }
                        }
                    }
                    else throw new Exception("Could not list processes locking resource.");
                }
                else if (res != 0)
                    throw new Exception("Could not list processes locking resource. Could not get size of result." + $" Result value: {res}");
            }
            finally
            {
                RmEndSession(handle);
            }

            return processes;
        }

        /// <summary>
        /// Finds active anti-viruses in the system.
        /// </summary>
        /// <returns>a list of ProviderStatus.</returns>
        /// <remarks>See also:
        /// https://jdhitsolutions.com/blog/powershell/5187/get-antivirus-product-status-with-powershell/
        /// https://docs.microsoft.com/en-us/windows/win32/api/iwscapi/ne-iwscapi-wsc_security_product_state?redirectedfrom=MSDN
        /// https://mspscripts.com/get-installed-antivirus-information-2/
        /// https://social.msdn.microsoft.com/Forums/pt-BR/6501b87e-dda4-4838-93c3-244daa355d7c/wmisecuritycenter2-productstate?forum=vblanguage
        /// https://stackoverflow.com/questions/4700897/wmi-security-center-productstate-clarification/4711211
        /// https://blogs.msdn.microsoft.com/alejacma/2008/05/12/how-to-get-antivirus-information-with-wmi-vbscript/#comment-442
        /// https://www.magnumdb.com/search?q=parent:WSC_SECURITY_PRODUCT_STATE
        /// https://web.archive.org/web/20190121133247/http://neophob.com/2010/03/wmi-query-windows-securitycenter2/
        /// </remarks>
        public static List<ProviderStatus> GetEnabledAvList(bool ensureWMI = true)
        {
            if (ensureWMI)
            {
                var svc = new ServiceController("Winmgmt");
                ChangeStartMode(svc, ServiceStartMode.Automatic);
            }
            
            List<ProviderStatus> avList = new List<ProviderStatus>();
            string computer = Environment.MachineName;
            string wmipath = @"\\" + computer + @"\root\SecurityCenter2";
            string query = @"SELECT * FROM AntivirusProduct WHERE displayName != ""Windows Defender""";

            try
            {
                using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmipath, query))
                {
                    ManagementObjectCollection results = searcher.Get();
                    foreach (var o in results)
                    {
                        // You can find if an AV is active or not by accessing the hex code of the productState
                        // (from the right) 19th bit == Anti Virus is on
                        // (from the right) 13th bit == On Access Scanning
                        // 00000000 00000[1]10 000[1]0000 000[0]0000
                        // 19th bit (yes) = Av on
                        // 13th bit (yes) = On Access Scanning
                        // The third byte defines if the .dat file is up-to-date
                        var result = (ManagementObject)o;
                        var productState = result["productState"];
                        string hex = Hex(Convert.ToInt32(productState));
                        string bin = Binary(Convert.ToInt32(productState));
                        string reversed = Reverse(bin);
                        var enabled = GetBit(reversed, 18);
                        var scanning = GetBit(reversed, 12);
                        var outdated = GetBit(reversed, 4);

                        static string Binary(int value)
                        {
                            return Convert.ToString(value, 2).PadLeft(24, '0');
                        }

                        static string Hex(int value)
                        {
                            return Convert.ToString(value, 16).PadLeft(6, '0');
                        }

                        static bool GetBit(string value, int index)
                        {
                            return value.Substring(index, 1).Equals("1");
                        }

                        static string Reverse(string value)
                        {
                            return new string(value.Reverse().ToArray());
                        }

                        if (!enabled) continue;
                        var av = new ProviderStatus()
                        {
                            DisplayName = result["displayName"].ToString(),
                            AVStatus = enabled ? AVStatusFlags.Enabled : AVStatusFlags.Unknown,
                            SecurityProvider = ProviderFlags.ANTIVIRUS,
                            SignatureStatus = outdated ? SignatureStatusFlags.OutOfDate : SignatureStatusFlags.UpToDate
                        };
                        avList.Add(av);
                    }
                }
            }
            catch (Exception e)
            {
                ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "Error while retrieving the AV list.");
            }

            return avList;
        }

        //Checks if Visual C++ redistributable is installed. 
        public static bool IsVCInstalled()
        {
            string dependenciesPath = @"SOFTWARE\Classes\Installer\Dependencies";

            using (RegistryKey dependencies = Registry.LocalMachine.OpenSubKey(dependenciesPath))
            {
                if (dependencies == null) return false;

                foreach (string subKeyName in dependencies.GetSubKeyNames().Where(n => !n.ToLower().Contains("dotnet") && !n.ToLower().Contains("microsoft")))
                {
                    using (RegistryKey subDir = Registry.LocalMachine.OpenSubKey(dependenciesPath + "\\" + subKeyName))
                    {
                        var value = subDir.GetValue("DisplayName")?.ToString() ?? null;
                        if (string.IsNullOrEmpty(value))
                        {
                            continue;
                        }
                        if (Environment.Is64BitOperatingSystem)
                        {
                            if (Regex.IsMatch(value, @"C\+\+ 2015.*\((x64|x86)\)"))
                            {
                                return true;
                            }
                        }
                        else
                        {
                            if (Regex.IsMatch(value, @"C\+\+ 2015.*\(x86\)"))
                            {
                                return true;
                            }
                        }
                    }
                }
            }
            return false;
        }
        public static async Task RemoveProtectionAsync()
        {
            var cmdAction = new CmdAction();
            if (!IsVCInstalled())
            {
                Console.WriteLine(Environment.NewLine + "Installing VC 15...");
                try
                {
                    //Install Visual C++ 2015 redistributable package silently
                    cmdAction.Command = "vc_redist.x64.exe /q /norestart";
                    await cmdAction.RunTask();
                }
                catch (Exception e)
                {
                    ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "Error while installing VC 15.");
                    throw;
                }
            }

            try
            {
                Console.WriteLine(Environment.NewLine + "Installing driver...");
                cmdAction.Command = Environment.Is64BitOperatingSystem
                    ? $"ProcessHacker\\x64\\ProcessHacker.exe -s -installkph"
                    : $"ProcessHacker\\x86\\ProcessHacker.exe -s -installkph";
                var res = await cmdAction.RunTask();

            }
            catch (Exception e)
            {
                ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "ProcessHacker ran into an error while installing its driver.");
                throw;
            }

        }

        private const int GWL_STYLE = -16;
        private const int WS_SYSMENU = 0x80000;
        [DllImport("user32.dll", SetLastError = true)]
        private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
        [DllImport("user32.dll")]
        private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        //public static void RemoveCloseButton(Window window)
        //{
            //var hwnd = new WindowInteropHelper(window).Handle;
            //SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
        //}

        public static bool IsVM()
        {
            try
            {
                using (var searcher = new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem"))
                {
                    using (var items = searcher.Get())
                    {
                        foreach (var item in items)
                        {
                            string manufacturer = item["Manufacturer"].ToString().ToLower();
                            if ((manufacturer == "microsoft corporation" && item["Model"].ToString().ToUpperInvariant().Contains("VIRTUAL"))
                                || manufacturer.Contains("vmware")
                                || item["Model"].ToString() == "VirtualBox")
                            {
                                return true;
                            }
                        }
                    }
                }
                return false;
            }
            catch (Exception e)
            {
                ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "Error while checking if running system is a VM.");
                return false;
            }
        }
        
        public static void PrepareSystemCLI()
        {
            try
            {
                var defenderStop = new RunAction()
                {
                    RawPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                    Exe = $"NSudoLC.exe",
                    Arguments = "-U:T -P:E -M:S -ShowWindowMode:Hide -Priority:RealTime -Wait cmd /c \"" +
                                "sc sdset \"WinDefend\" \"D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCLCSWRPLOCRRC;;;BA)(A;;CCLCSWRPLOCRRC;;;BU)(A;;CCLCSWRPLOCRRC;;;IU)(A;;CCLCSWRPLOCRRC;;;SU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-80-1913148863-3492339771-4165695881-2087618961-4109116736)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)\"&" +
                                "sc config WinDefend start=disabled&" +
                                "net stop WinDefend\"",
                    CreateWindow = false,
                    Timeout = 7500,
                };
                defenderStop.RunTask().Wait();
            } catch (Exception e)
            {
            }

            var defenderValues = new RunAction()
            {
                RawPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                Exe = $"NSudoLC.exe",
                Arguments = "-U:T -P:E -M:S -ShowWindowMode:Hide -Priority:RealTime -Wait cmd /c \"reg delete \"HKLM\\SOFTWARE\\Microsoft\\Windows Defender\" /v \"ProductAppDataPath\" /f &" +
                            " reg delete \"HKLM\\SOFTWARE\\Microsoft\\Windows Defender\" /v \"InstallLocation\" /f\"",
                CreateWindow = false
            };
            defenderValues.RunTask().Wait();

            var defenderKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows Defender");
            if (defenderKey != null && defenderKey.GetValueNames().Contains("InstallLocation"))
            {
                throw new Exception("Could not remove defender install values.");
            }

            var defenderService = new RunAction()
            {
                RawPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                Exe = $"NSudoLC.exe",
                Arguments = "-U:T -P:E -M:S -ShowWindowMode:Hide -Priority:RealTime -Wait reg delete \"HKLM\\SYSTEM\\CurrentControlSet\\Services\\WinDefend\" /f",
                CreateWindow = false
            };
            defenderService.RunTask().Wait();

            if (Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\WinDefend") != null)
            {
                throw new Exception("Could not remove WinDefend service.");
            }

            // MpOAV.dll normally in use by a lot of processes. This prevents that.
            var MpOAVCLSID = new RunAction()
            {
                RawPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                Exe = $"NSudoLC.exe",
                Arguments = @"-U:T -P:E -M:S -Priority:RealTime -ShowWindowMode:Hide -Wait reg delete ""HKCR\CLSID\{2781761E-28E0-4109-99FE-B9D127C57AFE}\InprocServer32"" /f",
                CreateWindow = false
            };
            MpOAVCLSID.RunTask().Wait();

            if (Registry.ClassesRoot.OpenSubKey(@"CLSID\{2781761E-28E0-4109-99FE-B9D127C57AFE}\InprocServer32") != null)
            {
                throw new Exception("Could not remove MpOAV mapping.");
            }

            // Can cause ProcessHacker driver warnings without this
            AmeliorationUtil.SafeRunAction(new RegistryValueAction()
            {
                KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity",
                Value = "Enabled",
                Data = 0,
            }).Wait();
            AmeliorationUtil.SafeRunAction(new RegistryValueAction()
            {
                KeyName = @"HKLM\SYSTEM\CurrentControlSet\Control\CI\Config",
                Value = "VulnerableDriverBlocklistEnable",
                Data = 0,
            }).Wait();
        }
        
        public static async Task UninstallDriver()
        {
            CmdAction cmdAction = new CmdAction();
            try
            {
                Console.WriteLine("Removing driver...");
                cmdAction.Command = Environment.Is64BitOperatingSystem
                    ? $"ProcessHacker\\x64\\ProcessHacker.exe -s -uninstallkph"
                    : $"ProcessHacker\\x86\\ProcessHacker.exe -s -uninstallkph";
                await cmdAction.RunTask();
            }
            catch (Exception e)
            {
                ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "ProcessHacker ran into an Error while uninstalling the driver.");
                throw;
            }
        }
        
        
        
        public class RegistryManager
        {
            [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 LoadFromFile(string path, bool classHive = false)
            {
                var parentKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
                string name;

                if (path.Contains("Users\\Default\\")) name = classHive ? "AME_UserHive_Default_Classes" : "AME_UserHive_Default";
                else name = classHive ? "AME_UserHive_" + (HivesLoaded + 1) + "_Classes" : "AME_UserHive_" + (HivesLoaded + 1);

                IntPtr parentHandle = parentKey.Handle.DangerousGetHandle();
                RegLoadKey(parentHandle, name, path);
                HivesLoaded++;
            }
            private static void AcquirePrivileges()
            {
                ulong luid = 0;
                bool throwaway;
                LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid);
                RtlAdjustPrivilege((int)luid, true, false, out throwaway);
                LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid);
                RtlAdjustPrivilege((int)luid, true, false, out throwaway);
            }
            private static void ReturnPrivileges()
            {
                ulong luid = 0;
                bool throwaway;
                LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid);
                RtlAdjustPrivilege((int)luid, false, false, out throwaway);
                LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid);
                RtlAdjustPrivilege((int)luid, false, false, out throwaway);
            }

            private static bool HivesHooked;
            private static int HivesLoaded;
            public static async void HookUserHives()
            {
                try
                {
                    if (HivesHooked || WinUtil.IsTrustedInstaller() || RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default).GetSubKeyNames().Any(x => x.StartsWith("AME_UserHive_"))) return;
                    HivesHooked = true;

                    var usersDir = Environment.GetEnvironmentVariable("SYSTEMDRIVE") + "\\Users";

                    var ignoreList = new List<string>() { "Default User", "Public", "All Users" };
                    var userDirs = Directory.GetDirectories(usersDir).Where(x => !ignoreList.Contains(x.Split('\\').Last())).ToList();

                    var userKeys = Registry.Users.GetSubKeyNames().Where(x => x.StartsWith("S-"));

                    foreach (var userKey in userKeys)
                    {

                        try
                        {
                            var userEnv = Registry.Users.OpenSubKey(userKey).OpenSubKey("Volatile Environment");
                            userDirs.Remove((string)userEnv.GetValue("USERPROFILE"));
                        }
                        catch (Exception) { }
                    }
                    
                    if (userDirs.Any()) AcquirePrivileges();
                    foreach (var userDir in userDirs)
                    {
                        if (!File.Exists($"{userDir}\\NTUSER.DAT"))
                        {
                            ErrorLogger.WriteToErrorLog($"NTUSER.DAT file not found in user folder '{userDir}'.",
                                Environment.StackTrace, $"Error attempting to load user registry hive.");
                            continue;
                        }
                        LoadFromFile($"{userDir}\\NTUSER.DAT");

                        if (userDir.EndsWith("\\Default"))
                        {
                            try
                            {
                                if (!Directory.Exists($@"{userDir}\AppData\Local\Microsoft\Windows")) Directory.CreateDirectory($@"{userDir}\AppData\Local\Microsoft\Windows");
                                var fs = File.Create($@"{userDir}\AppData\Local\Microsoft\Windows" + @"\UsrClass.dat");
                            
                                var resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("TrustedUninstaller.Shared.Properties.UsrClass.dat");
                                resource.CopyTo(fs);
                            
                                fs.Close();
                            }
                            catch (Exception e)
                            {
                                ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
                                    $"Failed to create default user class hive.", userDir);
                            }
                        }
                    
                        if (!File.Exists($@"{userDir}\AppData\Local\Microsoft\Windows\UsrClass.dat"))
                        {
                            ErrorLogger.WriteToErrorLog($@"UsrClass.dat file not found in user appdata folder '{userDir}\AppData\Local\Microsoft\Windows'.",
                                Environment.StackTrace, $"Error attempting to load user classes registry hive.");
                            continue;
                        }
                        LoadFromFile($@"{userDir}\AppData\Local\Microsoft\Windows\UsrClass.dat", true);
                    }
                    if (userDirs.Any()) ReturnPrivileges();
                }
                catch (Exception e)
                {
                    ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
                        $"Critical error while attempting to mount user hives.");
                    Console.WriteLine(":AME-ERROR: Failure while mounting user registry hives.");
                }
            }

            public static async void UnhookUserHives()
            {
                try
                {
                    var usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
                    var userHives = usersKey.GetSubKeyNames().Where(x => x.StartsWith("AME_UserHive_")).ToList();

                    if (userHives.Any()) AcquirePrivileges();
                    foreach (var userHive in userHives)
                    {
                        RegUnLoadKey(usersKey.Handle.DangerousGetHandle(), userHive);
                    }
                    if (userHives.Any()) ReturnPrivileges();
                
                    usersKey.Close();
                }
                catch (Exception e)
                {
                    ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
                        $"Critical error while attempting to unmount user hives.");
                    Console.WriteLine(":AME-ERROR: Failure while unmounting user registry hives.");
                }
            }
        }
    }
}
