Browse Source

Update to v0.7

master
Styris 9 months ago
parent
commit
32af7e5629
33 changed files with 873 additions and 200 deletions
  1. +0
    -2
      README.md
  2. +0
    -26
      TrustedUninstaller.CLI/App.config
  3. +158
    -14
      TrustedUninstaller.CLI/CLI.cs
  4. BIN
      TrustedUninstaller.CLI/Properties/resources/CLI-Resources.7z
  5. +20
    -5
      TrustedUninstaller.CLI/TrustedUninstaller.CLI.csproj
  6. +4
    -1
      TrustedUninstaller.Shared/Actions/AppxAction.cs
  7. +12
    -3
      TrustedUninstaller.Shared/Actions/CmdAction.cs
  8. +36
    -5
      TrustedUninstaller.Shared/Actions/FileAction.cs
  9. +1
    -1
      TrustedUninstaller.Shared/Actions/LanguageAction.cs
  10. +1
    -1
      TrustedUninstaller.Shared/Actions/LineInFileAction.cs
  11. +2
    -3
      TrustedUninstaller.Shared/Actions/PowershellAction.cs
  12. +1
    -1
      TrustedUninstaller.Shared/Actions/RegistryKeyAction.cs
  13. +1
    -1
      TrustedUninstaller.Shared/Actions/RegistryValueAction.cs
  14. +1
    -1
      TrustedUninstaller.Shared/Actions/RunAction.cs
  15. +1
    -1
      TrustedUninstaller.Shared/Actions/ScheduledTaskAction.cs
  16. +3
    -3
      TrustedUninstaller.Shared/Actions/ServiceAction.cs
  17. +1
    -1
      TrustedUninstaller.Shared/Actions/ShortcutAction.cs
  18. +2
    -1
      TrustedUninstaller.Shared/Actions/SystemPackageAction.cs
  19. +20
    -4
      TrustedUninstaller.Shared/Actions/TaskKillAction.cs
  20. +1
    -1
      TrustedUninstaller.Shared/Actions/UpdateAction.cs
  21. +1
    -1
      TrustedUninstaller.Shared/Actions/UserAction.cs
  22. +1
    -1
      TrustedUninstaller.Shared/Actions/WriteStatusAction.cs
  23. +67
    -14
      TrustedUninstaller.Shared/AmeliorationUtil.cs
  24. +2
    -2
      TrustedUninstaller.Shared/Globals.cs
  25. +350
    -2
      TrustedUninstaller.Shared/Playbook.cs
  26. +1
    -0
      TrustedUninstaller.Shared/ProviderStatus.cs
  27. +34
    -7
      TrustedUninstaller.Shared/Requirements.cs
  28. +14
    -0
      TrustedUninstaller.Shared/Tasks/TaskAction.cs
  29. +9
    -1
      TrustedUninstaller.Shared/Tasks/UninstallTask.cs
  30. +24
    -5
      TrustedUninstaller.Shared/TrustedUninstaller.Shared.csproj
  31. +105
    -92
      TrustedUninstaller.Shared/WinUtil.cs
  32. BIN
      Windows.winmd
  33. BIN
      ameliorated_logo.png

+ 0
- 2
README.md View File

@ -18,8 +18,6 @@ Coming soon.
4. Build TrustedUninstaller.CLI
If you run into reference issues with the Windows namespace, add `Windows.winmd` as a reference.
## License
This tool has an [MIT license](https://en.wikipedia.org/wiki/MIT_License), which waives any requirements or rules governing the source code’s use, removing politics from the equation.


+ 0
- 26
TrustedUninstaller.CLI/App.config View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SQLitePCLRaw.core" publicKeyToken="1488e028ca7ab535" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.7.1395" newVersion="2.0.7.1395" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IO.Compression" publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="mscorlib" publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

+ 158
- 14
TrustedUninstaller.CLI/CLI.cs View File

@ -10,6 +10,7 @@ using System.Windows;
using Microsoft.Win32;
using TrustedUninstaller.Shared;
using TrustedUninstaller.Shared.Actions;
using TrustedUninstaller.Shared.Tasks;
namespace TrustedUninstaller.CLI
{
@ -40,13 +41,12 @@ namespace TrustedUninstaller.CLI
if (args.Length < 1 || !Directory.Exists(args[0]))
{
Console.WriteLine("No Playbook selected: Use the GUI to select a playbook to run.");
Console.WriteLine("No Playbook selected.");
return -1;
}
AmeliorationUtil.Playbook = await AmeliorationUtil.DeserializePlaybook(args[0]);
AmeliorationUtil.Playbook.Path = args[0];
if (!Directory.Exists($"{AmeliorationUtil.Playbook.Path}\\Configuration") || Directory.GetFiles($"{AmeliorationUtil.Playbook.Path}\\Configuration").Length == 0)
{
Console.WriteLine("Configuration folder is empty, put YAML files in it and restart the application.");
@ -54,6 +54,9 @@ namespace TrustedUninstaller.CLI
return -1;
}
ExtractResourceFolder("resources", Directory.GetCurrentDirectory());
if (!WinUtil.IsTrustedInstaller())
{
Console.WriteLine("Checking requirements...\r\n");
@ -71,20 +74,30 @@ namespace TrustedUninstaller.CLI
Console.WriteLine("All 4 windows security toggles must be set to off.\r\nNavigate to Windows Security > Virus & threat detection > manage settings.\r\nPress any key to continue...");
Console.ReadKey();
}
Console.WriteLine("The system must be prepared before continuing Your system will restart after preparation\r\nPress any key to continue...");
bool remnantsOnly = Requirements.DefenderDisabled.RemnantsOnly();
Console.WriteLine(remnantsOnly ? "The system must be prepared before continuing.\r\nPress any key to continue..." : "The system must be prepared before continuing. Your system will restart after preparation\r\nPress any key to continue...");
Console.ReadKey();
try
{
WinUtil.PrepareSystemCLI();
CmdAction reboot = new CmdAction()
Console.WriteLine("\r\nPreparing system...");
PrepareSystemCLI();
Console.WriteLine("Preparation Complete");
if (!remnantsOnly)
{
Command = "timeout /t 1 & shutdown /r /t 0",
Wait = false
};
Console.WriteLine("\r\nRestarting system...");
CmdAction reboot = new CmdAction()
{
Command = "timeout /t 1 & shutdown /r /t 0",
Wait = false
};
AmeliorationUtil.SafeRunAction(reboot).Wait();
AmeliorationUtil.SafeRunAction(reboot).Wait();
Environment.Exit(0);
Environment.Exit(0);
}
} catch (Exception e)
{
Console.WriteLine("Error preparing system: " + e.Message);
@ -96,9 +109,33 @@ namespace TrustedUninstaller.CLI
{
Console.WriteLine("Internet must be connected to run this Playbook.");
}
}
if (!File.Exists($"{AmeliorationUtil.Playbook.Path}\\options.txt"))
{
List<string> defaultOptions = new List<string>();
foreach (var page in AmeliorationUtil.Playbook.FeaturePages)
{
if (page.DependsOn != null && !defaultOptions.Contains(page.DependsOn))
continue;
if (page.GetType() == typeof(Playbook.CheckboxPage))
{
foreach (var option in ((Playbook.CheckboxPage)page).Options.Where(x => ((Playbook.CheckboxPage.CheckboxOption)x).IsChecked))
{
defaultOptions.Add(option.Name);
}
}
if (page.GetType() == typeof(Playbook.RadioPage))
defaultOptions.Add(((Playbook.RadioPage)page).DefaultOption);
if (page.GetType() == typeof(Playbook.RadioImagePage))
defaultOptions.Add(((Playbook.RadioImagePage)page).DefaultOption);
}
AmeliorationUtil.Playbook.Options = defaultOptions;
}
try
{
if (!Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ame-assassin")))
@ -227,8 +264,7 @@ namespace TrustedUninstaller.CLI
}
}
}
public static async Task<List<bool>> GetDefenderToggles()
{
var result = new List<bool>();
@ -338,5 +374,113 @@ namespace TrustedUninstaller.CLI
});
return result;
}
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)
{
var disableDefender = new RunAction()
{
RawPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
Exe = $"NSudoLC.exe",
Arguments = "-U:T -P:E -M:S -ShowWindowMode:Hide -Priority:RealTime -Wait reg add \"HKLM\\SYSTEM\\CurrentControlSet\\Services\\WinDefend\" /v \"Start\" /t REG_DWORD /d 4 /f",
CreateWindow = false
};
disableDefender.RunTask().Wait();
if (new RegistryValueAction() {KeyName = @"SYSTEM\CurrentControlSet\Services\WinDefend", Value = "Start", Data = 4, Type = RegistryValueType.REG_DWORD}.GetStatus() != UninstallTaskStatus.Completed)
throw new Exception("Could not disable 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;
}
}
}
}

BIN
TrustedUninstaller.CLI/Properties/resources/CLI-Resources.7z View File


+ 20
- 5
TrustedUninstaller.CLI/TrustedUninstaller.CLI.csproj View File

@ -32,6 +32,9 @@
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>true</BootstrapperEnabled>
<AllowedReferenceRelatedFileExtensions>
*.xml
</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>
<PropertyGroup>
<PathMap>$([System.IO.Path]::GetFullPath('$(SolutionDir)'))=./</PathMap>
@ -40,7 +43,7 @@
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<DebugType>embedded</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
@ -60,6 +63,13 @@
<Prefer32Bit>true</Prefer32Bit>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Single File|x64' ">
<OutputPath>bin\x64\Release\</OutputPath>
<PlatformTarget>x64</PlatformTarget>
<Optimize>true</Optimize>
<DebugType>embedded</DebugType>
<DefineConstants>SINGLE</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<ManifestCertificateThumbprint>B8F0A67800B779C5CEF49BEAB6E5247E373F9452</ManifestCertificateThumbprint>
</PropertyGroup>
@ -81,6 +91,15 @@
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Single File|x64' ">
<PostBuildEvent>for /f "usebackq delims=" %%A in (`DIR /B /S /A:d "$(SolutionDir)" ^| FINDSTR /R /c:".*\\bin\\.*\\de$" /c:".*\\bin\\.*\\en$" /c:".*\\bin\\.*\\es$" /c:".*\\bin\\.*\\fr$" /c:".*\\bin\\.*\\it$" /c:".*\\bin\\.*\\ja$" /c:".*\\bin\\.*\\ko$" /c:".*\\bin\\.*\\ru$" /c:".*\\bin\\.*\\zh-Hans$" /c:".*\\bin\\.*\\zh-Hant$" /c:".*\\bin\\.*\\pl$" /c:".*\\bin\\.*\\zh-CN$"`) do (RMDIR /Q /S "%%A" &amp; cmd /c "exit /b 0")
cmd /c "echo Compiled&gt; "$(SolutionDir)\TrustedUninstaller.GUI\gui-builder\Compiled.txt""</PostBuildEvent>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Single File|x64' ">
<PreBuildEvent>PowerShell -NoP -C "Get-Process 'ame-builder' -EA SilentlyContinue | Wait-Process -EA SilentlyContinue; EXIT 0"
PowerShell -NoP -C "Start-Process '$(SolutionDir)\TrustedUninstaller.GUI\gui-builder\ame-builder.exe' -ArgumentList 'CLI' -WindowStyle Hidden"
</PreBuildEvent>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' != 'Single File|x64' ">
<PostBuildEvent>for /f "usebackq delims=" %%A in (`DIR /B /S /A:d "$(SolutionDir)" ^| FINDSTR /R /c:".*\\bin\\.*\\de$" /c:".*\\bin\\.*\\en$" /c:".*\\bin\\.*\\es$" /c:".*\\bin\\.*\\fr$" /c:".*\\bin\\.*\\it$" /c:".*\\bin\\.*\\ja$" /c:".*\\bin\\.*\\ko$" /c:".*\\bin\\.*\\ru$" /c:".*\\bin\\.*\\zh-Hans$" /c:".*\\bin\\.*\\zh-Hant$" /c:".*\\bin\\.*\\pl$" /c:".*\\bin\\.*\\zh-CN$"`) do (RMDIR /Q /S "%%A" &amp; cmd /c "exit /b 0")</PostBuildEvent>
</PropertyGroup>
@ -104,16 +123,12 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Windows">
<HintPath>..\Windows.winmd</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CLI.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="app.manifest" />
</ItemGroup>
<ItemGroup>


+ 4
- 1
TrustedUninstaller.Shared/Actions/AppxAction.cs View File

@ -1,6 +1,8 @@
using System;
using System.Linq;
using System.Threading.Tasks;
//using Windows.ApplicationModel;
//using Windows.Management.Deployment;
using TrustedUninstaller.Shared.Exceptions;
using TrustedUninstaller.Shared.Tasks;
using YamlDotNet.Serialization;
@ -11,7 +13,8 @@ using System.Threading;
namespace TrustedUninstaller.Shared.Actions
{
internal class AppxAction : ITaskAction
// Integrate ame-assassin later
internal class AppxAction : TaskAction, ITaskAction
{
public enum AppxOperation
{


+ 12
- 3
TrustedUninstaller.Shared/Actions/CmdAction.cs View File

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using TrustedUninstaller.Shared.Exceptions;
using TrustedUninstaller.Shared.Tasks;
@ -8,7 +9,7 @@ using YamlDotNet.Serialization;
namespace TrustedUninstaller.Shared.Actions
{
public class CmdAction : ITaskAction
public class CmdAction : TaskAction, ITaskAction
{
[YamlMember(typeof(string), Alias = "command")]
public string Command { get; set; }
@ -84,8 +85,16 @@ namespace TrustedUninstaller.Shared.Actions
process.Dispose();
return true;
}
var error = new StringBuilder();
process.OutputDataReceived += ProcOutputHandler;
process.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs args)
{
if (!String.IsNullOrEmpty(args.Data))
error.AppendLine(args.Data);
else
error.AppendLine();
};
process.BeginOutputReadLine();
@ -104,7 +113,7 @@ namespace TrustedUninstaller.Shared.Actions
if (process.ExitCode != 0)
{
StandardError = process.StandardError.ReadToEnd();
StandardError = error.ToString();
Console.WriteLine($"cmd instance exited with error code: {process.ExitCode}");
if (!String.IsNullOrEmpty(StandardError)) Console.WriteLine($"Error message: {StandardError}");


+ 36
- 5
TrustedUninstaller.Shared/Actions/FileAction.cs View File

@ -4,17 +4,20 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.ServiceProcess;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using TrustedUninstaller.Shared.Exceptions;
using TrustedUninstaller.Shared.Tasks;
using YamlDotNet.Serialization;
namespace TrustedUninstaller.Shared.Actions
{
public class FileAction : ITaskAction
public class FileAction : TaskAction, ITaskAction
{
[YamlMember(typeof(string), Alias = "path")]
public string RawPath { get; set; }
@ -71,6 +74,9 @@ namespace TrustedUninstaller.Shared.Actions
return isFile || isDirectory ? UninstallTaskStatus.ToDo : UninstallTaskStatus.Completed;
}
[DllImport("Unlocker.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private static extern bool EzUnlockFileW(string path);
private async Task DeleteFile(string file, bool log = false)
{
if (!TrustedInstaller)
@ -79,15 +85,36 @@ namespace TrustedUninstaller.Shared.Actions
if (File.Exists(file))
{
try
{
EzUnlockFileW(file);
}
catch (Exception e)
{
ErrorLogger.WriteToErrorLog($"Error while unlocking file: " + e.Message, e.StackTrace,
$"FileAction Error", file);
}
try {await Task.Run(() => File.Delete(file));} catch {}
CmdAction delAction = new CmdAction()
{
Command = $"del /q /f {file}"
Command = $"del /q /f \"{file}\""
};
await delAction.RunTask();
}
}
else if (File.Exists("NSudoLC.exe"))
{
try
{
EzUnlockFileW(file);
}
catch (Exception e)
{
ErrorLogger.WriteToErrorLog($"Error while unlocking file: " + e.Message, e.StackTrace,
$"FileAction Error", file);
}
RunAction tiDelAction = new RunAction()
{
Exe = "NSudoLC.exe",
@ -259,15 +286,19 @@ namespace TrustedUninstaller.Shared.Actions
Console.WriteLine($"\r\nError: Could not get amount of services locking file.\r\nException: " + e.Message);
}
}
if (svcCount > 8) Console.WriteLine("Amount of locking services exceeds 8, skipping...");
while (processes.Any() && delay <= 800 && svcCount <= 8)
while (processes.Any() && delay <= 800)
{
Console.WriteLine("Processes locking the file:");
foreach (var process in processes)
{
Console.WriteLine(process.ProcessName);
}
if (svcCount > 10)
{
Console.WriteLine("Amount of locking services exceeds 10, skipping...");
break;
}
foreach (var process in processes)
{


+ 1
- 1
TrustedUninstaller.Shared/Actions/LanguageAction.cs View File

@ -6,7 +6,7 @@ using TrustedUninstaller.Shared.Tasks;
namespace TrustedUninstaller.Shared.Actions
{
class LanguageAction : ITaskAction
class LanguageAction : TaskAction, ITaskAction
{
public int ProgressWeight { get; set; } = 1;
public int GetProgressWeight() => ProgressWeight;


+ 1
- 1
TrustedUninstaller.Shared/Actions/LineInFileAction.cs View File

@ -14,7 +14,7 @@ namespace TrustedUninstaller.Shared.Actions
Delete = 0,
Add = 1
}
internal class LineInFileAction : ITaskAction
internal class LineInFileAction : TaskAction, ITaskAction
{
[YamlMember(Alias = "path")]
public string RawPath { get; set; }


+ 2
- 3
TrustedUninstaller.Shared/Actions/PowershellAction.cs View File

@ -2,7 +2,6 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -13,7 +12,7 @@ using YamlDotNet.Serialization;
namespace TrustedUninstaller.Shared.Actions
{
public class PowerShellAction : ITaskAction
public class PowerShellAction : TaskAction, ITaskAction
{
[YamlMember(typeof(string), Alias = "command")]
public string Command { get; set; }
@ -69,7 +68,7 @@ namespace TrustedUninstaller.Shared.Actions
RedirectStandardOutput = true,
CreateNoWindow = true
};
if (ExeDir) startInfo.WorkingDirectory = Directory.GetCurrentDirectory() + "\\Executables";
if (ExeDir) startInfo.WorkingDirectory = AmeliorationUtil.Playbook.Path + "\\Executables";
if (!Wait)
{
startInfo.RedirectStandardError = false;


+ 1
- 1
TrustedUninstaller.Shared/Actions/RegistryKeyAction.cs View File

@ -19,7 +19,7 @@ namespace TrustedUninstaller.Shared.Actions
Delete = 0,
Add = 1
}
public class RegistryKeyAction : ITaskAction
public class RegistryKeyAction : TaskAction, ITaskAction
{
[YamlMember(typeof(string), Alias = "path")]
public string KeyName { get; set; }


+ 1
- 1
TrustedUninstaller.Shared/Actions/RegistryValueAction.cs View File

@ -36,7 +36,7 @@ namespace TrustedUninstaller.Shared.Actions
REG_UNKNOWN = RegistryValueKind.Unknown
}
public class RegistryValueAction : ITaskAction
public class RegistryValueAction : TaskAction, ITaskAction
{
[YamlMember(typeof(string), Alias = "path")]
public string KeyName { get; set; }


+ 1
- 1
TrustedUninstaller.Shared/Actions/RunAction.cs View File

@ -8,7 +8,7 @@ using YamlDotNet.Serialization;
namespace TrustedUninstaller.Shared.Actions
{
public class RunAction : ITaskAction
public class RunAction : TaskAction, ITaskAction
{
[YamlMember(typeof(string), Alias = "path")]
public string RawPath { get; set; } = null;


+ 1
- 1
TrustedUninstaller.Shared/Actions/ScheduledTaskAction.cs View File

@ -19,7 +19,7 @@ namespace TrustedUninstaller.Shared.Actions
DeleteFolder = 3
}
internal class ScheduledTaskAction : ITaskAction
internal class ScheduledTaskAction : TaskAction, ITaskAction
{
[YamlMember(typeof(ScheduledTaskOperation), Alias = "operation")]
public ScheduledTaskOperation Operation { get; set; } = ScheduledTaskOperation.Delete;


+ 3
- 3
TrustedUninstaller.Shared/Actions/ServiceAction.cs View File

@ -23,7 +23,7 @@ namespace TrustedUninstaller.Shared.Actions
Delete,
Change
}
internal class ServiceAction : ITaskAction
internal class ServiceAction : TaskAction, ITaskAction
{
[YamlMember(typeof(ServiceOperation), Alias = "operation")]
public ServiceOperation Operation { get; set; } = ServiceOperation.Delete;
@ -305,8 +305,8 @@ namespace TrustedUninstaller.Shared.Actions
else
{
cmdAction.Command = Environment.Is64BitOperatingSystem ?
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {ServiceName} -caction {Operation.ToString().ToLower()}" :
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {ServiceName} -caction {Operation.ToString().ToLower()}";
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction {Operation.ToString().ToLower()}" :
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction {Operation.ToString().ToLower()}";
await cmdAction.RunTask();
}


+ 1
- 1
TrustedUninstaller.Shared/Actions/ShortcutAction.cs View File

@ -8,7 +8,7 @@ using File = System.IO.File;
namespace TrustedUninstaller.Shared.Actions
{
class ShortcutAction : ITaskAction
class ShortcutAction : TaskAction, ITaskAction
{
[YamlMember(typeof(string), Alias = "path")]
public string RawPath { get; set; }


+ 2
- 1
TrustedUninstaller.Shared/Actions/SystemPackageAction.cs View File

@ -11,7 +11,8 @@ using System.Threading;
namespace TrustedUninstaller.Shared.Actions
{
internal class SystemPackageAction : ITaskAction
// Integrate ame-assassin later
internal class SystemPackageAction : TaskAction, ITaskAction
{
public enum Architecture
{


+ 20
- 4
TrustedUninstaller.Shared/Actions/TaskKillAction.cs View File

@ -11,7 +11,7 @@ using YamlDotNet.Serialization;
namespace TrustedUninstaller.Shared.Actions
{
class TaskKillAction : ITaskAction
class TaskKillAction : TaskAction, ITaskAction
{
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
@ -85,7 +85,7 @@ namespace TrustedUninstaller.Shared.Actions
.Where(process => process.ProcessName.EndsWith(ProcessName.TrimStart('*'), StringComparison.CurrentCultureIgnoreCase));
return Process.GetProcessesByName(ProcessName);
}
}
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool IsProcessCritical(IntPtr hProcess, ref bool Critical);
@ -252,7 +252,15 @@ namespace TrustedUninstaller.Shared.Actions
if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success))
{
bool isCritical = false;
IsProcessCritical(process.Handle, ref isCritical);
try
{
IsProcessCritical(process.Handle, ref isCritical);
}
catch (InvalidOperationException e)
{
ErrorLogger.WriteToErrorLog("Could not check if process is critical.", e.StackTrace, "TaskKillAction Error", process.ProcessName);
return false;
}
if (isCritical)
{
Console.WriteLine($"{process.ProcessName} is a critical process, skipping...");
@ -285,7 +293,15 @@ namespace TrustedUninstaller.Shared.Actions
if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success))
{
bool isCritical = false;
IsProcessCritical(process.Handle, ref isCritical);
try
{
IsProcessCritical(process.Handle, ref isCritical);
}
catch (InvalidOperationException e)
{
ErrorLogger.WriteToErrorLog("Could not check if process is critical.", e.StackTrace, "TaskKillAction Error", process.ProcessName);
continue;
}
if (isCritical)
{
Console.WriteLine($"{process.ProcessName} is a critical process, skipping...");


+ 1
- 1
TrustedUninstaller.Shared/Actions/UpdateAction.cs View File

@ -8,7 +8,7 @@ using YamlDotNet.Serialization;
namespace TrustedUninstaller.Shared.Actions
{
internal class UpdateAction : ITaskAction
internal class UpdateAction : TaskAction, ITaskAction
{
[YamlMember(typeof(string), Alias = "name")]
public string PackageName { get; set; }


+ 1
- 1
TrustedUninstaller.Shared/Actions/UserAction.cs View File

@ -8,7 +8,7 @@ using System.Security.Principal;
namespace TrustedUninstaller.Shared.Actions
{
public class UserAction : ITaskAction
public class UserAction : TaskAction, ITaskAction
{
[YamlMember(typeof(string), Alias = "name")]
public string Username { get; set; } = "";


+ 1
- 1
TrustedUninstaller.Shared/Actions/WriteStatusAction.cs View File

@ -5,7 +5,7 @@ using YamlDotNet.Serialization;
namespace TrustedUninstaller.Shared.Actions
{
public class WriteStatusAction : ITaskAction
public class WriteStatusAction : TaskAction, ITaskAction
{
[YamlMember(typeof(string), Alias = "status")]
public string Status { get; set; }


+ 67
- 14
TrustedUninstaller.Shared/AmeliorationUtil.cs View File

@ -1,6 +1,7 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
@ -11,6 +12,7 @@ using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Xml;
using System.Xml.Serialization;
using TrustedUninstaller.Shared.Actions;
@ -30,9 +32,23 @@ namespace TrustedUninstaller.Shared
public static readonly List<string> ErrorDisplayList = new List<string>();
public static int GetProgressMaximum()
public static int GetProgressMaximum(List<string> options)
{
return Parser.Tasks.Sum(task => task.Actions.Sum(action => action.GetProgressWeight()));
return Parser.Tasks.Sum(task => task.Actions.Sum(action =>
{
var taskAction = (TaskAction)action;
if (!String.IsNullOrEmpty(taskAction.Option) && (options == null ||
(taskAction.Option.StartsWith("!") && options.Contains(taskAction.Option.Substring(1), StringComparer.OrdinalIgnoreCase)) ||
(!options.Contains(taskAction.Option, StringComparer.OrdinalIgnoreCase))))
return 0;
if (!String.IsNullOrEmpty(taskAction.Arch) && (
(taskAction.Arch.StartsWith("!") && String.Equals(taskAction.Arch, RuntimeInformation.ProcessArchitecture.ToString(), StringComparison.OrdinalIgnoreCase)) ||
(!String.Equals(taskAction.Arch, RuntimeInformation.ProcessArchitecture.ToString(), StringComparison.OrdinalIgnoreCase))))
return 0;
return action.GetProgressWeight();
}));
}
public static bool AddTasks(string configPath, string file)
@ -89,16 +105,16 @@ namespace TrustedUninstaller.Shared
try
{
//If the privilege is admin and the program is running as TI, do not do the action.
if (privilege == UninstallTaskPrivilege.Admin && WinUtil.IsTrustedInstaller())
{
return 0;
}
//if (privilege == UninstallTaskPrivilege.Admin && WinUtil.IsTrustedInstaller())
//{
// return 0;
//}
if (privilege == UninstallTaskPrivilege.TrustedInstaller && !WinUtil.IsTrustedInstaller())
if (!WinUtil.IsTrustedInstaller())
{
Console.WriteLine("Relaunching as Trusted Installer!");
var mmf = MemoryMappedFile.CreateNew("ImgA", 5000000);
var mmf = MemoryMappedFile.CreateNew("ImgA", 30000000);
WinUtil.RelaunchAsTrustedInstaller();
if (NativeProcess.Process == null)
{
@ -185,6 +201,18 @@ namespace TrustedUninstaller.Shared
//Check the Actions folder inside the Shared folder for reference.
foreach (ITaskAction action in task.Actions)
{
var taskAction = (TaskAction)action;
if (!String.IsNullOrEmpty(taskAction.Option) && (Playbook.Options == null ||
(taskAction.Option.StartsWith("!") && Playbook.Options.Contains(taskAction.Option.Substring(1), StringComparer.OrdinalIgnoreCase)) ||
(!Playbook.Options.Contains(taskAction.Option, StringComparer.OrdinalIgnoreCase))))
continue;
if (!String.IsNullOrEmpty(taskAction.Arch) && (
(taskAction.Arch.StartsWith("!") && String.Equals(taskAction.Arch, RuntimeInformation.ProcessArchitecture.ToString(), StringComparison.OrdinalIgnoreCase)) ||
(!String.Equals(taskAction.Arch, RuntimeInformation.ProcessArchitecture.ToString(), StringComparison.OrdinalIgnoreCase))))
continue;
int i = 0;
//var actionType = action.GetType().ToString().Replace("TrustedUninstaller.Shared.Actions.", "");
@ -217,6 +245,7 @@ namespace TrustedUninstaller.Shared
break;
}
}
Thread.Sleep(300);
}
Console.WriteLine($"Status: {action.GetStatus()}");
if (i > 0) Thread.Sleep(50);
@ -226,7 +255,8 @@ namespace TrustedUninstaller.Shared
catch (Exception e)
{
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "Critical error while running action.");
Console.WriteLine($":AME-ERROR: Critical error while running action: " + e.Message);
if (!((TaskAction)action).IgnoreErrors)
Console.WriteLine($":AME-ERROR: Critical error while running action: " + e.Message);
}
if (i == 10)
@ -235,7 +265,8 @@ namespace TrustedUninstaller.Shared
ErrorLogger.WriteToErrorLog(errorString, Environment.StackTrace, "Action failed to complete.");
// AmeliorationUtil.ErrorDisplayList.Add(errorString) would NOT work here since this
// might be a separate process, and thus has to be forwarded via the console
Console.WriteLine($":AME-ERROR: {errorString}");
if (!((TaskAction)action).IgnoreErrors)
Console.WriteLine($":AME-ERROR: {errorString}");
//Environment.Exit(-2);
Console.WriteLine($"Action completed. Weight:{action.GetProgressWeight()}");
continue;
@ -260,12 +291,32 @@ namespace TrustedUninstaller.Shared
Playbook pb;
XmlSerializer serializer = new XmlSerializer(typeof(Playbook));
/*serializer.UnknownElement += delegate(object sender, XmlElementEventArgs args)
{
MessageBox.Show(args.Element.Name);
};
serializer.UnknownAttribute += delegate(object sender, XmlAttributeEventArgs args)
{
MessageBox.Show(args.Attr.Name);
};*/
using (XmlReader reader = XmlReader.Create($"{dir}\\playbook.conf"))
{
pb = (Playbook)serializer.Deserialize(reader);
}
pb.Path = dir;
var validateResult = pb.Validate();
if (validateResult != null)
throw new XmlException(validateResult);
if (File.Exists($"{dir}\\options.txt"))
{
pb.Options = new List<string>();
using (var reader = new StreamReader($"{dir}\\options.txt"))
{
while (!reader.EndOfStream)
pb.Options.Add(reader.ReadLine());
}
}
pb.Path = dir;
return Task.FromResult(pb);
}
@ -335,17 +386,19 @@ namespace TrustedUninstaller.Shared
if (Parser.Tasks.Any(x => x.Priority != Parser.Tasks.First().Priority))
Parser.Tasks.Sort(new TaskComparer());
UninstallTaskPrivilege prevPriv = UninstallTaskPrivilege.Admin;
bool launched = false;
foreach (var task in Parser.Tasks.Where(task => task.Actions.Count != 0))
{
try
{
if (prevPriv == UninstallTaskPrivilege.TrustedInstaller && task.Privilege == UninstallTaskPrivilege.TrustedInstaller && !WinUtil.IsTrustedInstaller())
//if (prevPriv == UninstallTaskPrivilege.TrustedInstaller && task.Privilege == UninstallTaskPrivilege.TrustedInstaller && !WinUtil.IsTrustedInstaller())
if (!WinUtil.IsTrustedInstaller() && launched)
{
continue;
}
launched = true;
await DoActions(task, task.Privilege);
prevPriv = task.Privilege;
//prevPriv = task.Privilege;
}
catch (Exception ex)
{


+ 2
- 2
TrustedUninstaller.Shared/Globals.cs View File

@ -9,8 +9,8 @@ namespace TrustedUninstaller.Shared
{
public class Globals
{
public const string CurrentVersion = "0.6.5";
public const double CurrentVersionNumber = 0.65;
public const string CurrentVersion = "0.7";
public const double CurrentVersionNumber = 0.7;
#if DEBUG
public static readonly int WinVer = 19045;
#else


+ 350
- 2
TrustedUninstaller.Shared/Playbook.cs View File

@ -1,10 +1,23 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Management.Automation.Runspaces;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Policy;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;
using System.Xml.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using MessageBox = System.Windows.MessageBox;
namespace TrustedUninstaller.Shared
{
@ -18,25 +31,360 @@ namespace TrustedUninstaller.Shared
public string Username { get; set; }
public string Details { get; set; }
public string Version { get; set; }
[XmlArray]
[XmlArrayItem(ElementName = "CheckboxPage", Type = typeof(CheckboxPage))]
[XmlArrayItem(ElementName = "RadioPage", Type = typeof(RadioPage))]
[XmlArrayItem(ElementName = "RadioImagePage", Type = typeof(RadioImagePage))]
public FeaturePage[] FeaturePages { get; set; }
public string ProgressText { get; set; } = "Deploying the selected Playbook configuration onto the system.";
public int EstimatedMinutes { get; set; } = 25;
#nullable enable
public string[]? SupportedBuilds { get; set; }
public Requirements.Requirement[]? Requirements { get; set; }
public Requirements.Requirement[] Requirements { get; set; } = new Requirements.Requirement[] {};
public string? Git { get; set; }
public string? DonateLink { get; set; }
public string? Website { get; set; }
public string? ProductCode { get; set; }
public string? PasswordReplace { get; set; }
#nullable disable
public bool Overhaul { get; set; } = false;
public string Path { get; set; }
public List<string> Options { get; set; } = null;
public string Validate()
{
if (FeaturePages == null)
return null;
foreach (var rawPage in FeaturePages)
{
if (rawPage.GetType() == typeof(CheckboxPage))
{
var page = (CheckboxPage)rawPage;
if (page.Options.Length > 2 && page.TopLine != null && page.BottomLine != null)
return @$"CheckboxPage with a TopLine and BottomLine must not have more than 2 options.";
if (page.Options.Length > 3 && (page.TopLine != null || page.BottomLine != null))
return @$"CheckboxPage with a TopLine or BottomLine must not have more than 3 options.";
if (page.Options.Length > 4)
return @$"CheckboxPage must not have more than 4 options.";
}
else if (rawPage.GetType() == typeof(RadioPage))
{
var page = (RadioPage)rawPage;
if (page.Options.Length > 2 && page.TopLine != null && page.BottomLine != null)
return @$"RadioPage with a TopLine and BottomLine must not have more than 2 options.";
if (page.Options.Length > 3 && (page.TopLine != null || page.BottomLine != null))
return @$"RadioPage with a TopLine or BottomLine must not have more than 3 options.";
if (page.Options.Length > 4)
return @$"RadioPage must not have more than 4 options.";
if (page.DefaultOption != null && !page.Options.Any(x => x.Name == page.DefaultOption))
return @$"No option matching DefaultOption {page.DefaultOption} in RadioPage.";
}
else if (rawPage.GetType() == typeof(RadioImagePage))
{
var page = (RadioImagePage)rawPage;
if (page.Options.Length > 4)
return @$"RadioImagePage must not have more than 4 options.";
if (page.DefaultOption != null && !page.Options.Any(x => x.Name == page.DefaultOption))
return @$"No option matching DefaultOption {page.DefaultOption} in RadioImagePage.";
}
}
return null;
}
public static double GetVersionNumber(string toBeParsed)
{
// Examples:
// 0.4
// 0.4 Alpha
// 1.0.5
// 1.0.5 Beta
// Remove characters after first space (and the space itself)
if (toBeParsed.IndexOf(' ') >= 0)
toBeParsed = toBeParsed.Substring(0, toBeParsed.IndexOf(' '));
if (toBeParsed.LastIndexOf('.') != toBeParsed.IndexOf('.'))
{
// Example: 1.0.5
toBeParsed = toBeParsed.Remove(toBeParsed.LastIndexOf('.'), 1);
// Result: 1.05
}
return double.Parse(toBeParsed, CultureInfo.InvariantCulture);
}
public double GetVersionNumber()
{
return GetVersionNumber(Version);
}
public async Task<string> LatestPlaybookVersion()
{
if (!IsValidGit())
{
throw new ArgumentException("Link provided is not a proper Git link.");
}
string gitPlatform = GetPlaybookGitPlatform();
string repo = GetRepository();
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("curl/7.55.1"); //Required for GitHub
string url = gitPlatform switch
{
"github.com" => $"https://api.github.com/repos/{repo}/releases",
"gitlab.com" => $"https://gitlab.com/api/v4/projects/{Uri.EscapeDataString(repo)}/releases",
_ => $"https://{gitPlatform}/api/v1/repos/{repo}/releases"
};
var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var array = JArray.Parse(json);
return (string) array.FirstOrDefault()?["tag_name"];
}
public async Task<List<string>> GetPlaybookVersions()
{
if (!IsValidGit())
{
throw new ArgumentException("Link provided is not a proper Git link.");
}
string gitPlatform = GetPlaybookGitPlatform();
string repo = GetRepository();
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("curl/7.55.1"); //Required for GitHub
string url = gitPlatform switch
{
"github.com" => $"https://api.github.com/repos/{repo}/releases",
"gitlab.com" => $"https://gitlab.com/api/v4/projects/{Uri.EscapeDataString(repo)}/releases",
_ => $"https://{gitPlatform}/api/v1/repos/{repo}/releases"
};
var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var array = JArray.Parse(json);
var result = new List<string>();
foreach (var releaseToken in array)
result.Add((string)releaseToken["tag_name"]);
return result;
}
public async Task DownloadLatestPlaybook(BackgroundWorker worker = null)
{
string repo = GetRepository();
string gitPlatform = GetPlaybookGitPlatform();
var httpClient = new WinUtil.HttpProgressClient();
httpClient.Client.DefaultRequestHeaders.UserAgent.ParseAdd("curl/7.55.1"); //Required for GitHub
var downloadUrl = string.Empty;
var downloadDir = System.IO.Path.Combine(Environment.GetEnvironmentVariable("TEMP"), "AME");
var downloadPath = System.IO.Path.Combine(downloadDir, "playbook.apbx");
string baseUrl;
string releasesUrl;
string assetsKey;
string browserDownloadUrlKey;
switch (gitPlatform)
{
case "github.com":
baseUrl = "https://api.github.com";
releasesUrl = $"{baseUrl}/repos/{repo}/releases";
assetsKey = "assets";
browserDownloadUrlKey = "browser_download_url";
break;
case "gitlab.com":
baseUrl = "https://gitlab.com/api/v4";
releasesUrl = $"{baseUrl}/projects/{Uri.EscapeDataString(repo)}/releases";
assetsKey = "assets.links";
browserDownloadUrlKey = "direct_asset_url";
break;
default:
baseUrl = $"https://{gitPlatform}/api/v1";
releasesUrl = $"{baseUrl}/repos/{repo}/releases";
assetsKey = "assets";
browserDownloadUrlKey = "browser_download_url";
break;
}
var releasesResponse = await httpClient.GetAsync(releasesUrl);
releasesResponse.EnsureSuccessStatusCode();
var releasesContent = await releasesResponse.Content.ReadAsStringAsync();
var releases = JArray.Parse(releasesContent);
var release = releases.FirstOrDefault();
long size = 3000000;
if (release?.SelectToken(assetsKey) is JArray assets)
{
var asset = assets.FirstOrDefault(a => a["name"].ToString().EndsWith(".apbx"));
if (asset != null)
{
downloadUrl = asset[browserDownloadUrlKey]?.ToString();
if (asset["size"] != null)
long.TryParse(asset["size"].ToString(), out size);
}
}
if (worker != null)
worker.ReportProgress(10);
// Download the release asset
if (!string.IsNullOrEmpty(downloadUrl))
{
httpClient.Client.DefaultRequestHeaders.Clear();
httpClient.ProgressChanged += (totalFileSize, totalBytesDownloaded, progressPercentage) => {
if (progressPercentage.HasValue && worker != null)
worker.ReportProgress((int)Math.Ceiling(10 + (progressPercentage.Value * 0.7)));
};
await httpClient.StartDownload(downloadUrl, downloadPath, size);
}
httpClient.Dispose();
}
public string GetRepository()
{
if (Git == null)
{
return null;
}
var urlSegments = Git.Replace("https://", "").Replace("http://", "").Split('/');
return urlSegments[1] +"/"+ urlSegments[2];
}
public string GetPlaybookGitPlatform()
{
if (this.Git == null)
{
throw new NullReferenceException("No Git link available.");
}
return new Uri(Git).Host;
}
public bool IsValidGit()
{
if (Git == null)
{
throw new NullReferenceException("No Git link available.");
}
return Regex.IsMatch(Git, "((git|ssh|http(s)?)|(git@[\\w\\.]+))(:(//)?)([\\w\\.@\\:/\\-~]+)(/)?");;
}
public override string ToString()
{
return $"Name: {Name}\nDescription: {Description}\nUsername: {Username}\nDetails: {Details}\nRequirements: {Requirements}.";
}
[XmlType("CheckboxPage")]
public class CheckboxPage : FeaturePage
{
public class CheckboxOption : Option
{
[XmlAttribute]
public bool IsChecked { get; set; } = true;
}
[XmlArray]
[XmlArrayItem(ElementName = "CheckboxOption", Type = typeof(CheckboxOption))]
public Option[] Options { get; set; }
}
public class RadioPage : FeaturePage
{
[XmlAttribute]
public string DefaultOption { get; set; } = null;
public class RadioOption : Option
{
}
[XmlArray]
[XmlArrayItem(ElementName = "RadioOption", Type = typeof(RadioOption))]
public Option[] Options { get; set; }
}
public class RadioImagePage : FeaturePage
{
[XmlAttribute]
public string DefaultOption { get; set; } = null;
public class RadioImageOption : Option
{
public string FileName { get; set; } = null;
public bool Fill { get; set; } = false;
[XmlAttribute]
public bool None { get; set; } = false;
public string GradientTopColor { get; set; } = null;
public string GradientBottomColor { get; set; } = null;
}
[XmlArray]
[XmlArrayItem(ElementName = "RadioImageOption", Type = typeof(RadioImageOption))]
public Option[] Options { get; set; }
[XmlAttribute]
public bool CheckDefaultBrowser { get; set; } = false;
}
public class FeaturePage
{
[XmlAttribute]
public string DependsOn { get; set; } = null;
[XmlAttribute]
public bool IsRequired { get; set; } = false;
public Line TopLine { get; set; } = null;
public Line BottomLine { get; set; } = null;
public class Option
{
public string Name { get; set; } = null;
public virtual string Text { get; set; }
[XmlAttribute]
public string DependsOn { get; set; } = null;
}
public class Line
{
[XmlAttribute("Text")]
public string Text { get; set; }
[XmlAttribute("Link")]
public string Link { get; set; } = null;
}
[XmlAttribute]
public string Description { get; set; }
}
}
}

+ 1
- 0
TrustedUninstaller.Shared/ProviderStatus.cs View File

@ -39,6 +39,7 @@ namespace TrustedUninstaller.Shared
public SignatureStatusFlags SignatureStatus;
public AVStatusFlags AVStatus;
public ProviderFlags SecurityProvider;
public bool FileExists;
public string DisplayName;
}
}

+ 34
- 7
TrustedUninstaller.Shared/Requirements.cs View File

@ -14,6 +14,7 @@ using System.Xml.Serialization;
using Microsoft.Win32;
using TrustedUninstaller.Shared;
using TrustedUninstaller.Shared.Actions;
using TrustedUninstaller.Shared.Tasks;
namespace TrustedUninstaller.Shared
{
@ -36,12 +37,16 @@ namespace TrustedUninstaller.Shared
Activation = 5,
[XmlEnum("NoAntivirus")]
NoAntivirus = 6,
[XmlEnum("LocalAccounts")]
LocalAccounts = 11,
[XmlEnum("PasswordSet")]
PasswordSet = 7,
PasswordSet = 11,
[XmlEnum("AdministratorPasswordSet")]
AdministratorPasswordSet = 8,
[XmlEnum("PluggedIn")]
PluggedIn = 9,
[XmlEnum("NoTweakware")]
NoTweakware = 10,
}
public static async Task<Requirement[]> MetRequirements(this Requirement[] requirements)
@ -59,7 +64,7 @@ namespace TrustedUninstaller.Shared
else metRequirements.Add(Requirement.NoInternet);
if (requirements.Contains(Requirement.NoAntivirus))
if (await new NoAntivirus().IsMet()) metRequirements.Add(Requirement.NoAntivirus);
if (true) metRequirements.Add(Requirement.NoAntivirus);
if (requirements.Contains(Requirement.NoPendingUpdates))
if (await new NoPendingUpdates().IsMet()) metRequirements.Add(Requirement.NoPendingUpdates);
@ -72,9 +77,9 @@ namespace TrustedUninstaller.Shared
if (requirements.Contains(Requirement.DefenderToggled))
if (await new DefenderDisabled().IsMet()) metRequirements.Add(Requirement.DefenderToggled);
if (requirements.Contains(Requirement.PasswordSet))
metRequirements.Add(Requirement.PasswordSet);
if (requirements.Contains(Requirement.LocalAccounts))
metRequirements.Add(Requirement.LocalAccounts);
if (requirements.Contains(Requirement.AdministratorPasswordSet))
metRequirements.Add(Requirement.AdministratorPasswordSet);
@ -210,13 +215,35 @@ namespace TrustedUninstaller.Shared
{
public async Task<bool> IsMet()
{
if (Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\WinDefend") != null)
try
{
if (Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Services\WinDefend") != null && new RegistryValueAction() { KeyName = @"SYSTEM\CurrentControlSet\Services\WinDefend", Value = "Start", Data = 4, Type = RegistryValueType.REG_DWORD }.GetStatus() != UninstallTaskStatus.Completed)
return false;
if (Registry.ClassesRoot.OpenSubKey(@"CLSID\{2781761E-28E0-4109-99FE-B9D127C57AFE}\InprocServer32") != null) return false;
if (Registry.ClassesRoot.OpenSubKey(@"CLSID\{a463fcb9-6b1c-4e0d-a80b-a2ca7999e25d}\InprocServer32") != null) return false;
var key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity");
if (key != null && (int)key.GetValue("Enabled") != 0)
{
return false;
}
}
catch (Exception e)
{
}
return RemnantsOnly();
}
public static bool RemnantsOnly()
{
if (Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Services\WinDefend") != null)
return false;
return Process.GetProcessesByName("MsMpEng").Length == 0;
}
public async Task<bool> Meet()
{
throw new NotImplementedException();
OnProgressAdded(30);
try
{
@ -244,7 +271,7 @@ namespace TrustedUninstaller.Shared
};
await defenderService.RunTask();
OnProgressAdded(20);
// MpOAV.dll normally in use by a lot of processes. This prevents that.
// MpOAV.dll normally is in use by a lot of processes. This prevents that.
var MpOAVCLSID = new RunAction()
{
Exe = $"NSudoLC.exe",


+ 14
- 0
TrustedUninstaller.Shared/Tasks/TaskAction.cs View File

@ -0,0 +1,14 @@
using YamlDotNet.Serialization;
namespace TrustedUninstaller.Shared.Tasks
{
public class TaskAction
{
[YamlMember(typeof(bool), Alias = "ignoreErrors")]
public bool IgnoreErrors { get; set; } = false;
[YamlMember(typeof(string), Alias = "option")]
public string Option { get; set; } = null;
[YamlMember(typeof(string), Alias = "cpuArch")]
public string Arch { get; set; } = null;
}
}

+ 9
- 1
TrustedUninstaller.Shared/Tasks/UninstallTask.cs View File

@ -19,12 +19,20 @@ namespace TrustedUninstaller.Shared.Tasks
public int? MaxVersion { get; set; }
#nullable disable
public UninstallTaskStatus Status { get; set; } = UninstallTaskStatus.ToDo;
public List<ITaskAction> Actions { get; set; }
public List<ITaskAction> Actions { get; set; } = new List<ITaskAction>();
public int Priority { get; set; } = 1;
public UninstallTaskPrivilege Privilege { get; set; } = UninstallTaskPrivilege.Admin;
public List<string> Features { get; set; } = new List<string>();
public List<string> Tasks
{
set => Features = value;
get => Features;
}
public void Update()
{
/*


+ 24
- 5
TrustedUninstaller.Shared/TrustedUninstaller.Shared.csproj View File

@ -14,6 +14,9 @@
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
<AllowedReferenceRelatedFileExtensions>
*.xml
</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>
<PropertyGroup>
<PathMap>$([System.IO.Path]::GetFullPath('$(SolutionDir)'))=./</PathMap>
@ -40,6 +43,14 @@
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Single File|x64' ">
<OutputPath>bin\x64\Release\</OutputPath>
<PlatformTarget>x64</PlatformTarget>
<Optimize>true</Optimize>
<DebugType>embedded</DebugType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>SINGLE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationFramework" />
<Reference Include="System" />
@ -61,10 +72,8 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Windows">
<HintPath>bin\Debug\Windows.winmd</HintPath>
<Private>True</Private>
<EmbedInteropTypes>False</EmbedInteropTypes>
<Reference Include="YamlDotNet">
<HintPath>..\TrustedUninstaller.GUI\bin\x64\Release\YamlDotNet.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@ -98,6 +107,7 @@
<Compile Include="Parser\TaskActionResolver.cs" />
<Compile Include="Predicates\IPredicate.cs" />
<Compile Include="Requirements.cs" />
<Compile Include="Tasks\TaskAction.cs" />
<Compile Include="Tasks\UninstallTaskPrivilege.cs" />
<Compile Include="Tasks\ITaskAction.cs" />
<Compile Include="Tasks\UninstallTaskStatus.cs" />
@ -121,7 +131,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.PowerShell.5.ReferenceAssemblies" Version="1.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.IO" Version="4.3.0" />
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
@ -145,6 +155,15 @@
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Single File|x64' ">
<PostBuildEvent>for /f "usebackq delims=" %%A in (`DIR /B /S /A:d "$(SolutionDir)" ^| FINDSTR /R /c:".*\\bin\\.*\\de$" /c:".*\\bin\\.*\\en$" /c:".*\\bin\\.*\\es$" /c:".*\\bin\\.*\\fr$" /c:".*\\bin\\.*\\it$" /c:".*\\bin\\.*\\ja$" /c:".*\\bin\\.*\\ko$" /c:".*\\bin\\.*\\ru$" /c:".*\\bin\\.*\\zh-Hans$" /c:".*\\bin\\.*\\zh-Hant$" /c:".*\\bin\\.*\\pl$" /c:".*\\bin\\.*\\zh-CN$"`) do (RMDIR /Q /S "%%A" &amp; cmd /c "exit /b 0")
cmd /c "echo Compiled&gt; "$(SolutionDir)\TrustedUninstaller.GUI\gui-builder\Compiled.txt""
</PostBuildEvent>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Single File|x64' ">
<PreBuildEvent>PowerShell -NoP -C "Get-Process 'ame-builder' -EA SilentlyContinue | Wait-Process -EA SilentlyContinue; EXIT 0"
PowerShell -NoP -C "Start-Process '$(SolutionDir)\TrustedUninstaller.GUI\gui-builder\ame-builder.exe' -ArgumentList 'Shared' -WindowStyle Hidden"</PreBuildEvent>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' != 'Single File|x64' ">
<PostBuildEvent>for /f "usebackq delims=" %%A in (`DIR /B /S /A:d "$(SolutionDir)" ^| FINDSTR /R /c:".*\\bin\\.*\\de$" /c:".*\\bin\\.*\\en$" /c:".*\\bin\\.*\\es$" /c:".*\\bin\\.*\\fr$" /c:".*\\bin\\.*\\it$" /c:".*\\bin\\.*\\ja$" /c:".*\\bin\\.*\\ko$" /c:".*\\bin\\.*\\ru$" /c:".*\\bin\\.*\\zh-Hans$" /c:".*\\bin\\.*\\zh-Hant$" /c:".*\\bin\\.*\\pl$" /c:".*\\bin\\.*\\zh-CN$"`) do (RMDIR /Q /S "%%A" &amp; cmd /c "exit /b 0")</PostBuildEvent>
</PropertyGroup>

+ 105
- 92
TrustedUninstaller.Shared/WinUtil.cs View File

@ -173,7 +173,7 @@ namespace TrustedUninstaller.Shared
select g).FirstOrDefault();
return msAccount == null ? Environment.UserName : msAccount.Substring(@"MicrosoftAccount\".Length);
}
public static bool IsLocalAccount()
{
var wi = WindowsIdentity.GetCurrent();
@ -190,15 +190,14 @@ namespace TrustedUninstaller.Shared
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)]
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);
@ -206,7 +205,7 @@ namespace TrustedUninstaller.Shared
{
// Microsoft-Windows-Security-SPP GUID
// http://technet.microsoft.com/en-us/library/dd772270.aspx
var windowsSlid = new SLID("55c92734-d682-4d71-983e-d6ec3f16059f");
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
@ -214,20 +213,21 @@ namespace TrustedUninstaller.Shared
#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();
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);
var currentPrincipal = (WindowsPrincipal) Thread.CurrentPrincipal;
var groups = GetWindowsGroups((WindowsIdentity) currentPrincipal.Identity);
return groups.Any(group => group == groupName);
}
@ -554,7 +554,8 @@ namespace TrustedUninstaller.Shared
DisplayName = result["displayName"].ToString(),
AVStatus = enabled ? AVStatusFlags.Enabled : AVStatusFlags.Unknown,
SecurityProvider = ProviderFlags.ANTIVIRUS,
SignatureStatus = outdated ? SignatureStatusFlags.OutOfDate : SignatureStatusFlags.UpToDate
SignatureStatus = outdated ? SignatureStatusFlags.OutOfDate : SignatureStatusFlags.UpToDate,
FileExists = File.Exists(result["pathToSignedProductExe"].ToString())
};
avList.Add(av);
}
@ -682,87 +683,7 @@ namespace TrustedUninstaller.Shared
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();
@ -781,8 +702,6 @@ namespace TrustedUninstaller.Shared
}
}
public class RegistryManager
{
[DllImport("advapi32.dll", SetLastError = true)]
@ -933,5 +852,99 @@ namespace TrustedUninstaller.Shared
}
}
}
public class HttpProgressClient : IDisposable
{
private string _downloadUrl;
private string _destinationFilePath;
public HttpClient Client;
public delegate void ProgressChangedHandler(long? totalFileSize, long totalBytesDownloaded, double? progressPercentage);
public event ProgressChangedHandler ProgressChanged;
public HttpProgressClient()
{
Client = new HttpClient { Timeout = TimeSpan.FromDays(1) };
}
public async Task StartDownload(string downloadUrl, string destinationFilePath, long? size = null)
{
_downloadUrl = downloadUrl;
_destinationFilePath = destinationFilePath;
using (var response = await Client.GetAsync(_downloadUrl, HttpCompletionOption.ResponseHeadersRead))
await DownloadFileFromHttpResponseMessage(response, size);
}
public Task<HttpResponseMessage> GetAsync(string link)
{
return Client.GetAsync(link);
}
private async Task DownloadFileFromHttpResponseMessage(HttpResponseMessage response, long? size)
{
response.EnsureSuccessStatusCode();
if (!size.HasValue)
size = response.Content.Headers.ContentLength;
using (var contentStream = await response.Content.ReadAsStreamAsync())
await ProcessContentStream(size, contentStream);
}
private async Task ProcessContentStream(long? totalDownloadSize, Stream contentStream)
{
var totalBytesRead = 0L;
var readCount = 0L;
var buffer = new byte[8192];
var isMoreToRead = true;
using (var fileStream = new FileStream(_destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
{
do
{
var bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
isMoreToRead = false;
TriggerProgressChanged(totalDownloadSize, totalBytesRead);
continue;
}
await fileStream.WriteAsync(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
readCount += 1;
if (readCount % 50 == 0)
TriggerProgressChanged(totalDownloadSize, totalBytesRead);
}
while (isMoreToRead);
}
}
private void TriggerProgressChanged(long? totalDownloadSize, long totalBytesRead)
{
if (ProgressChanged == null)
return;
double? progressPercentage = null;
if (totalDownloadSize.HasValue)
{
progressPercentage = Math.Round((double)totalBytesRead / totalDownloadSize.Value * 100, 2);
}
ProgressChanged(totalDownloadSize, totalBytesRead, progressPercentage);
}
public void Dispose()
{
Client?.Dispose();
}
}
}
}

BIN
Windows.winmd View File


BIN
ameliorated_logo.png View File

Before After
Width: 1039  |  Height: 905  |  Size: 80 KiB

Loading…
Cancel
Save