@ -0,0 +1,568 @@ | |||
# AME GUI Resource Playbooks | |||
TrustedUninstaller.GUI/resources/Shared-PB/* | |||
TrustedUninstaller.GUI/resources/W10-PB/* | |||
TrustedUninstaller.GUI/resources/W11-PB/* | |||
# GUI Builder Temp Files | |||
TrustedUninstaller.GUI/gui-builder/Compiled.txt | |||
TrustedUninstaller.GUI/gui-builder/BuildOut-* | |||
!*.gitkeep | |||
# User-specific files | |||
*.rsuser | |||
*.suo | |||
*.user | |||
*.userosscache | |||
*.sln.docstates | |||
# User-specific files (MonoDevelop/Xamarin Studio) | |||
*.userprefs | |||
# Mono auto generated files | |||
mono_crash.* | |||
# Build results | |||
[Dd]ebug/ | |||
[Dd]ebugPublic/ | |||
[Rr]elease/ | |||
[Rr]eleases/ | |||
x64/ | |||
x86/ | |||
[Ww][Ii][Nn]32/ | |||
[Aa][Rr][Mm]/ | |||
[Aa][Rr][Mm]64/ | |||
bld/ | |||
[Bb]in/ | |||
[Oo]bj/ | |||
[Ll]og/ | |||
[Ll]ogs/ | |||
# Visual Studio 2015/2017 cache/options directory | |||
.vs/ | |||
# Uncomment if you have tasks that create the project's static files in wwwroot | |||
#wwwroot/ | |||
# Visual Studio 2017 auto generated files | |||
Generated\ Files/ | |||
# MSTest test Results | |||
[Tt]est[Rr]esult*/ | |||
[Bb]uild[Ll]og.* | |||
# NUnit | |||
*.VisualState.xml | |||
TestResult.xml | |||
nunit-*.xml | |||
# Build Results of an ATL Project | |||
[Dd]ebugPS/ | |||
[Rr]eleasePS/ | |||
dlldata.c | |||
# Benchmark Results | |||
BenchmarkDotNet.Artifacts/ | |||
# .NET Core | |||
project.lock.json | |||
project.fragment.lock.json | |||
artifacts/ | |||
# ASP.NET Scaffolding | |||
ScaffoldingReadMe.txt | |||
# StyleCop | |||
StyleCopReport.xml | |||
# Files built by Visual Studio | |||
*_i.c | |||
*_p.c | |||
*_h.h | |||
*.ilk | |||
*.meta | |||
*.obj | |||
*.iobj | |||
*.pch | |||
*.pdb | |||
*.ipdb | |||
*.pgc | |||
*.pgd | |||
*.rsp | |||
*.sbr | |||
*.tlb | |||
*.tli | |||
*.tlh | |||
*.tmp | |||
*.tmp_proj | |||
*_wpftmp.csproj | |||
*.log | |||
*.tlog | |||
*.vspscc | |||
*.vssscc | |||
.builds | |||
*.pidb | |||
*.svclog | |||
*.scc | |||
# Chutzpah Test files | |||
_Chutzpah* | |||
# Visual C++ cache files | |||
ipch/ | |||
*.aps | |||
*.ncb | |||
*.opendb | |||
*.opensdf | |||
*.sdf | |||
*.cachefile | |||
*.VC.db | |||
*.VC.VC.opendb | |||
# Visual Studio profiler | |||
*.psess | |||
*.vsp | |||
*.vspx | |||
*.sap | |||
# Visual Studio Trace Files | |||
*.e2e | |||
# TFS 2012 Local Workspace | |||
$tf/ | |||
# Guidance Automation Toolkit | |||
*.gpState | |||
# ReSharper is a .NET coding add-in | |||
_ReSharper*/ | |||
*.[Rr]e[Ss]harper | |||
*.DotSettings.user | |||
# TeamCity is a build add-in | |||
_TeamCity* | |||
# DotCover is a Code Coverage Tool | |||
*.dotCover | |||
# AxoCover is a Code Coverage Tool | |||
.axoCover/* | |||
!.axoCover/settings.json | |||
# Coverlet is a free, cross platform Code Coverage Tool | |||
coverage*.json | |||
coverage*.xml | |||
coverage*.info | |||
# Visual Studio code coverage results | |||
*.coverage | |||
*.coveragexml | |||
# NCrunch | |||
_NCrunch_* | |||
.*crunch*.local.xml | |||
nCrunchTemp_* | |||
# MightyMoose | |||
*.mm.* | |||
AutoTest.Net/ | |||
# Web workbench (sass) | |||
.sass-cache/ | |||
# Installshield output folder | |||
[Ee]xpress/ | |||
# DocProject is a documentation generator add-in | |||
DocProject/buildhelp/ | |||
DocProject/Help/*.HxT | |||
DocProject/Help/*.HxC | |||
DocProject/Help/*.hhc | |||
DocProject/Help/*.hhk | |||
DocProject/Help/*.hhp | |||
DocProject/Help/Html2 | |||
DocProject/Help/html | |||
# Click-Once directory | |||
publish/ | |||
# Publish Web Output | |||
*.[Pp]ublish.xml | |||
*.azurePubxml | |||
# Note: Comment the next line if you want to checkin your web deploy settings, | |||
# but database connection strings (with potential passwords) will be unencrypted | |||
*.pubxml | |||
*.publishproj | |||
# Microsoft Azure Web App publish settings. Comment the next line if you want to | |||
# checkin your Azure Web App publish settings, but sensitive information contained | |||
# in these scripts will be unencrypted | |||
PublishScripts/ | |||
# NuGet Packages | |||
*.nupkg | |||
# NuGet Symbol Packages | |||
*.snupkg | |||
# The packages folder can be ignored because of Package Restore | |||
**/[Pp]ackages/* | |||
# except build/, which is used as an MSBuild target. | |||
!**/[Pp]ackages/build/ | |||
# Uncomment if necessary however generally it will be regenerated when needed | |||
#!**/[Pp]ackages/repositories.config | |||
# NuGet v3's project.json files produces more ignorable files | |||
*.nuget.props | |||
*.nuget.targets | |||
# Nuget personal access tokens and Credentials | |||
nuget.config | |||
# Microsoft Azure Build Output | |||
csx/ | |||
*.build.csdef | |||
# Microsoft Azure Emulator | |||
ecf/ | |||
rcf/ | |||
# Windows Store app package directories and files | |||
AppPackages/ | |||
BundleArtifacts/ | |||
Package.StoreAssociation.xml | |||
_pkginfo.txt | |||
*.appx | |||
*.appxbundle | |||
*.appxupload | |||
# Visual Studio cache files | |||
# files ending in .cache can be ignored | |||
*.[Cc]ache | |||
# but keep track of directories ending in .cache | |||
!?*.[Cc]ache/ | |||
# Others | |||
ClientBin/ | |||
~$* | |||
*~ | |||
*.dbmdl | |||
*.dbproj.schemaview | |||
*.jfm | |||
*.pfx | |||
*.publishsettings | |||
orleans.codegen.cs | |||
# Including strong name files can present a security risk | |||
# (https://github.com/github/gitignore/pull/2483#issue-259490424) | |||
#*.snk | |||
# Since there are multiple workflows, uncomment next line to ignore bower_components | |||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) | |||
#bower_components/ | |||
# RIA/Silverlight projects | |||
Generated_Code/ | |||
# Backup & report files from converting an old project file | |||
# to a newer Visual Studio version. Backup files are not needed, | |||
# because we have git ;-) | |||
_UpgradeReport_Files/ | |||
Backup*/ | |||
UpgradeLog*.XML | |||
UpgradeLog*.htm | |||
ServiceFabricBackup/ | |||
*.rptproj.bak | |||
# SQL Server files | |||
*.mdf | |||
*.ldf | |||
*.ndf | |||
# Business Intelligence projects | |||
*.rdl.data | |||
*.bim.layout | |||
*.bim_*.settings | |||
*.rptproj.rsuser | |||
*- [Bb]ackup.rdl | |||
*- [Bb]ackup ([0-9]).rdl | |||
*- [Bb]ackup ([0-9][0-9]).rdl | |||
# Microsoft Fakes | |||
FakesAssemblies/ | |||
# GhostDoc plugin setting file | |||
*.GhostDoc.xml | |||
# Node.js Tools for Visual Studio | |||
.ntvs_analysis.dat | |||
node_modules/ | |||
# Visual Studio 6 build log | |||
*.plg | |||
# Visual Studio 6 workspace options file | |||
*.opt | |||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) | |||
*.vbw | |||
# Visual Studio LightSwitch build output | |||
**/*.HTMLClient/GeneratedArtifacts | |||
**/*.DesktopClient/GeneratedArtifacts | |||
**/*.DesktopClient/ModelManifest.xml | |||
**/*.Server/GeneratedArtifacts | |||
**/*.Server/ModelManifest.xml | |||
_Pvt_Extensions | |||
# Paket dependency manager | |||
.paket/paket.exe | |||
paket-files/ | |||
# FAKE - F# Make | |||
.fake/ | |||
# CodeRush personal settings | |||
.cr/personal | |||
# Python Tools for Visual Studio (PTVS) | |||
__pycache__/ | |||
*.pyc | |||
# Cake - Uncomment if you are using it | |||
# tools/** | |||
# !tools/packages.config | |||
# Tabs Studio | |||
*.tss | |||
# Telerik's JustMock configuration file | |||
*.jmconfig | |||
# BizTalk build output | |||
*.btp.cs | |||
*.btm.cs | |||
*.odx.cs | |||
*.xsd.cs | |||
# OpenCover UI analysis results | |||
OpenCover/ | |||
# Azure Stream Analytics local run output | |||
ASALocalRun/ | |||
# MSBuild Binary and Structured Log | |||
*.binlog | |||
# NVidia Nsight GPU debugger configuration file | |||
*.nvuser | |||
# MFractors (Xamarin productivity tool) working folder | |||
.mfractor/ | |||
# Local History for Visual Studio | |||
.localhistory/ | |||
# BeatPulse healthcheck temp database | |||
healthchecksdb | |||
# Backup folder for Package Reference Convert tool in Visual Studio 2017 | |||
MigrationBackup/ | |||
# Ionide (cross platform F# VS Code tools) working folder | |||
.ionide/ | |||
# Fody - auto-generated XML schema | |||
FodyWeavers.xsd | |||
# VS Code files for those working on multiple tools | |||
.vscode/* | |||
!.vscode/settings.json | |||
!.vscode/tasks.json | |||
!.vscode/launch.json | |||
!.vscode/extensions.json | |||
*.code-workspace | |||
# Local History for Visual Studio Code | |||
.history/ | |||
# Windows Installer files from build outputs | |||
*.cab | |||
*.msi | |||
*.msix | |||
*.msm | |||
*.msp | |||
# JetBrains Rider | |||
.idea/ | |||
*.sln.iml | |||
### Git ### | |||
# Created by git for backups. To disable backups in Git: | |||
# $ git config --global mergetool.keepBackup false | |||
*.orig | |||
# Created by git when using merge tools for conflicts | |||
*.BACKUP.* | |||
*.BASE.* | |||
*.LOCAL.* | |||
*.REMOTE.* | |||
*_BACKUP_*.txt | |||
*_BASE_*.txt | |||
*_LOCAL_*.txt | |||
*_REMOTE_*.txt | |||
### vs ### | |||
# User-specific files | |||
# User-specific files (MonoDevelop/Xamarin Studio) | |||
# Mono auto generated files | |||
# Build results | |||
# Visual Studio 2015/2017 cache/options directory | |||
# Uncomment if you have tasks that create the project's static files in wwwroot | |||
# Visual Studio 2017 auto generated files | |||
# MSTest test Results | |||
# NUnit | |||
# Build Results of an ATL Project | |||
# Benchmark Results | |||
# .NET Core | |||
# StyleCop | |||
# Files built by Visual Studio | |||
# Chutzpah Test files | |||
# Visual C++ cache files | |||
# Visual Studio profiler | |||
# Visual Studio Trace Files | |||
# TFS 2012 Local Workspace | |||
# Guidance Automation Toolkit | |||
# ReSharper is a .NET coding add-in | |||
# TeamCity is a build add-in | |||
# DotCover is a Code Coverage Tool | |||
# AxoCover is a Code Coverage Tool | |||
# Coverlet is a free, cross platform Code Coverage Tool | |||
coverage*[.json, .xml, .info] | |||
# Visual Studio code coverage results | |||
# NCrunch | |||
# MightyMoose | |||
# Web workbench (sass) | |||
# Installshield output folder | |||
# DocProject is a documentation generator add-in | |||
# Click-Once directory | |||
# Publish Web Output | |||
# Note: Comment the next line if you want to checkin your web deploy settings, | |||
# but database connection strings (with potential passwords) will be unencrypted | |||
# Microsoft Azure Web App publish settings. Comment the next line if you want to | |||
# checkin your Azure Web App publish settings, but sensitive information contained | |||
# in these scripts will be unencrypted | |||
# NuGet Packages | |||
# NuGet Symbol Packages | |||
# The packages folder can be ignored because of Package Restore | |||
# except build/, which is used as an MSBuild target. | |||
# Uncomment if necessary however generally it will be regenerated when needed | |||
# NuGet v3's project.json files produces more ignorable files | |||
# Microsoft Azure Build Output | |||
# Microsoft Azure Emulator | |||
# Windows Store app package directories and files | |||
# Visual Studio cache files | |||
# files ending in .cache can be ignored | |||
# but keep track of directories ending in .cache | |||
# Others | |||
# Including strong name files can present a security risk | |||
# (https://github.com/github/gitignore/pull/2483#issue-259490424) | |||
# Since there are multiple workflows, uncomment next line to ignore bower_components | |||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) | |||
# RIA/Silverlight projects | |||
# Backup & report files from converting an old project file | |||
# to a newer Visual Studio version. Backup files are not needed, | |||
# because we have git ;-) | |||
# SQL Server files | |||
# Business Intelligence projects | |||
# Microsoft Fakes | |||
# GhostDoc plugin setting file | |||
# Node.js Tools for Visual Studio | |||
# Visual Studio 6 build log | |||
# Visual Studio 6 workspace options file | |||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) | |||
# Visual Studio LightSwitch build output | |||
# Paket dependency manager | |||
# FAKE - F# Make | |||
# CodeRush personal settings | |||
# Python Tools for Visual Studio (PTVS) | |||
# Cake - Uncomment if you are using it | |||
# tools/** | |||
# !tools/packages.config | |||
# Tabs Studio | |||
# Telerik's JustMock configuration file | |||
# BizTalk build output | |||
# OpenCover UI analysis results | |||
# Azure Stream Analytics local run output | |||
# MSBuild Binary and Structured Log | |||
# NVidia Nsight GPU debugger configuration file | |||
# MFractors (Xamarin productivity tool) working folder | |||
# Local History for Visual Studio | |||
# BeatPulse healthcheck temp database | |||
# Backup folder for Package Reference Convert tool in Visual Studio 2017 | |||
# Ionide (cross platform F# VS Code tools) working folder |
@ -0,0 +1,25 @@ | |||
# TrustedUninstaller CLI | |||
CLI tool for running Playbooks. | |||
## Usage | |||
See our [CLI guide](hgttps:/d) for detailed instructions. | |||
## Compilation | |||
1. Clone the repository | |||
git clone https://git.ameliorated.info/Styris/trusted-uninstaller-cli.git | |||
2. Open TrustedUninstaller.sln with Visual Studo or JetBrains Rider | |||
3. Set the configuration to **Release** | |||
4. Build TrustedUninstaller.CLI | |||
## 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. | |||
Since this project makes major alterations to the operating system and has the ability to install software during this process, it is imperative that we **provide its source code for auditing purposes.** | |||
This has not only helped us build trust, and make our project stand out among the crowd, but has also led to many community contributions along the way. |
@ -0,0 +1,26 @@ | |||
<?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> |
@ -0,0 +1,131 @@ | |||
using System; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Reflection; | |||
using System.Windows; | |||
using TrustedUninstaller.Shared; | |||
namespace TrustedUninstaller.CLI | |||
{ | |||
public class CLI | |||
{ | |||
private static async System.Threading.Tasks.Task<int> Main(string[] args) | |||
{ | |||
//Needed after defender removal's reboot, the "current directory" will be set to System32 | |||
//After the auto start up. | |||
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); | |||
DualOut.Init(); | |||
if (!WinUtil.IsAdministrator()) | |||
{ | |||
System.Console.Error.WriteLine("This program must be launched as an Administrator!"); | |||
return -1; | |||
} | |||
#if !DEBUG | |||
/* | |||
if (!WinUtil.IsGenuineWindows()) | |||
{ | |||
System.Console.Error.WriteLine("This program only works on genuine Windows copies!"); | |||
return -1; | |||
} | |||
*/ | |||
#endif | |||
if (args.Length < 1 || !Directory.Exists(args[0])) | |||
{ | |||
Console.WriteLine("No Playbook selected: Use the GUI to select a playbook to run."); | |||
return -1; | |||
} | |||
AmeliorationUtil.Playbook = await AmeliorationUtil.DeserializePlaybook(args[0]); | |||
if (!Directory.Exists($"{AmeliorationUtil.Playbook.Path}\\Configuration")) | |||
{ | |||
Console.WriteLine("Creating Configuration folder..."); | |||
Directory.CreateDirectory($"{AmeliorationUtil.Playbook.Path}\\Configuration"); | |||
} | |||
if (Directory.GetFiles($"{AmeliorationUtil.Playbook.Path}\\Configuration").Length == 0) | |||
{ | |||
Console.WriteLine("Configuration folder is empty, put YAML files in it and restart the application."); | |||
Console.WriteLine($"Current directory: {Directory.GetCurrentDirectory()}"); | |||
return -1; | |||
} | |||
ExtractResourceFolder("resources", Directory.GetCurrentDirectory()); | |||
await AmeliorationUtil.StartAmelioration(); | |||
return 0; | |||
} | |||
public static void ExtractResourceFolder(string resource, string dir, bool overwrite = false) | |||
{ | |||
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); | |||
Assembly assembly = Assembly.GetExecutingAssembly(); | |||
var resources = assembly.GetManifestResourceNames().Where(res => res.StartsWith($"TrustedUninstaller.CLI.Properties")); | |||
foreach (var obj in resources) | |||
{ | |||
using (UnmanagedMemoryStream stream = (UnmanagedMemoryStream)assembly.GetManifestResourceStream(obj)) | |||
{ | |||
int MB = 1024 * 1024; | |||
int offset = -MB; | |||
var file = dir + $"\\{obj.Substring($"TrustedUninstaller.CLI.Properties.{resource}.".Length).Replace("---", "\\")}"; | |||
if (file.EndsWith(".gitkeep")) continue; | |||
var fileDir = Path.GetDirectoryName(file); | |||
if (fileDir != null && !Directory.Exists(fileDir)) Directory.CreateDirectory(fileDir); | |||
if (File.Exists(file) && !overwrite) continue; | |||
if (File.Exists(file) && overwrite) | |||
{ | |||
try | |||
{ | |||
File.Delete(file); | |||
} | |||
catch (Exception e) | |||
{ | |||
if (!Directory.Exists(Directory.GetCurrentDirectory() + "\\Logs")) | |||
Directory.CreateDirectory(Directory.GetCurrentDirectory() + "\\Logs"); | |||
using (var writer = new StreamWriter(Path.Combine(Directory.GetCurrentDirectory(), "Logs\\ErrorLog.txt"), true)) | |||
{ | |||
writer.WriteLine($"Title: Could not delete existing resource file {file}.\r\nMessage: {e.Message}\r\n\r\nStackTrace: {e.StackTrace}"); | |||
writer.WriteLine("\r\nDate/Time: " + DateTime.Now); | |||
writer.WriteLine("============================================"); | |||
} | |||
continue; | |||
} | |||
} | |||
using (FileStream fsDlst = new FileStream(file, FileMode.CreateNew, FileAccess.Write)) | |||
{ | |||
while (offset + MB < stream.Length) | |||
{ | |||
var buffer = new byte[MB]; | |||
offset += MB; | |||
if (offset + MB > stream.Length) | |||
{ | |||
var bytesLeft = stream.Length - offset; | |||
buffer = new byte[bytesLeft]; | |||
} | |||
stream.Seek(offset, SeekOrigin.Begin); | |||
stream.Read(buffer, 0, buffer.Length); | |||
fsDlst.Seek(offset, SeekOrigin.Begin); | |||
fsDlst.Write(buffer, 0, buffer.Length); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,36 @@ | |||
using System.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
using TrustedUninstaller.Shared; | |||
// General Information about an assembly is controlled through the following | |||
// set of attributes. Change these attribute values to modify the information | |||
// associated with an assembly. | |||
[assembly: AssemblyTitle("TrustedUninstaller.CLI")] | |||
[assembly: AssemblyDescription("")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("Ameliorated LLC")] | |||
[assembly: AssemblyProduct("TrustedUninstaller.CLI")] | |||
[assembly: AssemblyCopyright("MIT License")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
// Setting ComVisible to false makes the types in this assembly not visible | |||
// to COM components. If you need to access a type in this assembly from | |||
// COM, set the ComVisible attribute to true on that type. | |||
[assembly: ComVisible(false)] | |||
// The following GUID is for the ID of the typelib if this project is exposed to COM | |||
[assembly: Guid("476d3799-2cfc-4670-94e5-9af51b234b07")] | |||
// Version information for an assembly consists of the following four values: | |||
// | |||
// Major Version | |||
// Minor Version | |||
// Build Number | |||
// Revision | |||
// | |||
// You can specify all the values or you can default the Build and Revision Numbers | |||
// by using the '*' as shown below: | |||
// [assembly: AssemblyVersion("1.0.*")] | |||
[assembly: AssemblyVersion(Globals.CurrentVersion)] | |||
[assembly: AssemblyFileVersion(Globals.CurrentVersion)] |
@ -0,0 +1,502 @@ | |||
Process Hacker | |||
2.31 | |||
* NEW/IMPROVED: | |||
* Updated ExtendedServices plugin: | |||
* Fixed some bugs relating to Windows 8 | |||
* Updated OnlineChecks plugin: | |||
* Added upload progress | |||
* Updated UserNotes plugin: | |||
* Fixed bug where process priorities were not actually saved | |||
* FIXED: | |||
* Fixed module list not updating properly | |||
* DLL enumeration crash | |||
2.30 | |||
* NEW/IMPROVED: | |||
* Added "Icon click toggles visibility" option | |||
* Re-enabled powerful process termination on 32-bit Windows 8 | |||
* Updated UserNotes plugin: | |||
* Added ability to save process priority | |||
* Added "Only for processes with the same command line" option for process comments | |||
* FIXED: | |||
* Fixed crash on CPUs without SSE2 | |||
2.29 | |||
* NEW/IMPROVED: | |||
* Added App ID column for processes | |||
* Added new ASLR information for Windows 8 | |||
* Added Restart to Boot Options and Hybrid Shutdown menu items for | |||
Windows 8 | |||
* Added ability to specify processes by their names and inject and | |||
unload DLLs in command line | |||
* Removed 512 character limit when copying text | |||
* Moved Terminator to Miscellaneous menu | |||
* Updated default dbghelp.dll path for Windows SDK v8 | |||
* Updated ExtendedServices plugin: | |||
* Added new triggers for Windows 8 | |||
* Fixed bug when restarting services | |||
* Updated ExtendedTools plugin: | |||
* Improved support for multiple GPUs (again) | |||
* GPU column now respects "Include CPU usage of children" option | |||
* Updated ToolStatus plugin: | |||
* Fixed search box fonts | |||
* Fixed controls not being properly hidden/removed from the window when disabled | |||
* Updated WindowExplorer plugin: | |||
* Fixed window list not displaying Modern UI windows | |||
* FIXED: | |||
* Fixed Load Count column sorting bug | |||
* Fixed signature verification on Windows 8 | |||
* Fixed task scheduler information on Windows 8 | |||
* Fixed drag bug in tree list | |||
* Fixed KProcessHacker bug affecting TmTx objects | |||
* Fixed Run As feature on Windows 8 | |||
* Fixed bug where -settings parameter is not propagated | |||
* Fixed tab key behavior on main window | |||
* Fixed recognition of Modern UI windows | |||
2.28 | |||
* NEW/IMPROVED: | |||
* peview now resolves .lnk targets | |||
* Fixed Ctrl+A for processes, services and network connections and | |||
added Ctrl+A for other windows | |||
* Changed confirmation prompts to select the destructive action by | |||
default | |||
* Updated DotNetTools plugin: | |||
* Fixed inaccurate stack traces for certain .NET programs | |||
* Updated ExtendedTools plugin: | |||
* Fixed network graph scaling | |||
* Updated ToolStatus plugin: | |||
* Added search box | |||
* Updated Updater plugin | |||
* FIXED: | |||
* Fixed Verification Status column sorting bug in module list | |||
* Fixed rare System Information crash | |||
* Fixed bug in opening process handles | |||
* Fixed freezing when viewing stack traces of certain system threads | |||
2.27 | |||
* NEW/IMPROVED: | |||
* Updated OnlineChecks plugin: | |||
* 2012-01-16: Updated VirusTotal uploader and added hash checking | |||
* FIXED: | |||
* Fixed Description column sorting bug | |||
* Fixed notification icon bug | |||
2.26 | |||
* NEW/IMPROVED: | |||
* Added option to show Commit Charge in system information | |||
summary view | |||
* Added -priority and -selectpid command line options | |||
* Updated ExtendedTools plugin: | |||
* Improved support for multiple GPUs | |||
* FIXED: | |||
* Fixed 100% CPU when starting on some machines | |||
2.25 | |||
* NEW/IMPROVED: | |||
* Improved CPU frequency calculation | |||
* Updated ExtendedTools plugin: | |||
* Added GPU node selection | |||
* Fixed incorrect GPU usage calculation | |||
* FIXED: | |||
* Graph tooltip position with large cursors | |||
* Fixed .NET process detection | |||
* Fixed incorrect values in Bits column | |||
2.24 | |||
* NOTE: | |||
* This release has significant internal code changes. Please | |||
make sure all plugins are up-to-date. | |||
* NEW/IMPROVED: | |||
* Completely new system information window | |||
* Added option to scroll to new processes | |||
* Added option to hide driver services | |||
* Added menu item to copy individual cells | |||
* Improved module scanning | |||
* Added Start Task Manager menu item | |||
* Added Image base to peview | |||
* Updated ExtendedTools plugin: | |||
* Added support for new system information window | |||
* Added Disk, Network and GPU tray icons | |||
* Added support for custom fonts in the Disk tab | |||
* Updated Updater plugin: | |||
* Added download speed | |||
* Added remaining time | |||
* FIXED: | |||
* Fixed retrieval of version information for certain files | |||
* Fixed driver file names on Windows XP | |||
* Fixed Run As Administrator when used with complex commands | |||
2.23 | |||
* NEW/IMPROVED: | |||
* Added display of token capabilities, user/device claims | |||
and security attributes | |||
* Added ability to change token integrity levels | |||
* Added Description column to service list | |||
* Added option to reset all settings | |||
* Made grid color darker | |||
* Enabled multi-selection in the hidden processes window | |||
* Added UserNotes plugin | |||
* Updated ExtendedNotifications plugin: | |||
* Added Growl support | |||
* Updated ExtendedTools plugin: | |||
* Added GPU monitoring | |||
* Added rate columns for disk and network I/O | |||
* FIXED: | |||
* Fixed copying lists when plugin columns are enabled | |||
* Freezing when viewing the tooltip for a process with a | |||
very long command line | |||
* Disabled Hidden Processes feature on 64-bit systems | |||
2.22 | |||
* NEW/IMPROVED: | |||
* Added highlighting for metro style apps | |||
* Added Package Name column | |||
* Added package name to process tooltip | |||
* Improved .NET process detection | |||
* Updated OS Context column for Windows 8 | |||
* Updated ExtendedTools plugin: | |||
* Updated disk monitoring for Windows 8 | |||
* Updated memory list information for Windows 8 | |||
* Updated WindowExplorer plugin: | |||
* Fixed hook support for low integrity processes | |||
* FIXED: | |||
* Fixed memory leaks | |||
* Fixed bug preventing Interrupts/DPCs from being shown | |||
as the max. CPU process on 64-bit systems | |||
* Fixed DEP Status column on 64-bit systems | |||
2.21 | |||
* NEW/IMPROVED: | |||
* Added Private Bytes Delta, ASLR and Subsystem columns | |||
* Added ASLR and Time Stamp columns to modules list | |||
* Added check for debugger in Terminator | |||
* FIXED: | |||
* Fixed Show CPU Below 0.01 not respecting locale | |||
* Fixed copying from network list | |||
2.20 | |||
* NEW/IMPROVED: | |||
* Added support for managed thread stacks on x64 | |||
* Added column selection for handle list | |||
* Added CPU column to threads list | |||
* Improved module detection | |||
* Added Ideal Processor to Threads tab | |||
* Added pool usage and minimum/maximum working set columns | |||
* Implemented Properties button for Thread handles | |||
* Set descending sort as the default for most numeric columns | |||
* Extended header context menu | |||
* Removed tooltip text truncation | |||
* Improved cycle-based CPU usage calculation | |||
* Set default KProcessHacker security level to only allow | |||
connections when Process Hacker is running as administrator. | |||
See README.txt for instructions on how to restore the old | |||
behavior. | |||
* Added Updater plugin | |||
* Updated DotNetTools plugin: | |||
* Added managed symbol resolution for thread stacks | |||
* Updated ExtendedTools plugin: | |||
* Added Disk tab | |||
* Added Hard Faults, Hard Faults Delta and Peak Threads | |||
columns to process tree list | |||
* Added Firewall Status column | |||
* FIXED: | |||
* Fixed file name resolution bug | |||
* Save settings on shutdown/logoff | |||
* Fixed state highlighting bug | |||
* Fixed command line propagation for -elevate | |||
* Fixed tree list mouse wheel handling | |||
* Fixed saving network list | |||
2.19 | |||
* NEW/IMPROVED: | |||
* Added cycle-based CPU usage for Windows 7 | |||
* Added Show CPU Below 0.01 | |||
* Added OS Context column | |||
* Rewrote graph drawing code for improved performance | |||
* Optimized retrieval of cycle time and private working set | |||
information for Windows 7 | |||
* Added Open File Location to process context menu and | |||
reorganized some items | |||
* Added checkboxes to Terminator | |||
* FIXED: | |||
* Crash when sorting by Time Stamp | |||
* GDI handle leak in drag selection | |||
2.18 | |||
* NEW/IMPROVED: | |||
* Completely rewritten tree list control: | |||
* Process Name column is now fixed to the left | |||
* Tooltips for column headers | |||
* Improved performance | |||
* Bug fixes | |||
* Added more process tree list columns | |||
* Added Time stamp column to network list | |||
* Date/time display is now swapped (so time is shown before | |||
date) | |||
* Added W3 terminator test | |||
* Added DotNetTools plugin | |||
* Updated ExtendedServices plugin: | |||
* Disabled editing of required privileges for drivers | |||
* Updated ExtendedTools plugin: | |||
* Added ETW columns for processes and network connections | |||
* Updated OnlineChecks plugin: | |||
* Added Comodo Instant Malware Analysis | |||
* Updated WindowExplorer plugin: | |||
* Fixed hook bugs | |||
* FIXED: | |||
* Fixed Run As This User | |||
* Verification Status sorting | |||
2.17 | |||
* NEW/IMPROVED: | |||
* Added support for setting page priority | |||
* Added elevation support for setting priority | |||
* Added support for automatically using a settings file in | |||
the program directory (e.g. ProcessHacker.exe.settings.xml) | |||
* Improved Run As mechanism | |||
* Updated ExtendedServices plugin: | |||
* Added support for editing triggers | |||
* Added support for editing preshutdown time-out | |||
* Added support for editing required privileges | |||
* Added elevation support for restarting services | |||
* Updated WindowExplorer plugin: | |||
* Added more window properties | |||
* FIXED: | |||
* Handle leak | |||
2.16 | |||
* NEW/IMPROVED: | |||
* Updated WindowExplorer plugin | |||
* PE viewer: Added version string to CLR tab | |||
* PE viewer: Added display of delay imports | |||
* PE viewer: Added Load Config tab | |||
* Improved wait analysis | |||
* Added arrows to the service list to indicate whether a | |||
service is running | |||
* FIXED: | |||
* Fixed the IPv6-related workaround causing crashes | |||
* Incorrect handling of window positions | |||
2.15 | |||
* NEW/IMPROVED: | |||
* Updated ExtendedServices plugin | |||
* Updated ToolStatus plugin | |||
* Added DEP Status column | |||
* Improved User Name column | |||
* FIXED: | |||
* Image file versions | |||
* Workaround for an IPv6-related bug in Windows XP | |||
* DPCs and Interrupts in System Information tooltips | |||
* File dialog crash on Windows XP | |||
* ExtendedTools plugin: WS Watch refresh bug | |||
2.14 | |||
* NEW/IMPROVED: | |||
* ExtendedServices plugin: Option to add a Services menu | |||
for processes | |||
* Command line support for setting process priority and | |||
I/O priority | |||
* Improved termination of explorer.exe | |||
* FIXED: | |||
* Icon should restore the main window if it is minimized | |||
* System Information window crashes | |||
* Hide Processes From Other Users and Hide Signed Processes | |||
settings are now saved | |||
* Font selection on Windows XP | |||
* ToolStatus plugin: Always on Top status being reset by | |||
Find Window | |||
* Service-related crashes | |||
* WindowExplorer plugin: sorting in tree list | |||
* Process minidump creation with old versions of dbghelp.dll | |||
2.13 | |||
* NEW/IMPROVED: | |||
* Added copy support to PE viewer | |||
* Added Connect Time, Disconnect Time and Last Input Time | |||
to session properties | |||
* Added more working set counters to the Statistics tab | |||
* FIXED: | |||
* Column sort arrows | |||
* CPU usage calculations | |||
2.12 | |||
* NEW/IMPROVED: | |||
* Updated KProcessHacker for Windows 7 SP1 | |||
* Added elevation support for more actions | |||
* Added ability to disable plugins | |||
* Updated ToolStatus plugin | |||
* Added Remote Control for sessions | |||
* More command line options | |||
* FIXED: | |||
* Memory leaks | |||
* Run As issues with different sessions | |||
2.11 | |||
* NEW/IMPROVED: | |||
* Added WS Watch and other features to ExtendedTools | |||
plugin | |||
* Added WindowExplorer plugin | |||
* Properties for hidden processes | |||
* Improved menus | |||
* Debug console can now be closed without affecting the | |||
entire program | |||
* FIXED: | |||
* Always on Top issues | |||
* Hang when setting DEP status of a terminating process | |||
* Encoding bug in NetworkTools plugin | |||
* LSA interfacing issues | |||
* Creating dumps of self | |||
2.10 | |||
* NEW/IMPROVED: | |||
* KProcessHacker is now signed, so it works on 64-bit | |||
systems. Thank you to the ReactOS Foundation. | |||
* Added Run As Limited User | |||
* Added CPU, private bytes and I/O history columns | |||
* Added font selection | |||
* Slightly improved highlighting configuration | |||
* FIXED: | |||
* High DPI support | |||
* Multi-monitor support in graph tooltips | |||
* DEP status retrieval | |||
* ExtendedTools plugin crash | |||
* Notification icon menu crash | |||
* Memory leaks | |||
* Other small bug fixes | |||
2.9 | |||
* NEW/IMPROVED: | |||
* Added column selection for modules list | |||
* Added wait analysis for 64-bit systems | |||
* Added signature verification for modules | |||
* Added ExtendedTools plugin (Vista and above only) | |||
with Disk and Network information | |||
* Updated ExtendedNotifications plugin: added ability | |||
to log events to a file | |||
* Updated ExtendedServices plugin: new tab on Vista | |||
and above | |||
* Updated ToolStatus plugin: resolves ghost windows | |||
to hung windows | |||
* Environment variables and current directory are | |||
now correctly shown for WOW64 processes | |||
* I/O priority names are now used instead of numbers | |||
* FIXED: | |||
* Network list bug | |||
* Memory leaks | |||
2.8 | |||
* NEW/IMPROVED: | |||
* Better service list (including column selection) | |||
* Added Peak Handles | |||
* Process tree sorting is now preserved | |||
* Save works for services and network connections | |||
* Pausing now works correctly with the Network tab | |||
* Added option to display inclusive CPU usages for | |||
collapsed processes | |||
* Added CLR tab to peview | |||
* Added ability to destroy heaps | |||
* Improved process tree list appearance | |||
* Certain command line parameters are now propagated | |||
* FIXED: | |||
* Icon handling bugs | |||
* Memory leaks | |||
* Extended tooltips for WOW64 processes | |||
2.7 | |||
* NEW/IMPROVED: | |||
* Vastly improved startup time and lower memory usage | |||
* Added Cycles and Cycles Delta columns | |||
* Added option to disable address resolution for | |||
network connections | |||
* Added Logon Time to session properties | |||
* Added time stamp display to peview | |||
* FIXED: | |||
* ToolStatus layout problems | |||
* .NET highlighting crashes | |||
* Run As on Windows XP | |||
2.6 | |||
* NEW/IMPROVED: | |||
* Sorting for most lists is now much faster | |||
* Hide Signed Processes option | |||
* Added plugin for uploading files to online virus | |||
scanners | |||
* Added Network tools plugin | |||
* Updated ExtendedServices plugin | |||
* PE viewer now verifies checksums | |||
* Performance improvements | |||
* FIXED: | |||
* Fixed service handle leak | |||
2.5 | |||
* NEW/IMPROVED: | |||
* Unmap section views in Memory tab | |||
* Plugin for extended service information (including | |||
recovery information, dependencies and dependents) | |||
* FIXED: | |||
* Critical bug for file dialogs on Windows XP | |||
* Esc couldn't close Service Properties on open | |||
* Small bug fixes | |||
2.4 | |||
* NEW/IMPROVED: | |||
* Better Run As behaviour | |||
* Show Processes From All Users option | |||
* Can now unmap section views | |||
* Control over thread affinity | |||
* Window Title and Window Status columns | |||
* Plugin for filtering notifications | |||
* Plugin for toolbar and status bar | |||
* Performance improvements | |||
* FIXED: | |||
* Memory leak | |||
* SbieSupport plugin on 64-bit | |||
* Crash when running under certain conditions | |||
* Memory case-insensitive filter | |||
* Process parent association bug | |||
* REMOVED: | |||
* Process database | |||
2.3 | |||
* NEW/IMPROVED: | |||
* Can add processes to jobs | |||
* Double-clicking in the system information graphs now opens | |||
information for the relevant process | |||
* Setting I/O priority doesn't need KProcessHacker anymore | |||
* Elevation for certain actions | |||
* FIXED: | |||
* HKCU key name resolution | |||
* Network connection host resolution | |||
* Information window resizing | |||
* Log clearing | |||
2.2 | |||
* NEW/IMPROVED: | |||
* Plugins support | |||
* Can now unload 32-bit modules on 64-bit systems | |||
* Tasks are shown in tooltips for taskeng.exe/taskhost.exe processes | |||
* Run As can now start processes elevated | |||
* Handle count by type | |||
* Process priorities in notification icon menu | |||
* CSV export | |||
* Relative start times | |||
* FIXED: | |||
* Run and Run As shortcuts | |||
* Command line handling | |||
* Process tree selection | |||
2.1 | |||
* NEW/IMPROVED: | |||
* Add Pause key shortcut to pause/resume updates | |||
* Added Ctrl+Tab and Ctrl+Shift+Tab shortcuts | |||
* Grid is a bit darker | |||
* Checks for digital signatures and packing is now | |||
off by default and optional | |||
* FIXED: | |||
* MD5 calculation code for files was wrong | |||
* Process record bugs | |||
2.0 | |||
* First release in the Process Hacker 2.x branch. |
@ -0,0 +1,132 @@ | |||
== Process Hacker == | |||
Process Hacker is licensed under the GNU GPL v3, with exceptions. A full | |||
copy of the license is provided in LICENSE.txt. | |||
Copyright (C) 2009-2012 wj32 and various authors | |||
This program is free software: you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation, either version 3 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
== Mini-XML == | |||
Process Hacker uses Mini-XML licensed under the following terms: | |||
The Mini-XML library and included programs are provided under the | |||
terms of the GNU Library General Public License (LGPL) with the | |||
following exceptions: | |||
1. Static linking of applications to the Mini-XML library | |||
does not constitute a derivative work and does not require | |||
the author to provide source code for the application, use | |||
the shared Mini-XML libraries, or link their applications | |||
against a user-supplied version of Mini-XML. | |||
If you link the application to a modified version of | |||
Mini-XML, then the changes to Mini-XML must be provided | |||
under the terms of the LGPL in sections 1, 2, and 4. | |||
2. You do not have to provide a copy of the Mini-XML license | |||
with programs that are linked to the Mini-XML library, nor | |||
do you have to identify the Mini-XML license in your | |||
program or documentation as required by section 6 of the | |||
LGPL. | |||
== PCRE == | |||
Process Hacker uses Perl-Compatible Regular Expressions licensed under the | |||
following terms: | |||
PCRE is a library of functions to support regular expressions whose syntax | |||
and semantics are as close as possible to those of the Perl 5 language. | |||
Release 8 of PCRE is distributed under the terms of the "BSD" licence, as | |||
specified below. | |||
Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are met: | |||
* Redistributions of source code must retain the above copyright notice, | |||
this list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above copyright | |||
notice, this list of conditions and the following disclaimer in the | |||
documentation and/or other materials provided with the distribution. | |||
* Neither the name of the University of Cambridge nor the name of Google | |||
Inc. nor the names of their contributors may be used to endorse or | |||
promote products derived from this software without specific prior | |||
written permission. | |||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||
POSSIBILITY OF SUCH DAMAGE. | |||
== MD5 == | |||
Process Hacker uses a MD5 implementation licensed under the following terms: | |||
MD5 hash implementation and interface functions | |||
Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License version 2 as | |||
published by the Free Software Foundation. | |||
== SHA == | |||
Process Hacker uses a SHA implementation licensed under the following terms: | |||
Copyright 2004 Filip Navara | |||
Based on public domain SHA code by Steve Reid <steve@edmweb.com> | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |||
== Natural order string comparison == | |||
Process Hacker uses "strnatcmp.c" licensed under the following terms: | |||
strnatcmp.c -- Perform 'natural order' comparisons of strings in C. | |||
Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net> | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
This code has been modified for Process Hacker. |
@ -0,0 +1,685 @@ | |||
Process Hacker is distributed under the GNU GPL version 3, with the | |||
following exception: | |||
Permission is granted to dynamically (but not statically) link this | |||
program with independent modules, regardless of the license terms of | |||
these independent modules, provided that this program is not modified | |||
in any way. An independent module is a module which is not derived | |||
from or based on this program. If you modify this program, this | |||
additional permission no longer applies unless authorized by the | |||
copyright holders. | |||
GNU GENERAL PUBLIC LICENSE | |||
Version 3, 29 June 2007 | |||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> | |||
Everyone is permitted to copy and distribute verbatim copies | |||
of this license document, but changing it is not allowed. | |||
Preamble | |||
The GNU General Public License is a free, copyleft license for | |||
software and other kinds of works. | |||
The licenses for most software and other practical works are designed | |||
to take away your freedom to share and change the works. By contrast, | |||
the GNU General Public License is intended to guarantee your freedom to | |||
share and change all versions of a program--to make sure it remains free | |||
software for all its users. We, the Free Software Foundation, use the | |||
GNU General Public License for most of our software; it applies also to | |||
any other work released this way by its authors. You can apply it to | |||
your programs, too. | |||
When we speak of free software, we are referring to freedom, not | |||
price. Our General Public Licenses are designed to make sure that you | |||
have the freedom to distribute copies of free software (and charge for | |||
them if you wish), that you receive source code or can get it if you | |||
want it, that you can change the software or use pieces of it in new | |||
free programs, and that you know you can do these things. | |||
To protect your rights, we need to prevent others from denying you | |||
these rights or asking you to surrender the rights. Therefore, you have | |||
certain responsibilities if you distribute copies of the software, or if | |||
you modify it: responsibilities to respect the freedom of others. | |||
For example, if you distribute copies of such a program, whether | |||
gratis or for a fee, you must pass on to the recipients the same | |||
freedoms that you received. You must make sure that they, too, receive | |||
or can get the source code. And you must show them these terms so they | |||
know their rights. | |||
Developers that use the GNU GPL protect your rights with two steps: | |||
(1) assert copyright on the software, and (2) offer you this License | |||
giving you legal permission to copy, distribute and/or modify it. | |||
For the developers' and authors' protection, the GPL clearly explains | |||
that there is no warranty for this free software. For both users' and | |||
authors' sake, the GPL requires that modified versions be marked as | |||
changed, so that their problems will not be attributed erroneously to | |||
authors of previous versions. | |||
Some devices are designed to deny users access to install or run | |||
modified versions of the software inside them, although the manufacturer | |||
can do so. This is fundamentally incompatible with the aim of | |||
protecting users' freedom to change the software. The systematic | |||
pattern of such abuse occurs in the area of products for individuals to | |||
use, which is precisely where it is most unacceptable. Therefore, we | |||
have designed this version of the GPL to prohibit the practice for those | |||
products. If such problems arise substantially in other domains, we | |||
stand ready to extend this provision to those domains in future versions | |||
of the GPL, as needed to protect the freedom of users. | |||
Finally, every program is threatened constantly by software patents. | |||
States should not allow patents to restrict development and use of | |||
software on general-purpose computers, but in those that do, we wish to | |||
avoid the special danger that patents applied to a free program could | |||
make it effectively proprietary. To prevent this, the GPL assures that | |||
patents cannot be used to render the program non-free. | |||
The precise terms and conditions for copying, distribution and | |||
modification follow. | |||
TERMS AND CONDITIONS | |||
0. Definitions. | |||
"This License" refers to version 3 of the GNU General Public License. | |||
"Copyright" also means copyright-like laws that apply to other kinds of | |||
works, such as semiconductor masks. | |||
"The Program" refers to any copyrightable work licensed under this | |||
License. Each licensee is addressed as "you". "Licensees" and | |||
"recipients" may be individuals or organizations. | |||
To "modify" a work means to copy from or adapt all or part of the work | |||
in a fashion requiring copyright permission, other than the making of an | |||
exact copy. The resulting work is called a "modified version" of the | |||
earlier work or a work "based on" the earlier work. | |||
A "covered work" means either the unmodified Program or a work based | |||
on the Program. | |||
To "propagate" a work means to do anything with it that, without | |||
permission, would make you directly or secondarily liable for | |||
infringement under applicable copyright law, except executing it on a | |||
computer or modifying a private copy. Propagation includes copying, | |||
distribution (with or without modification), making available to the | |||
public, and in some countries other activities as well. | |||
To "convey" a work means any kind of propagation that enables other | |||
parties to make or receive copies. Mere interaction with a user through | |||
a computer network, with no transfer of a copy, is not conveying. | |||
An interactive user interface displays "Appropriate Legal Notices" | |||
to the extent that it includes a convenient and prominently visible | |||
feature that (1) displays an appropriate copyright notice, and (2) | |||
tells the user that there is no warranty for the work (except to the | |||
extent that warranties are provided), that licensees may convey the | |||
work under this License, and how to view a copy of this License. If | |||
the interface presents a list of user commands or options, such as a | |||
menu, a prominent item in the list meets this criterion. | |||
1. Source Code. | |||
The "source code" for a work means the preferred form of the work | |||
for making modifications to it. "Object code" means any non-source | |||
form of a work. | |||
A "Standard Interface" means an interface that either is an official | |||
standard defined by a recognized standards body, or, in the case of | |||
interfaces specified for a particular programming language, one that | |||
is widely used among developers working in that language. | |||
The "System Libraries" of an executable work include anything, other | |||
than the work as a whole, that (a) is included in the normal form of | |||
packaging a Major Component, but which is not part of that Major | |||
Component, and (b) serves only to enable use of the work with that | |||
Major Component, or to implement a Standard Interface for which an | |||
implementation is available to the public in source code form. A | |||
"Major Component", in this context, means a major essential component | |||
(kernel, window system, and so on) of the specific operating system | |||
(if any) on which the executable work runs, or a compiler used to | |||
produce the work, or an object code interpreter used to run it. | |||
The "Corresponding Source" for a work in object code form means all | |||
the source code needed to generate, install, and (for an executable | |||
work) run the object code and to modify the work, including scripts to | |||
control those activities. However, it does not include the work's | |||
System Libraries, or general-purpose tools or generally available free | |||
programs which are used unmodified in performing those activities but | |||
which are not part of the work. For example, Corresponding Source | |||
includes interface definition files associated with source files for | |||
the work, and the source code for shared libraries and dynamically | |||
linked subprograms that the work is specifically designed to require, | |||
such as by intimate data communication or control flow between those | |||
subprograms and other parts of the work. | |||
The Corresponding Source need not include anything that users | |||
can regenerate automatically from other parts of the Corresponding | |||
Source. | |||
The Corresponding Source for a work in source code form is that | |||
same work. | |||
2. Basic Permissions. | |||
All rights granted under this License are granted for the term of | |||
copyright on the Program, and are irrevocable provided the stated | |||
conditions are met. This License explicitly affirms your unlimited | |||
permission to run the unmodified Program. The output from running a | |||
covered work is covered by this License only if the output, given its | |||
content, constitutes a covered work. This License acknowledges your | |||
rights of fair use or other equivalent, as provided by copyright law. | |||
You may make, run and propagate covered works that you do not | |||
convey, without conditions so long as your license otherwise remains | |||
in force. You may convey covered works to others for the sole purpose | |||
of having them make modifications exclusively for you, or provide you | |||
with facilities for running those works, provided that you comply with | |||
the terms of this License in conveying all material for which you do | |||
not control copyright. Those thus making or running the covered works | |||
for you must do so exclusively on your behalf, under your direction | |||
and control, on terms that prohibit them from making any copies of | |||
your copyrighted material outside their relationship with you. | |||
Conveying under any other circumstances is permitted solely under | |||
the conditions stated below. Sublicensing is not allowed; section 10 | |||
makes it unnecessary. | |||
3. Protecting Users' Legal Rights From Anti-Circumvention Law. | |||
No covered work shall be deemed part of an effective technological | |||
measure under any applicable law fulfilling obligations under article | |||
11 of the WIPO copyright treaty adopted on 20 December 1996, or | |||
similar laws prohibiting or restricting circumvention of such | |||
measures. | |||
When you convey a covered work, you waive any legal power to forbid | |||
circumvention of technological measures to the extent such circumvention | |||
is effected by exercising rights under this License with respect to | |||
the covered work, and you disclaim any intention to limit operation or | |||
modification of the work as a means of enforcing, against the work's | |||
users, your or third parties' legal rights to forbid circumvention of | |||
technological measures. | |||
4. Conveying Verbatim Copies. | |||
You may convey verbatim copies of the Program's source code as you | |||
receive it, in any medium, provided that you conspicuously and | |||
appropriately publish on each copy an appropriate copyright notice; | |||
keep intact all notices stating that this License and any | |||
non-permissive terms added in accord with section 7 apply to the code; | |||
keep intact all notices of the absence of any warranty; and give all | |||
recipients a copy of this License along with the Program. | |||
You may charge any price or no price for each copy that you convey, | |||
and you may offer support or warranty protection for a fee. | |||
5. Conveying Modified Source Versions. | |||
You may convey a work based on the Program, or the modifications to | |||
produce it from the Program, in the form of source code under the | |||
terms of section 4, provided that you also meet all of these conditions: | |||
a) The work must carry prominent notices stating that you modified | |||
it, and giving a relevant date. | |||
b) The work must carry prominent notices stating that it is | |||
released under this License and any conditions added under section | |||
7. This requirement modifies the requirement in section 4 to | |||
"keep intact all notices". | |||
c) You must license the entire work, as a whole, under this | |||
License to anyone who comes into possession of a copy. This | |||
License will therefore apply, along with any applicable section 7 | |||
additional terms, to the whole of the work, and all its parts, | |||
regardless of how they are packaged. This License gives no | |||
permission to license the work in any other way, but it does not | |||
invalidate such permission if you have separately received it. | |||
d) If the work has interactive user interfaces, each must display | |||
Appropriate Legal Notices; however, if the Program has interactive | |||
interfaces that do not display Appropriate Legal Notices, your | |||
work need not make them do so. | |||
A compilation of a covered work with other separate and independent | |||
works, which are not by their nature extensions of the covered work, | |||
and which are not combined with it such as to form a larger program, | |||
in or on a volume of a storage or distribution medium, is called an | |||
"aggregate" if the compilation and its resulting copyright are not | |||
used to limit the access or legal rights of the compilation's users | |||
beyond what the individual works permit. Inclusion of a covered work | |||
in an aggregate does not cause this License to apply to the other | |||
parts of the aggregate. | |||
6. Conveying Non-Source Forms. | |||
You may convey a covered work in object code form under the terms | |||
of sections 4 and 5, provided that you also convey the | |||
machine-readable Corresponding Source under the terms of this License, | |||
in one of these ways: | |||
a) Convey the object code in, or embodied in, a physical product | |||
(including a physical distribution medium), accompanied by the | |||
Corresponding Source fixed on a durable physical medium | |||
customarily used for software interchange. | |||
b) Convey the object code in, or embodied in, a physical product | |||
(including a physical distribution medium), accompanied by a | |||
written offer, valid for at least three years and valid for as | |||
long as you offer spare parts or customer support for that product | |||
model, to give anyone who possesses the object code either (1) a | |||
copy of the Corresponding Source for all the software in the | |||
product that is covered by this License, on a durable physical | |||
medium customarily used for software interchange, for a price no | |||
more than your reasonable cost of physically performing this | |||
conveying of source, or (2) access to copy the | |||
Corresponding Source from a network server at no charge. | |||
c) Convey individual copies of the object code with a copy of the | |||
written offer to provide the Corresponding Source. This | |||
alternative is allowed only occasionally and noncommercially, and | |||
only if you received the object code with such an offer, in accord | |||
with subsection 6b. | |||
d) Convey the object code by offering access from a designated | |||
place (gratis or for a charge), and offer equivalent access to the | |||
Corresponding Source in the same way through the same place at no | |||
further charge. You need not require recipients to copy the | |||
Corresponding Source along with the object code. If the place to | |||
copy the object code is a network server, the Corresponding Source | |||
may be on a different server (operated by you or a third party) | |||
that supports equivalent copying facilities, provided you maintain | |||
clear directions next to the object code saying where to find the | |||
Corresponding Source. Regardless of what server hosts the | |||
Corresponding Source, you remain obligated to ensure that it is | |||
available for as long as needed to satisfy these requirements. | |||
e) Convey the object code using peer-to-peer transmission, provided | |||
you inform other peers where the object code and Corresponding | |||
Source of the work are being offered to the general public at no | |||
charge under subsection 6d. | |||
A separable portion of the object code, whose source code is excluded | |||
from the Corresponding Source as a System Library, need not be | |||
included in conveying the object code work. | |||
A "User Product" is either (1) a "consumer product", which means any | |||
tangible personal property which is normally used for personal, family, | |||
or household purposes, or (2) anything designed or sold for incorporation | |||
into a dwelling. In determining whether a product is a consumer product, | |||
doubtful cases shall be resolved in favor of coverage. For a particular | |||
product received by a particular user, "normally used" refers to a | |||
typical or common use of that class of product, regardless of the status | |||
of the particular user or of the way in which the particular user | |||
actually uses, or expects or is expected to use, the product. A product | |||
is a consumer product regardless of whether the product has substantial | |||
commercial, industrial or non-consumer uses, unless such uses represent | |||
the only significant mode of use of the product. | |||
"Installation Information" for a User Product means any methods, | |||
procedures, authorization keys, or other information required to install | |||
and execute modified versions of a covered work in that User Product from | |||
a modified version of its Corresponding Source. The information must | |||
suffice to ensure that the continued functioning of the modified object | |||
code is in no case prevented or interfered with solely because | |||
modification has been made. | |||
If you convey an object code work under this section in, or with, or | |||
specifically for use in, a User Product, and the conveying occurs as | |||
part of a transaction in which the right of possession and use of the | |||
User Product is transferred to the recipient in perpetuity or for a | |||
fixed term (regardless of how the transaction is characterized), the | |||
Corresponding Source conveyed under this section must be accompanied | |||
by the Installation Information. But this requirement does not apply | |||
if neither you nor any third party retains the ability to install | |||
modified object code on the User Product (for example, the work has | |||
been installed in ROM). | |||
The requirement to provide Installation Information does not include a | |||
requirement to continue to provide support service, warranty, or updates | |||
for a work that has been modified or installed by the recipient, or for | |||
the User Product in which it has been modified or installed. Access to a | |||
network may be denied when the modification itself materially and | |||
adversely affects the operation of the network or violates the rules and | |||
protocols for communication across the network. | |||
Corresponding Source conveyed, and Installation Information provided, | |||
in accord with this section must be in a format that is publicly | |||
documented (and with an implementation available to the public in | |||
source code form), and must require no special password or key for | |||
unpacking, reading or copying. | |||
7. Additional Terms. | |||
"Additional permissions" are terms that supplement the terms of this | |||
License by making exceptions from one or more of its conditions. | |||
Additional permissions that are applicable to the entire Program shall | |||
be treated as though they were included in this License, to the extent | |||
that they are valid under applicable law. If additional permissions | |||
apply only to part of the Program, that part may be used separately | |||
under those permissions, but the entire Program remains governed by | |||
this License without regard to the additional permissions. | |||
When you convey a copy of a covered work, you may at your option | |||
remove any additional permissions from that copy, or from any part of | |||
it. (Additional permissions may be written to require their own | |||
removal in certain cases when you modify the work.) You may place | |||
additional permissions on material, added by you to a covered work, | |||
for which you have or can give appropriate copyright permission. | |||
Notwithstanding any other provision of this License, for material you | |||
add to a covered work, you may (if authorized by the copyright holders of | |||
that material) supplement the terms of this License with terms: | |||
a) Disclaiming warranty or limiting liability differently from the | |||
terms of sections 15 and 16 of this License; or | |||
b) Requiring preservation of specified reasonable legal notices or | |||
author attributions in that material or in the Appropriate Legal | |||
Notices displayed by works containing it; or | |||
c) Prohibiting misrepresentation of the origin of that material, or | |||
requiring that modified versions of such material be marked in | |||
reasonable ways as different from the original version; or | |||
d) Limiting the use for publicity purposes of names of licensors or | |||
authors of the material; or | |||
e) Declining to grant rights under trademark law for use of some | |||
trade names, trademarks, or service marks; or | |||
f) Requiring indemnification of licensors and authors of that | |||
material by anyone who conveys the material (or modified versions of | |||
it) with contractual assumptions of liability to the recipient, for | |||
any liability that these contractual assumptions directly impose on | |||
those licensors and authors. | |||
All other non-permissive additional terms are considered "further | |||
restrictions" within the meaning of section 10. If the Program as you | |||
received it, or any part of it, contains a notice stating that it is | |||
governed by this License along with a term that is a further | |||
restriction, you may remove that term. If a license document contains | |||
a further restriction but permits relicensing or conveying under this | |||
License, you may add to a covered work material governed by the terms | |||
of that license document, provided that the further restriction does | |||
not survive such relicensing or conveying. | |||
If you add terms to a covered work in accord with this section, you | |||
must place, in the relevant source files, a statement of the | |||
additional terms that apply to those files, or a notice indicating | |||
where to find the applicable terms. | |||
Additional terms, permissive or non-permissive, may be stated in the | |||
form of a separately written license, or stated as exceptions; | |||
the above requirements apply either way. | |||
8. Termination. | |||
You may not propagate or modify a covered work except as expressly | |||
provided under this License. Any attempt otherwise to propagate or | |||
modify it is void, and will automatically terminate your rights under | |||
this License (including any patent licenses granted under the third | |||
paragraph of section 11). | |||
However, if you cease all violation of this License, then your | |||
license from a particular copyright holder is reinstated (a) | |||
provisionally, unless and until the copyright holder explicitly and | |||
finally terminates your license, and (b) permanently, if the copyright | |||
holder fails to notify you of the violation by some reasonable means | |||
prior to 60 days after the cessation. | |||
Moreover, your license from a particular copyright holder is | |||
reinstated permanently if the copyright holder notifies you of the | |||
violation by some reasonable means, this is the first time you have | |||
received notice of violation of this License (for any work) from that | |||
copyright holder, and you cure the violation prior to 30 days after | |||
your receipt of the notice. | |||
Termination of your rights under this section does not terminate the | |||
licenses of parties who have received copies or rights from you under | |||
this License. If your rights have been terminated and not permanently | |||
reinstated, you do not qualify to receive new licenses for the same | |||
material under section 10. | |||
9. Acceptance Not Required for Having Copies. | |||
You are not required to accept this License in order to receive or | |||
run a copy of the Program. Ancillary propagation of a covered work | |||
occurring solely as a consequence of using peer-to-peer transmission | |||
to receive a copy likewise does not require acceptance. However, | |||
nothing other than this License grants you permission to propagate or | |||
modify any covered work. These actions infringe copyright if you do | |||
not accept this License. Therefore, by modifying or propagating a | |||
covered work, you indicate your acceptance of this License to do so. | |||
10. Automatic Licensing of Downstream Recipients. | |||
Each time you convey a covered work, the recipient automatically | |||
receives a license from the original licensors, to run, modify and | |||
propagate that work, subject to this License. You are not responsible | |||
for enforcing compliance by third parties with this License. | |||
An "entity transaction" is a transaction transferring control of an | |||
organization, or substantially all assets of one, or subdividing an | |||
organization, or merging organizations. If propagation of a covered | |||
work results from an entity transaction, each party to that | |||
transaction who receives a copy of the work also receives whatever | |||
licenses to the work the party's predecessor in interest had or could | |||
give under the previous paragraph, plus a right to possession of the | |||
Corresponding Source of the work from the predecessor in interest, if | |||
the predecessor has it or can get it with reasonable efforts. | |||
You may not impose any further restrictions on the exercise of the | |||
rights granted or affirmed under this License. For example, you may | |||
not impose a license fee, royalty, or other charge for exercise of | |||
rights granted under this License, and you may not initiate litigation | |||
(including a cross-claim or counterclaim in a lawsuit) alleging that | |||
any patent claim is infringed by making, using, selling, offering for | |||
sale, or importing the Program or any portion of it. | |||
11. Patents. | |||
A "contributor" is a copyright holder who authorizes use under this | |||
License of the Program or a work on which the Program is based. The | |||
work thus licensed is called the contributor's "contributor version". | |||
A contributor's "essential patent claims" are all patent claims | |||
owned or controlled by the contributor, whether already acquired or | |||
hereafter acquired, that would be infringed by some manner, permitted | |||
by this License, of making, using, or selling its contributor version, | |||
but do not include claims that would be infringed only as a | |||
consequence of further modification of the contributor version. For | |||
purposes of this definition, "control" includes the right to grant | |||
patent sublicenses in a manner consistent with the requirements of | |||
this License. | |||
Each contributor grants you a non-exclusive, worldwide, royalty-free | |||
patent license under the contributor's essential patent claims, to | |||
make, use, sell, offer for sale, import and otherwise run, modify and | |||
propagate the contents of its contributor version. | |||
In the following three paragraphs, a "patent license" is any express | |||
agreement or commitment, however denominated, not to enforce a patent | |||
(such as an express permission to practice a patent or covenant not to | |||
sue for patent infringement). To "grant" such a patent license to a | |||
party means to make such an agreement or commitment not to enforce a | |||
patent against the party. | |||
If you convey a covered work, knowingly relying on a patent license, | |||
and the Corresponding Source of the work is not available for anyone | |||
to copy, free of charge and under the terms of this License, through a | |||
publicly available network server or other readily accessible means, | |||
then you must either (1) cause the Corresponding Source to be so | |||
available, or (2) arrange to deprive yourself of the benefit of the | |||
patent license for this particular work, or (3) arrange, in a manner | |||
consistent with the requirements of this License, to extend the patent | |||
license to downstream recipients. "Knowingly relying" means you have | |||
actual knowledge that, but for the patent license, your conveying the | |||
covered work in a country, or your recipient's use of the covered work | |||
in a country, would infringe one or more identifiable patents in that | |||
country that you have reason to believe are valid. | |||
If, pursuant to or in connection with a single transaction or | |||
arrangement, you convey, or propagate by procuring conveyance of, a | |||
covered work, and grant a patent license to some of the parties | |||
receiving the covered work authorizing them to use, propagate, modify | |||
or convey a specific copy of the covered work, then the patent license | |||
you grant is automatically extended to all recipients of the covered | |||
work and works based on it. | |||
A patent license is "discriminatory" if it does not include within | |||
the scope of its coverage, prohibits the exercise of, or is | |||
conditioned on the non-exercise of one or more of the rights that are | |||
specifically granted under this License. You may not convey a covered | |||
work if you are a party to an arrangement with a third party that is | |||
in the business of distributing software, under which you make payment | |||
to the third party based on the extent of your activity of conveying | |||
the work, and under which the third party grants, to any of the | |||
parties who would receive the covered work from you, a discriminatory | |||
patent license (a) in connection with copies of the covered work | |||
conveyed by you (or copies made from those copies), or (b) primarily | |||
for and in connection with specific products or compilations that | |||
contain the covered work, unless you entered into that arrangement, | |||
or that patent license was granted, prior to 28 March 2007. | |||
Nothing in this License shall be construed as excluding or limiting | |||
any implied license or other defenses to infringement that may | |||
otherwise be available to you under applicable patent law. | |||
12. No Surrender of Others' Freedom. | |||
If conditions are imposed on you (whether by court order, agreement or | |||
otherwise) that contradict the conditions of this License, they do not | |||
excuse you from the conditions of this License. If you cannot convey a | |||
covered work so as to satisfy simultaneously your obligations under this | |||
License and any other pertinent obligations, then as a consequence you may | |||
not convey it at all. For example, if you agree to terms that obligate you | |||
to collect a royalty for further conveying from those to whom you convey | |||
the Program, the only way you could satisfy both those terms and this | |||
License would be to refrain entirely from conveying the Program. | |||
13. Use with the GNU Affero General Public License. | |||
Notwithstanding any other provision of this License, you have | |||
permission to link or combine any covered work with a work licensed | |||
under version 3 of the GNU Affero General Public License into a single | |||
combined work, and to convey the resulting work. The terms of this | |||
License will continue to apply to the part which is the covered work, | |||
but the special requirements of the GNU Affero General Public License, | |||
section 13, concerning interaction through a network will apply to the | |||
combination as such. | |||
14. Revised Versions of this License. | |||
The Free Software Foundation may publish revised and/or new versions of | |||
the GNU General Public License from time to time. Such new versions will | |||
be similar in spirit to the present version, but may differ in detail to | |||
address new problems or concerns. | |||
Each version is given a distinguishing version number. If the | |||
Program specifies that a certain numbered version of the GNU General | |||
Public License "or any later version" applies to it, you have the | |||
option of following the terms and conditions either of that numbered | |||
version or of any later version published by the Free Software | |||
Foundation. If the Program does not specify a version number of the | |||
GNU General Public License, you may choose any version ever published | |||
by the Free Software Foundation. | |||
If the Program specifies that a proxy can decide which future | |||
versions of the GNU General Public License can be used, that proxy's | |||
public statement of acceptance of a version permanently authorizes you | |||
to choose that version for the Program. | |||
Later license versions may give you additional or different | |||
permissions. However, no additional obligations are imposed on any | |||
author or copyright holder as a result of your choosing to follow a | |||
later version. | |||
15. Disclaimer of Warranty. | |||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY | |||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT | |||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY | |||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, | |||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM | |||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF | |||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | |||
16. Limitation of Liability. | |||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | |||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS | |||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY | |||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE | |||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF | |||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD | |||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), | |||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF | |||
SUCH DAMAGES. | |||
17. Interpretation of Sections 15 and 16. | |||
If the disclaimer of warranty and limitation of liability provided | |||
above cannot be given local legal effect according to their terms, | |||
reviewing courts shall apply local law that most closely approximates | |||
an absolute waiver of all civil liability in connection with the | |||
Program, unless a warranty or assumption of liability accompanies a | |||
copy of the Program in return for a fee. | |||
END OF TERMS AND CONDITIONS | |||
How to Apply These Terms to Your New Programs | |||
If you develop a new program, and you want it to be of the greatest | |||
possible use to the public, the best way to achieve this is to make it | |||
free software which everyone can redistribute and change under these terms. | |||
To do so, attach the following notices to the program. It is safest | |||
to attach them to the start of each source file to most effectively | |||
state the exclusion of warranty; and each file should have at least | |||
the "copyright" line and a pointer to where the full notice is found. | |||
<one line to give the program's name and a brief idea of what it does.> | |||
Copyright (C) <year> <name of author> | |||
This program is free software: you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation, either version 3 of the License, or | |||
(at your option) any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
Also add information on how to contact you by electronic and paper mail. | |||
If the program does terminal interaction, make it output a short | |||
notice like this when it starts in an interactive mode: | |||
<program> Copyright (C) <year> <name of author> | |||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | |||
This is free software, and you are welcome to redistribute it | |||
under certain conditions; type `show c' for details. | |||
The hypothetical commands `show w' and `show c' should show the appropriate | |||
parts of the General Public License. Of course, your program's commands | |||
might be different; for a GUI interface, you would use an "about box". | |||
You should also get your employer (if you work as a programmer) or school, | |||
if any, to sign a "copyright disclaimer" for the program, if necessary. | |||
For more information on this, and how to apply and follow the GNU GPL, see | |||
<http://www.gnu.org/licenses/>. | |||
The GNU General Public License does not permit incorporating your program | |||
into proprietary programs. If your program is a subroutine library, you | |||
may consider it more useful to permit linking proprietary applications with | |||
the library. If this is what you want to do, use the GNU Lesser General | |||
Public License instead of this License. But first, please read | |||
<http://www.gnu.org/philosophy/why-not-lgpl.html>. |
@ -0,0 +1,73 @@ | |||
Process Hacker is a powerful free and open source process viewer. | |||
== Getting started == | |||
Simply run ProcessHacker.exe to start Process Hacker. There are two | |||
versions, 32-bit (x86) and 64-bit (x64). If you are not sure which | |||
version to use, open Control Panel > System and check the "System | |||
type". You cannot run the 32-bit version of Process Hacker on a | |||
64-bit system and expect it to work correctly, unlike other programs. | |||
== System requirements == | |||
Windows XP SP2 or higher, 32-bit or 64-bit. | |||
== Settings == | |||
If you are running Process Hacker from a USB drive, you may want to | |||
save Process Hacker's settings there as well. To do this, create a | |||
blank file named "ProcessHacker.exe.settings.xml" in the same | |||
directory as ProcessHacker.exe. You can do this using Windows Explorer: | |||
1. Make sure "Hide extensions for known file types" is unticked in | |||
Tools > Folder options > View. | |||
2. Right-click in the folder and choose New > Text Document. | |||
3. Rename the file to ProcessHacker.exe.settings.xml (delete the ".txt" | |||
extension). | |||
== Plugins == | |||
Plugins can be configured from Hacker > Plugins. | |||
If you experience any crashes involving plugins, make sure they | |||
are up to date. | |||
The ExtendedTools plugin is only available for Windows Vista and | |||
above. Disk and Network information provided by this plugin is | |||
only available when running Process Hacker with administrative | |||
rights. | |||
== KProcessHacker == | |||
NOTE: The driver has been very generously signed by the | |||
ReactOS Foundation (http://www.reactos.org). | |||
Process Hacker uses a kernel-mode driver, KProcessHacker, to | |||
assist with certain functionality. This includes: | |||
* Bypassing security software and rootkits in limited ways | |||
* More powerful process and thread termination (*) | |||
* Setting DEP status of processes | |||
* Capturing kernel-mode stack traces | |||
* More efficiently enumerating process handles | |||
* Retrieving names for file handles | |||
* Retrieving names for EtwRegistration objects | |||
* Setting handle attributes | |||
The feature(s) marked with an asterisk (*) are NOT available on 64-bit | |||
versions of Windows. | |||
Certain features such as modifying process protection are disabled | |||
in the released driver binary due to legal reasons. You can enable | |||
them by building KProcessHacker with the "dirty" configuration. | |||
Note that by default, KProcessHacker only allows connections from | |||
processes with SeDebugPrivilege. To allow Process Hacker to show details | |||
for all processes when it is not running as administrator: | |||
1. In Registry Editor, navigate to: | |||
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\KProcessHacker2 | |||
2. Under this key, create a key named Parameters if it does not exist. | |||
3. Create a DWORD value named SecurityLevel and set it to 0. | |||
4. Restart the KProcessHacker2 service (sc stop KProcessHacker2, | |||
sc start KProcessHacker2). |
@ -0,0 +1,18 @@ | |||
<?xml version="1.0" encoding="utf-8"?><configuration> | |||
<runtime> | |||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> | |||
<dependentAssembly> | |||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> | |||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" /> | |||
</dependentAssembly> | |||
<dependentAssembly> | |||
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> | |||
<bindingRedirect oldVersion="0.0.0.0-4.1.3.0" newVersion="4.1.3.0" /> | |||
</dependentAssembly> | |||
<dependentAssembly> | |||
<assemblyIdentity name="SQLitePCLRaw.core" publicKeyToken="1488e028ca7ab535" culture="neutral" /> | |||
<bindingRedirect oldVersion="0.0.0.0-2.1.2.1721" newVersion="2.1.2.1721" /> | |||
</dependentAssembly> | |||
</assemblyBinding> | |||
</runtime> | |||
</configuration> |
@ -0,0 +1,188 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | |||
<PropertyGroup> | |||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
<ProjectGuid>{476D3799-2CFC-4670-94E5-9AF51B234B07}</ProjectGuid> | |||
<OutputType>Exe</OutputType> | |||
<RootNamespace>TrustedUninstaller.CLI</RootNamespace> | |||
<AssemblyName>TrustedUninstaller.CLI</AssemblyName> | |||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> | |||
<FileAlignment>512</FileAlignment> | |||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> | |||
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> | |||
<Deterministic>true</Deterministic> | |||
<IsWebBootstrapper>false</IsWebBootstrapper> | |||
<TargetFrameworkProfile /> | |||
<NuGetPackageImportStamp> | |||
</NuGetPackageImportStamp> | |||
<PublishUrl>C:\Users\yohas\Documents\AME\</PublishUrl> | |||
<Install>true</Install> | |||
<InstallFrom>Disk</InstallFrom> | |||
<UpdateEnabled>false</UpdateEnabled> | |||
<UpdateMode>Foreground</UpdateMode> | |||
<UpdateInterval>7</UpdateInterval> | |||
<UpdateIntervalUnits>Days</UpdateIntervalUnits> | |||
<UpdatePeriodically>false</UpdatePeriodically> | |||
<UpdateRequired>false</UpdateRequired> | |||
<MapFileExtensions>true</MapFileExtensions> | |||
<ApplicationRevision>0</ApplicationRevision> | |||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion> | |||
<UseApplicationTrust>false</UseApplicationTrust> | |||
<PublishWizardCompleted>true</PublishWizardCompleted> | |||
<BootstrapperEnabled>true</BootstrapperEnabled> | |||
</PropertyGroup> | |||
<PropertyGroup /> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> | |||
<DebugSymbols>true</DebugSymbols> | |||
<OutputPath>bin\x64\Debug\</OutputPath> | |||
<DefineConstants>DEBUG;TRACE</DefineConstants> | |||
<DebugType>full</DebugType> | |||
<PlatformTarget>x64</PlatformTarget> | |||
<LangVersion>7.3</LangVersion> | |||
<ErrorReport>prompt</ErrorReport> | |||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> | |||
<Prefer32Bit>true</Prefer32Bit> | |||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> | |||
<OutputPath>bin\x64\Release\</OutputPath> | |||
<DefineConstants>TRACE</DefineConstants> | |||
<Optimize>true</Optimize> | |||
<DebugType>embedded</DebugType> | |||
<PlatformTarget>x64</PlatformTarget> | |||
<LangVersion>7.3</LangVersion> | |||
<ErrorReport>prompt</ErrorReport> | |||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> | |||
<Prefer32Bit>true</Prefer32Bit> | |||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<ManifestCertificateThumbprint>B8F0A67800B779C5CEF49BEAB6E5247E373F9452</ManifestCertificateThumbprint> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<ManifestKeyFile>TrustedUninstaller.CLI_TemporaryKey.pfx</ManifestKeyFile> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<GenerateManifests>false</GenerateManifests> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<SignManifests>false</SignManifests> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<TargetZone>LocalIntranet</TargetZone> | |||
</PropertyGroup> | |||
<PropertyGroup /> | |||
<PropertyGroup /> | |||
<PropertyGroup /> | |||
<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" & cmd /c "exit /b 0")</PostBuildEvent> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="Microsoft.Build" /> | |||
<Reference Include="PresentationFramework" /> | |||
<Reference Include="System" /> | |||
<Reference Include="System.Core" /> | |||
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath> | |||
<SpecificVersion>False</SpecificVersion> | |||
<Private>False</Private> | |||
</Reference> | |||
<Reference Include="System.Management" /> | |||
<Reference Include="System.Net.Http" /> | |||
<Reference Include="System.Numerics" /> | |||
<Reference Include="System.ServiceProcess" /> | |||
<Reference Include="System.Windows.Forms" /> | |||
<Reference Include="System.Xml.Linq" /> | |||
<Reference Include="System.Data.DataSetExtensions" /> | |||
<Reference Include="Microsoft.CSharp" /> | |||
<Reference Include="System.Data" /> | |||
<Reference Include="System.Xml" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="CLI.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="App.config" /> | |||
<None Include="app.manifest" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---ame-assassin.exe.config" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<BootstrapperPackage Include=".NETFramework,Version=v4.8"> | |||
<Visible>False</Visible> | |||
<ProductName>Microsoft .NET Framework 4.8 %28x86 and x64%29</ProductName> | |||
<Install>true</Install> | |||
</BootstrapperPackage> | |||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1"> | |||
<Visible>False</Visible> | |||
<ProductName>.NET Framework 3.5 SP1</ProductName> | |||
<Install>false</Install> | |||
</BootstrapperPackage> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\TrustedUninstaller.Shared\TrustedUninstaller.Shared.csproj"> | |||
<Project>{9bda9d32-e9a1-4db8-9d90-443792107e28}</Project> | |||
<Name>TrustedUninstaller.Shared</Name> | |||
</ProjectReference> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.0" /> | |||
<PackageReference Include="Microsoft.PowerShell.5.ReferenceAssemblies" Version="1.1.0" /> | |||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> | |||
<PackageReference Include="SQLitePCLRaw.core" Version="2.0.7" /> | |||
<PackageReference Include="System.Buffers" Version="4.5.1" /> | |||
<PackageReference Include="System.CodeDom" Version="6.0.0" /> | |||
<PackageReference Include="System.IO.Compression" Version="4.0.0" /> | |||
<PackageReference Include="System.Management" Version="6.0.0" /> | |||
<PackageReference Include="System.Memory" Version="4.5.4" /> | |||
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" /> | |||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" /> | |||
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" /> | |||
<PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---ame-assassin.exe" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---assassin-helper.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---assassin-helper.exe" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---e_sqlite3.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---Microsoft.Data.Sqlite.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---Microsoft.Win32.Registry.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---Microsoft.Win32.TaskScheduler.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---SQLitePCLRaw.batteries_v2.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---SQLitePCLRaw.core.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---SQLitePCLRaw.provider.dynamic_cdecl.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---SQLitePCLRaw.provider.e_sqlite3.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---System.Buffers.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---System.CodeDom.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---System.Diagnostics.DiagnosticSource.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---System.Diagnostics.EventLog.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---System.Memory.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---System.Numerics.Vectors.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---System.Runtime.CompilerServices.Unsafe.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ame-assassin---System.Security.Principal.Windows.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---CHANGELOG.txt" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---COPYRIGHT.txt" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---LICENSE.txt" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---README.txt" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---kprocesshacker.sys" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---peview.exe" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---plugins---DotNetTools.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---plugins---ExtendedNotifications.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---plugins---ExtendedServices.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---plugins---ExtendedTools.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---plugins---NetworkTools.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---plugins---OnlineChecks.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---plugins---SbieSupport.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---plugins---ToolStatus.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---plugins---Updater.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---plugins---UserNotes.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---plugins---WindowExplorer.dll" /> | |||
<EmbeddedResource Include="Properties\resources\ProcessHacker---x64---ProcessHacker.exe" /> | |||
</ItemGroup> | |||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
</Project> |
@ -0,0 +1,70 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> | |||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app" /> | |||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> | |||
<security> | |||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> | |||
<!-- UAC Manifest Options | |||
If you want to change the Windows User Account Control level replace the | |||
requestedExecutionLevel node with one of the following. | |||
<requestedExecutionLevel level="asInvoker" uiAccess="false" /> | |||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" /> | |||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" /> | |||
Specifying requestedExecutionLevel element will disable file and registry virtualization. | |||
Remove this element if your application requires this virtualization for backwards | |||
compatibility. | |||
--> | |||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" /> | |||
</requestedPrivileges> | |||
<applicationRequestMinimum> | |||
<defaultAssemblyRequest permissionSetReference="Custom" /> | |||
<PermissionSet ID="Custom" SameSite="site" Unrestricted="true" /> | |||
</applicationRequestMinimum> | |||
</security> | |||
</trustInfo> | |||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> | |||
<application> | |||
<!-- A list of the Windows versions that this application has been tested on | |||
and is designed to work with. Uncomment the appropriate elements | |||
and Windows will automatically select the most compatible environment. --> | |||
<!-- Windows Vista --> | |||
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />--> | |||
<!-- Windows 7 --> | |||
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />--> | |||
<!-- Windows 8 --> | |||
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />--> | |||
<!-- Windows 8.1 --> | |||
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />--> | |||
<!-- Windows 10 --> | |||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> | |||
</application> | |||
</compatibility> | |||
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher | |||
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need | |||
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should | |||
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. --> | |||
<!-- | |||
<application xmlns="urn:schemas-microsoft-com:asm.v3"> | |||
<windowsSettings> | |||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> | |||
</windowsSettings> | |||
</application> | |||
--> | |||
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) --> | |||
<!-- | |||
<dependency> | |||
<dependentAssembly> | |||
<assemblyIdentity | |||
type="win32" | |||
name="Microsoft.Windows.Common-Controls" | |||
version="6.0.0.0" | |||
processorArchitecture="*" | |||
publicKeyToken="6595b64144ccf1df" | |||
language="*" | |||
/> | |||
</dependentAssembly> | |||
</dependency> | |||
--> | |||
</assembly> |
@ -0,0 +1,134 @@ | |||
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; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Text; | |||
using System.Threading; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
// Integrate ame-assassin later | |||
internal class AppxAction : ITaskAction | |||
{ | |||
public enum AppxOperation | |||
{ | |||
Remove = 0, | |||
ClearCache = 1, | |||
} | |||
public enum Level | |||
{ | |||
Family = 0, | |||
Package = 1, | |||
App = 2 | |||
} | |||
[YamlMember(typeof(string), Alias = "name")] | |||
public string Name { get; set; } | |||
[YamlMember(typeof(Level), Alias = "type")] | |||
public Level? Type { get; set; } = Level.Family; | |||
[YamlMember(typeof(AppxOperation), Alias = "operation")] | |||
public AppxOperation Operation { get; set; } = AppxOperation.Remove; | |||
[YamlMember(typeof(bool), Alias = "verboseOutput")] | |||
public bool Verbose { get; set; } = false; | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 30; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"AppxAction failed to remove '{Name}'."; | |||
private Package GetPackage() | |||
{ | |||
var packageManager = new PackageManager(); | |||
return packageManager.FindPackages().FirstOrDefault(package => package.Id.Name == Name); | |||
} | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) return UninstallTaskStatus.InProgress; | |||
return HasFinished ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
//return GetPackage() == null ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
private bool HasFinished = false; | |||
public async Task<bool> RunTask() | |||
{ | |||
if (InProgress) throw new TaskInProgressException("Another Appx action was called while one was in progress."); | |||
InProgress = true; | |||
Console.WriteLine($"Removing APPX {Type.ToString().ToLower()} '{Name}'..."); | |||
string verboseArg = Verbose ? " -Verbose" : ""; | |||
var psi = new ProcessStartInfo() | |||
{ | |||
UseShellExecute = false, | |||
CreateNoWindow = true, | |||
Arguments = $@"-{Type.ToString()} ""{Name}""" + verboseArg, | |||
FileName = Directory.GetCurrentDirectory() + "\\ame-assassin\\ame-assassin.exe", | |||
RedirectStandardOutput = true, | |||
RedirectStandardError = true | |||
}; | |||
if (Operation == AppxOperation.ClearCache) | |||
{ | |||
psi.Arguments = $@"-ClearCache ""{Name}"""; | |||
} | |||
var proc = Process.Start(psi); | |||
proc.OutputDataReceived += ProcOutputHandler; | |||
proc.ErrorDataReceived += ProcOutputHandler; | |||
proc.BeginOutputReadLine(); | |||
proc.BeginErrorReadLine(); | |||
bool exited = proc.WaitForExit(30000); | |||
// WaitForExit alone seems to not be entirely reliable | |||
while (!exited && ExeRunning(proc)) | |||
{ | |||
exited = proc.WaitForExit(30000); | |||
} | |||
using (var log = new StreamWriter("Logs\\Packages.txt", true)) | |||
log.Write(output.ToString()); | |||
HasFinished = true; | |||
InProgress = false; | |||
return true; | |||
} | |||
private StringBuilder output = new StringBuilder(""); | |||
private void ProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) | |||
{ | |||
var write = outLine.Data == null ? "" : outLine.Data; | |||
output.Append(write + Environment.NewLine); | |||
if (!write.Equals("Complete!")) Console.WriteLine(write); | |||
} | |||
private static bool ExeRunning(Process process) | |||
{ | |||
try | |||
{ | |||
return Process.GetProcessesByName(process.ProcessName).Any(x => x.Id == process.Id); | |||
} | |||
catch (Exception) | |||
{ | |||
return false; | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,147 @@ | |||
using System; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Threading.Tasks; | |||
using TrustedUninstaller.Shared.Exceptions; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
public class CmdAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(string), Alias = "command")] | |||
public string Command { get; set; } | |||
[YamlMember(typeof(string), Alias = "timeout")] | |||
public int? Timeout { get; set; } | |||
[YamlMember(typeof(string), Alias = "wait")] | |||
public bool Wait { get; set; } = true; | |||
[YamlMember(typeof(bool), Alias = "exeDir")] | |||
public bool ExeDir { get; set; } = false; | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 1; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
private int? ExitCode { get; set; } | |||
public string? StandardError { get; set; } | |||
public string StandardOutput { get; set; } | |||
public string ErrorString() => $"CmdAction failed to run command '{Command}'."; | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) | |||
{ | |||
return UninstallTaskStatus.InProgress; | |||
} | |||
return ExitCode == null ? UninstallTaskStatus.ToDo: UninstallTaskStatus.Completed; | |||
} | |||
public async Task<bool> RunTask() | |||
{ | |||
if (InProgress) throw new TaskInProgressException("Another Cmd action was called while one was in progress."); | |||
InProgress = true; | |||
Console.WriteLine($"Running cmd command '{Command}'..."); | |||
ExitCode = null; | |||
var process = new Process(); | |||
var startInfo = new ProcessStartInfo | |||
{ | |||
WindowStyle = ProcessWindowStyle.Normal, | |||
FileName = "cmd.exe", | |||
Arguments = "/C " + $"\"{Environment.ExpandEnvironmentVariables(this.Command)}\"", | |||
UseShellExecute = false, | |||
RedirectStandardError = true, | |||
RedirectStandardOutput = true, | |||
CreateNoWindow = true | |||
}; | |||
if (ExeDir) startInfo.WorkingDirectory = AmeliorationUtil.Playbook.Path + "\\Executables"; | |||
if (!Wait) | |||
{ | |||
startInfo.RedirectStandardError = false; | |||
startInfo.RedirectStandardOutput = false; | |||
startInfo.WindowStyle = ProcessWindowStyle.Hidden; | |||
startInfo.UseShellExecute = true; | |||
} | |||
process.StartInfo = startInfo; | |||
process.Start(); | |||
if (!Wait) | |||
{ | |||
process.Dispose(); | |||
return true; | |||
} | |||
process.OutputDataReceived += ProcOutputHandler; | |||
process.BeginOutputReadLine(); | |||
if (Timeout != null) | |||
{ | |||
var exited = process.WaitForExit(Timeout.Value); | |||
if (!exited) | |||
{ | |||
process.Kill(); | |||
throw new TimeoutException($"Command '{Command}' timeout exceeded."); | |||
} | |||
} | |||
else process.WaitForExit(); | |||
if (process.ExitCode != 0) | |||
{ | |||
StandardError = process.StandardError.ReadToEnd(); | |||
Console.WriteLine($"cmd instance exited with error code: {process.ExitCode}"); | |||
if (!String.IsNullOrEmpty(StandardError)) Console.WriteLine($"Error message: {StandardError}"); | |||
ErrorLogger.WriteToErrorLog("Cmd exited with a non-zero exit code: " + process.ExitCode, null, "CmdAction Error", Command); | |||
this.ExitCode = process.ExitCode; | |||
} | |||
else | |||
{ | |||
ExitCode = 0; | |||
} | |||
process.CancelOutputRead(); | |||
process.Dispose(); | |||
InProgress = false; | |||
return true; | |||
} | |||
private static void ProcOutputHandler(object sendingProcess, | |||
DataReceivedEventArgs outLine) | |||
{ | |||
var outputString = outLine.Data; | |||
// Collect the sort command output. | |||
if (!String.IsNullOrEmpty(outLine.Data)) | |||
{ | |||
if (outputString.Contains("\\AME")) | |||
{ | |||
outputString = outputString.Substring(outputString.IndexOf('>') + 1); | |||
} | |||
Console.WriteLine(outputString); | |||
} | |||
else | |||
{ | |||
Console.WriteLine(); | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,640 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Management; | |||
using System.Security.AccessControl; | |||
using System.ServiceProcess; | |||
using System.Text.RegularExpressions; | |||
using System.Threading.Tasks; | |||
using TrustedUninstaller.Shared.Exceptions; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
public class FileAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(string), Alias = "path")] | |||
public string RawPath { get; set; } | |||
[YamlMember(typeof(string), Alias = "prioritizeExe")] | |||
public bool ExeFirst { get; set; } = false; | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 2; | |||
[YamlMember(typeof(string), Alias = "useNSudoTI")] | |||
public bool TrustedInstaller { get; set; } = false; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"FileAction failed to remove file or directory '{Environment.ExpandEnvironmentVariables(RawPath)}'."; | |||
private string GetRealPath() | |||
{ | |||
return Environment.ExpandEnvironmentVariables(RawPath); | |||
} | |||
private string GetRealPath(string path) | |||
{ | |||
return Environment.ExpandEnvironmentVariables(path); | |||
} | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) return UninstallTaskStatus.InProgress; var realPath = GetRealPath(); | |||
if (realPath.Contains("*")) | |||
{ | |||
var lastToken = realPath.LastIndexOf("\\"); | |||
var parentPath = realPath.Remove(lastToken).TrimEnd('\\'); | |||
// This is to prevent it from re-iterating with an incorrect argument | |||
if (parentPath.Contains("*")) return UninstallTaskStatus.Completed; | |||
var filter = realPath.Substring(lastToken + 1); | |||
if (Directory.Exists(parentPath) && (Directory.GetFiles(parentPath, filter).Any() || Directory.GetDirectories(parentPath, filter).Any())) | |||
{ | |||
return UninstallTaskStatus.ToDo; | |||
} | |||
else return UninstallTaskStatus.Completed; | |||
} | |||
var isFile = File.Exists(realPath); | |||
var isDirectory = Directory.Exists(realPath); | |||
return isFile || isDirectory ? UninstallTaskStatus.ToDo : UninstallTaskStatus.Completed; | |||
} | |||
private async Task DeleteFile(string file, bool log = false) | |||
{ | |||
if (!TrustedInstaller) | |||
{ | |||
try {await Task.Run(() => File.Delete(file));} catch {} | |||
if (File.Exists(file)) | |||
{ | |||
CmdAction delAction = new CmdAction() | |||
{ | |||
Command = $"del /q /f {file}" | |||
}; | |||
await delAction.RunTask(); | |||
} | |||
} | |||
else if (File.Exists("NSudoLC.exe")) | |||
{ | |||
RunAction tiDelAction = new RunAction() | |||
{ | |||
Exe = "NSudoLC.exe", | |||
Arguments = $"-U:T -P:E -M:S -Priority:RealTime -UseCurrentConsole -Wait cmd /c \"del /q /f \"{file}\"\"", | |||
BaseDir = true, | |||
CreateWindow = false | |||
}; | |||
await tiDelAction.RunTask(); | |||
if (tiDelAction.Output != null) | |||
{ | |||
if (log) ErrorLogger.WriteToErrorLog(tiDelAction.Output, Environment.StackTrace, | |||
$"FileAction Error", file); | |||
} | |||
} | |||
else | |||
{ | |||
ErrorLogger.WriteToErrorLog($"NSudo was invoked with no supplied NSudo executable.", Environment.StackTrace, | |||
$"FileAction Error", file); | |||
} | |||
} | |||
private async Task RemoveDirectory(string dir, bool log = false) | |||
{ | |||
if (!TrustedInstaller) | |||
{ | |||
try { Directory.Delete(dir, true); } catch { } | |||
if (Directory.Exists(dir)) | |||
{ | |||
Console.WriteLine("Directory still exists.. trying second method."); | |||
var deleteDirCmd = new CmdAction() | |||
{ | |||
Command = $"rmdir /Q /S \"{dir}\"" | |||
}; | |||
await deleteDirCmd.RunTask(); | |||
if (deleteDirCmd.StandardError != null) | |||
{ | |||
Console.WriteLine($"Error Output: {deleteDirCmd.StandardError}"); | |||
} | |||
if (deleteDirCmd.StandardOutput != null) | |||
{ | |||
Console.WriteLine($"Standard Output: {deleteDirCmd.StandardOutput}"); | |||
} | |||
} | |||
} | |||
else if (File.Exists("NSudoLC.exe")) | |||
{ | |||
RunAction tiDelAction = new RunAction() | |||
{ | |||
Exe = "NSudoLC.exe", | |||
Arguments = $"-U:T -P:E -M:S -Priority:RealTime -UseCurrentConsole -Wait cmd /c \"rmdir /q /s \"{dir}\"\"", | |||
BaseDir = true, | |||
CreateWindow = false | |||
}; | |||
await tiDelAction.RunTask(); | |||
if (tiDelAction.Output != null) | |||
{ | |||
if (log) ErrorLogger.WriteToErrorLog(tiDelAction.Output, Environment.StackTrace, | |||
$"FileAction Error", dir); | |||
} | |||
} | |||
else | |||
{ | |||
ErrorLogger.WriteToErrorLog($"NSudo was invoked with no supplied NSudo executable.", Environment.StackTrace, | |||
$"FileAction Error", dir); | |||
} | |||
} | |||
private async Task DeleteItemsInDirectory(string dir, string filter = "*") | |||
{ | |||
var realPath = GetRealPath(dir); | |||
var files = Directory.EnumerateFiles(realPath, filter); | |||
var directories = Directory.EnumerateDirectories(realPath, filter); | |||
if (ExeFirst) files = files.ToList().OrderByDescending(x => x.EndsWith(".exe")); | |||
var lockedFilesList = new List<string> { "MpOAV.dll", "MsMpLics.dll", "EppManifest.dll", "MpAsDesc.dll", "MpClient.dll", "MsMpEng.exe" }; | |||
foreach (var file in files) | |||
{ | |||
Console.WriteLine($"Deleting {file}..."); | |||
System.GC.Collect(); | |||
System.GC.WaitForPendingFinalizers(); | |||
await DeleteFile(file); | |||
if (File.Exists(file)) | |||
{ | |||
TaskKillAction taskKillAction = new TaskKillAction(); | |||
if (file.EndsWith(".sys")) | |||
{ | |||
var driverService = Path.GetFileNameWithoutExtension(file); | |||
try | |||
{ | |||
//ServiceAction won't work here due to it not being able to detect driver services. | |||
var cmdAction = new CmdAction(); | |||
Console.WriteLine($"Removing driver service {driverService}..."); | |||
cmdAction.Command = Environment.Is64BitOperatingSystem ? | |||
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop" : | |||
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop"; | |||
await cmdAction.RunTask(); | |||
cmdAction.Command = Environment.Is64BitOperatingSystem ? | |||
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete" : | |||
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete"; | |||
await cmdAction.RunTask(); | |||
} | |||
catch (Exception servException) | |||
{ | |||
ErrorLogger.WriteToErrorLog(servException.Message, servException.StackTrace, | |||
$"FileAction Error: Error while trying to delete driver service {driverService}.", file); | |||
} | |||
} | |||
if (lockedFilesList.Contains(Path.GetFileName(file))) | |||
{ | |||
TaskKillAction killAction = new TaskKillAction() | |||
{ | |||
ProcessName = "MsMpEng" | |||
}; | |||
await killAction.RunTask(); | |||
killAction.ProcessName = "NisSrv"; | |||
await killAction.RunTask(); | |||
killAction.ProcessName = "SecurityHealthService"; | |||
await killAction.RunTask(); | |||
killAction.ProcessName = "smartscreen"; | |||
await killAction.RunTask(); | |||
} | |||
var processes = new List<Process>(); | |||
try | |||
{ | |||
processes = WinUtil.WhoIsLocking(file); | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, | |||
$"FileAction Error", file); | |||
} | |||
var delay = 0; | |||
int svcCount = 0; | |||
foreach (var svchost in processes.Where(x => x.ProcessName.Equals("svchost"))) | |||
{ | |||
try | |||
{ | |||
using var search = new ManagementObjectSearcher($"select * from Win32_Service where ProcessId = '{svchost.Id}'"); | |||
foreach (ManagementObject queryObj in search.Get()) | |||
{ | |||
var serviceName = (string)queryObj["Name"]; // Access service name | |||
var serv = ServiceController.GetServices().FirstOrDefault(x => x.ServiceName.Equals(serviceName)); | |||
if (serv == null) svcCount++; | |||
else svcCount += serv.DependentServices.Length + 1; | |||
} | |||
} catch (Exception e) | |||
{ | |||
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) | |||
{ | |||
Console.WriteLine("Processes locking the file:"); | |||
foreach (var process in processes) | |||
{ | |||
Console.WriteLine(process.ProcessName); | |||
} | |||
foreach (var process in processes) | |||
{ | |||
try | |||
{ | |||
if (process.ProcessName.Equals("TrustedUninstaller.CLI")) | |||
{ | |||
Console.WriteLine("Skipping TU.CLI..."); | |||
continue; | |||
} | |||
if (Regex.Match(process.ProcessName, "ame.?wizard", RegexOptions.IgnoreCase).Success) | |||
{ | |||
Console.WriteLine("Skipping AME Wizard..."); | |||
continue; | |||
} | |||
taskKillAction.ProcessName = process.ProcessName; | |||
taskKillAction.ProcessID = process.Id; | |||
Console.WriteLine($"Killing locking process {process.ProcessName} with PID {process.Id}..."); | |||
} | |||
catch (InvalidOperationException) | |||
{ | |||
// Calling ProcessName on a process object that has exited will thrown this exception causing the | |||
// entire loop to abort. Since killing a process takes a bit of time, another process in the loop | |||
// could exit during that time. This accounts for that. | |||
continue; | |||
} | |||
await taskKillAction.RunTask(); | |||
} | |||
// This gives any obstinant processes some time to unlock the file on their own. | |||
// | |||
// This could be done above but it's likely to cause HasExited errors if delays are | |||
// introduced after WhoIsLocking. | |||
System.Threading.Thread.Sleep(delay); | |||
try | |||
{ | |||
processes = WinUtil.WhoIsLocking(file); | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, | |||
$"FileAction Error", file); | |||
} | |||
delay += 100; | |||
} | |||
if (delay >= 800) | |||
ErrorLogger.WriteToErrorLog($"Could not kill locking processes for file '{file}'. Process termination loop exceeded max cycles (8).", | |||
Environment.StackTrace, "FileAction Error"); | |||
await DeleteFile(file, true); | |||
using (var writer = new StreamWriter("Logs\\FileChecklist.txt", true)) | |||
{ | |||
writer.WriteLine($"File Path: {file}\r\nDeleted: {!File.Exists(file)}\r\n" + | |||
$"======================"); | |||
} | |||
} | |||
} | |||
//Loop through any subdirectories | |||
foreach (var directory in directories) | |||
{ | |||
//Deletes the content of the directory | |||
await DeleteItemsInDirectory(directory); | |||
System.GC.Collect(); | |||
System.GC.WaitForPendingFinalizers(); | |||
await RemoveDirectory(directory, true); | |||
if (Directory.Exists(directory)) | |||
ErrorLogger.WriteToErrorLog($"Could not remove directory '{directory}'.", | |||
Environment.StackTrace, $"FileAction Error"); | |||
} | |||
} | |||
public async Task<bool> RunTask() | |||
{ | |||
if (InProgress) throw new TaskInProgressException("Another File action was called while one was in progress."); | |||
InProgress = true; | |||
var realPath = GetRealPath(); | |||
Console.WriteLine($"Removing file or directory '{realPath}'..."); | |||
if (realPath.Contains("*")) | |||
{ | |||
var lastToken = realPath.LastIndexOf("\\"); | |||
var parentPath = realPath.Remove(lastToken).TrimEnd('\\'); | |||
if (parentPath.Contains("*")) throw new ArgumentException("Parent directories to a given file filter cannot contain wildcards."); | |||
var filter = realPath.Substring(lastToken + 1); | |||
await DeleteItemsInDirectory(parentPath, filter); | |||
InProgress = false; | |||
return true; | |||
} | |||
var isFile = File.Exists(realPath); | |||
var isDirectory = Directory.Exists(realPath); | |||
if (isDirectory) | |||
{ | |||
System.GC.Collect(); | |||
System.GC.WaitForPendingFinalizers(); | |||
await RemoveDirectory(realPath); | |||
if (Directory.Exists(realPath)) | |||
{ | |||
CmdAction permAction = new CmdAction() | |||
{ | |||
Command = $"takeown /f \"{realPath}\" /r /d Y>NUL & icacls \"{realPath}\" /t /grant Administrators:F /c > NUL", | |||
Timeout = 5000 | |||
}; | |||
try | |||
{ | |||
await permAction.RunTask(); | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "FileAction Error", realPath); | |||
} | |||
try | |||
{ | |||
if (realPath.Contains("Defender")) | |||
{ | |||
TaskKillAction killAction = new TaskKillAction() | |||
{ | |||
ProcessName = "MsMpEng" | |||
}; | |||
await killAction.RunTask(); | |||
killAction.ProcessName = "NisSrv"; | |||
await killAction.RunTask(); | |||
killAction.ProcessName = "SecurityHealthService"; | |||
await killAction.RunTask(); | |||
killAction.ProcessName = "smartscreen"; | |||
await killAction.RunTask(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, | |||
$"FileAction Error", realPath); | |||
} | |||
await RemoveDirectory(realPath, true); | |||
if (Directory.Exists(realPath)) | |||
{ | |||
//Delete the files in the initial directory. DOES delete directories. | |||
await DeleteItemsInDirectory(realPath); | |||
System.GC.Collect(); | |||
System.GC.WaitForPendingFinalizers(); | |||
await RemoveDirectory(realPath, true); | |||
} | |||
} | |||
} | |||
else if (isFile) | |||
{ | |||
try | |||
{ | |||
var lockedFilesList = new List<string> { "MpOAV.dll", "MsMpLics.dll", "EppManifest.dll", "MpAsDesc.dll", "MpClient.dll", "MsMpEng.exe" }; | |||
var fileName = realPath.Split('\\').LastOrDefault(); | |||
System.GC.Collect(); | |||
System.GC.WaitForPendingFinalizers(); | |||
await DeleteFile(realPath); | |||
if (File.Exists(realPath)) | |||
{ | |||
CmdAction permAction = new CmdAction() | |||
{ | |||
Command = $"takeown /f \"{realPath}\" /r /d Y>NUL & icacls \"{realPath}\" /t /grant Administrators:F /c > NUL", | |||
Timeout = 5000 | |||
}; | |||
try | |||
{ | |||
await permAction.RunTask(); | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "FileAction Error", realPath); | |||
} | |||
TaskKillAction taskKillAction = new TaskKillAction(); | |||
if (realPath.EndsWith(".sys")) | |||
{ | |||
var driverService = Path.GetFileNameWithoutExtension(realPath); | |||
try | |||
{ | |||
//ServiceAction won't work here due to it not being able to detect driver services. | |||
var cmdAction = new CmdAction(); | |||
Console.WriteLine($"Removing driver service {driverService}..."); | |||
cmdAction.Command = Environment.Is64BitOperatingSystem ? | |||
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop" : | |||
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop"; | |||
await cmdAction.RunTask(); | |||
cmdAction.Command = Environment.Is64BitOperatingSystem ? | |||
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete" : | |||
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete"; | |||
await cmdAction.RunTask(); | |||
} | |||
catch (Exception servException) | |||
{ | |||
ErrorLogger.WriteToErrorLog(servException.Message, servException.StackTrace, | |||
$"FileAction Error: Error trying to delete driver service {driverService}.", realPath); | |||
} | |||
} | |||
if (lockedFilesList.Contains(fileName)) | |||
{ | |||
TaskKillAction killAction = new TaskKillAction() | |||
{ | |||
ProcessName = "MsMpEng" | |||
}; | |||
await killAction.RunTask(); | |||
killAction.ProcessName = "NisSrv"; | |||
await killAction.RunTask(); | |||
killAction.ProcessName = "SecurityHealthService"; | |||
await killAction.RunTask(); | |||
killAction.ProcessName = "smartscreen"; | |||
await killAction.RunTask(); | |||
} | |||
var processes = new List<Process>(); | |||
try | |||
{ | |||
processes = WinUtil.WhoIsLocking(realPath); | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, | |||
$"FileAction Error", realPath); | |||
} | |||
var delay = 0; | |||
int svcCount = 0; | |||
foreach (var svchost in processes.Where(x => x.ProcessName.Equals("svchost"))) | |||
{ | |||
try | |||
{ | |||
using var search = new ManagementObjectSearcher($"select * from Win32_Service where ProcessId = '{svchost.Id}'"); | |||
foreach (ManagementObject queryObj in search.Get()) | |||
{ | |||
var serviceName = (string)queryObj["Name"]; // Access service name | |||
var serv = ServiceController.GetServices().FirstOrDefault(x => x.ServiceName.Equals(serviceName)); | |||
if (serv == null) svcCount++; | |||
else svcCount += serv.DependentServices.Length + 1; | |||
} | |||
} catch (Exception e) | |||
{ | |||
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) | |||
{ | |||
Console.WriteLine("Processes locking the file:"); | |||
foreach (var process in processes) | |||
{ | |||
Console.WriteLine(process.ProcessName); | |||
} | |||
foreach (var process in processes) | |||
{ | |||
try | |||
{ | |||
if (process.ProcessName.Equals("TrustedUninstaller.CLI")) | |||
{ | |||
Console.WriteLine("Skipping TU.CLI..."); | |||
continue; | |||
} | |||
if (Regex.Match(process.ProcessName, "ame.?wizard", RegexOptions.IgnoreCase).Success) | |||
{ | |||
Console.WriteLine("Skipping AME Wizard..."); | |||
continue; | |||
} | |||
taskKillAction.ProcessName = process.ProcessName; | |||
taskKillAction.ProcessID = process.Id; | |||
Console.WriteLine($"Killing {process.ProcessName} with PID {process.Id}... it is locking {realPath}"); | |||
} | |||
catch (InvalidOperationException) | |||
{ | |||
// Calling ProcessName on a process object that has exited will thrown this exception causing the | |||
// entire loop to abort. Since killing a process takes a bit of time, another process in the loop | |||
// could exit during that time. This accounts for that. | |||
continue; | |||
} | |||
try | |||
{ | |||
await taskKillAction.RunTask(); | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, | |||
$"FileAction Error: Could not kill process {process.ProcessName}."); | |||
} | |||
} | |||
// This gives any obstinant processes some time to unlock the file on their own. | |||
// | |||
// This could be done above but it's likely to cause HasExited errors if delays are | |||
// introduced after WhoIsLocking. | |||
System.Threading.Thread.Sleep(delay); | |||
try | |||
{ | |||
processes = WinUtil.WhoIsLocking(realPath); | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, | |||
$"FileAction Error", realPath); | |||
} | |||
delay += 100; | |||
} | |||
if (delay >= 800) | |||
ErrorLogger.WriteToErrorLog($"Could not kill locking processes for file '{realPath}'. Process termination loop exceeded max cycles (8).", | |||
Environment.StackTrace, "FileAction Error"); | |||
await DeleteFile(realPath, true); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, | |||
$"FileAction Error: Error while trying to delete {realPath}."); | |||
} | |||
using (var writer = new StreamWriter("Logs\\FileChecklist.txt", true)) | |||
{ | |||
writer.WriteLine($"File Path: {realPath}\r\nDeleted: {!File.Exists(realPath)}\r\n" + | |||
$"======================"); | |||
} | |||
} | |||
else | |||
{ | |||
Console.WriteLine($"File or directory '{realPath}' not found."); | |||
} | |||
InProgress = false; | |||
return true; | |||
} | |||
} | |||
} |
@ -0,0 +1,50 @@ | |||
using System.Linq; | |||
using System.Runtime.InteropServices; | |||
using System.Threading.Tasks; | |||
using System.Windows.Forms; | |||
using TrustedUninstaller.Shared.Tasks; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
class LanguageAction : ITaskAction | |||
{ | |||
public int ProgressWeight { get; set; } = 1; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string Tag { get; set; } = ""; | |||
public string Primary() => "for language " + Tag; | |||
public bool Display { get; set; } = false; | |||
public string ErrorString() => $"LanguageAction failed to install language {Tag}."; | |||
[DllImport("intl.cpl", CharSet = CharSet.Unicode, SetLastError = true)] | |||
public static extern int IntlUpdateSystemLocale(string LocaleName, int dwFlags); | |||
public UninstallTaskStatus GetStatus() => | |||
InputLanguage.InstalledInputLanguages.Cast<InputLanguage>() | |||
.Any(c => c.Culture.IetfLanguageTag == this.Tag) | |||
? UninstallTaskStatus.Completed | |||
: UninstallTaskStatus.ToDo; | |||
public async Task<bool> RunTask() | |||
{ | |||
if (GetStatus() != UninstallTaskStatus.ToDo) | |||
{ | |||
return false; | |||
} | |||
if (this.Display) | |||
{ | |||
// Reversed from the Set-WinSystemLocale cmdlet... | |||
// TODO: Figure out the return value lol | |||
IntlUpdateSystemLocale(this.Tag, 2); | |||
} | |||
// TODO: Installing languages is AIDS | |||
return false; | |||
} | |||
} | |||
} |
@ -0,0 +1,98 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using TrustedUninstaller.Shared.Exceptions; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
internal enum LineInFileOperation | |||
{ | |||
Delete = 0, | |||
Add = 1 | |||
} | |||
internal class LineInFileAction : ITaskAction | |||
{ | |||
[YamlMember(Alias = "path")] | |||
public string RawPath { get; set; } | |||
[YamlMember(Alias = "line")] | |||
public string RawLines { get; set; } | |||
[YamlMember(Alias = "operation")] | |||
public LineInFileOperation Operation { get; set; } = LineInFileOperation.Delete; | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 1; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } = false; | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"LineInFileAction failed to {Operation.ToString().ToLower()} lines to file '{RawPath}'."; | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) | |||
{ | |||
return UninstallTaskStatus.InProgress; | |||
} | |||
var realPath = this.GetRealPath(); | |||
if (!File.Exists(realPath)) | |||
{ | |||
// If the file doesn't exist it can't contain the lines either, can it? | |||
return Operation == LineInFileOperation.Delete ? | |||
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
var isDone = !GetMissingLines().Any(); | |||
return isDone ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
private IEnumerable<string> GetLines() => | |||
RawLines.Split( | |||
new[] { "\r\n", "\r", "\n" }, | |||
StringSplitOptions.None | |||
); | |||
private IEnumerable<string> GetMissingLines() | |||
{ | |||
var realPath = this.GetRealPath(); | |||
var fileLines = File.ReadAllLines(realPath); | |||
var targetLines = GetLines(); | |||
return targetLines.Where(line => !fileLines.Contains(line)); | |||
} | |||
private string GetRealPath() | |||
{ | |||
return Environment.ExpandEnvironmentVariables(RawPath); | |||
} | |||
public async Task<bool> RunTask() | |||
{ | |||
if (InProgress) throw new TaskInProgressException("Another LineInFile action was called while one was in progress."); | |||
InProgress = true; | |||
//Wording should be improved here | |||
Console.WriteLine($"{Operation.ToString().TrimEnd('e')}ing text lines in file '{RawPath}'..."); | |||
var realPath = this.GetRealPath(); | |||
var missingLines = GetMissingLines(); | |||
using var sw = File.AppendText(realPath); | |||
foreach (var missingLine in missingLines) | |||
{ | |||
await sw.WriteLineAsync(missingLine); | |||
} | |||
InProgress = false; | |||
return true; | |||
} | |||
} | |||
} |
@ -0,0 +1,150 @@ | |||
using System; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Management.Automation; | |||
using System.Text; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using System.Windows.Forms; | |||
using TrustedUninstaller.Shared.Exceptions; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
public class PowerShellAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(string), Alias = "command")] | |||
public string Command { get; set; } | |||
[YamlMember(typeof(string), Alias = "timeout")] | |||
public int? Timeout { get; set; } | |||
[YamlMember(typeof(string), Alias = "wait")] | |||
public bool Wait { get; set; } = true; | |||
[YamlMember(typeof(bool), Alias = "exeDir")] | |||
public bool ExeDir { get; set; } = false; | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 1; | |||
private int? ExitCode { get; set; } | |||
public string? StandardError { get; set; } | |||
public string StandardOutput { get; set; } | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"PowerShellAction failed to run command '{Command}'."; | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) | |||
{ | |||
return UninstallTaskStatus.InProgress; | |||
} | |||
return ExitCode == null ? UninstallTaskStatus.ToDo: UninstallTaskStatus.Completed; | |||
} | |||
public async Task<bool> RunTask() | |||
{ | |||
if (InProgress) throw new TaskInProgressException("Another Powershell action was called while one was in progress."); | |||
InProgress = true; | |||
Console.WriteLine($"Running PowerShell command '{Command}'..."); | |||
var process = new Process(); | |||
var startInfo = new ProcessStartInfo | |||
{ | |||
WindowStyle = ProcessWindowStyle.Normal, | |||
FileName = "PowerShell.exe", | |||
Arguments = $@"-NoP -ExecutionPolicy Bypass -NonInteractive -C ""{Command}""", | |||
UseShellExecute = false, | |||
RedirectStandardError = true, | |||
RedirectStandardOutput = true, | |||
CreateNoWindow = true | |||
}; | |||
if (ExeDir) startInfo.WorkingDirectory = Directory.GetCurrentDirectory() + "\\Executables"; | |||
if (!Wait) | |||
{ | |||
startInfo.RedirectStandardError = false; | |||
startInfo.RedirectStandardOutput = false; | |||
startInfo.WindowStyle = ProcessWindowStyle.Hidden; | |||
startInfo.UseShellExecute = true; | |||
} | |||
process.StartInfo = startInfo; | |||
process.Start(); | |||
if (!Wait) | |||
{ | |||
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(); | |||
process.BeginErrorReadLine(); | |||
if (Timeout != null) | |||
{ | |||
var exited = process.WaitForExit(Timeout.Value); | |||
if (!exited) | |||
{ | |||
process.Kill(); | |||
throw new TimeoutException($"Command '{Command}' timeout exceeded."); | |||
} | |||
} | |||
else process.WaitForExit(); | |||
StandardError = error.ToString(); | |||
if (process.ExitCode != 0) | |||
{ | |||
Console.WriteLine($"PowerShell instance exited with error code: {process.ExitCode}"); | |||
if (!String.IsNullOrEmpty(StandardError)) Console.WriteLine($"Error message: {StandardError}"); | |||
ErrorLogger.WriteToErrorLog("PowerShell exited with a non-zero exit code: " + process.ExitCode, null, "PowerShellAction Error", Command); | |||
this.ExitCode = process.ExitCode; | |||
} | |||
else | |||
{ | |||
if (!String.IsNullOrEmpty(StandardError)) Console.WriteLine($"Error output: {StandardError}"); | |||
ExitCode = 0; | |||
} | |||
process.CancelOutputRead(); | |||
process.CancelErrorRead(); | |||
process.Dispose(); | |||
InProgress = false; | |||
return true; | |||
} | |||
private static void ProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) | |||
{ | |||
if (!String.IsNullOrEmpty(outLine.Data)) | |||
{ | |||
Console.WriteLine(outLine.Data); | |||
} | |||
else | |||
{ | |||
Console.WriteLine(); | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,199 @@ | |||
#nullable enable | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Runtime.InteropServices; | |||
using System.Security; | |||
using System.Text.RegularExpressions; | |||
using System.Threading.Tasks; | |||
using Microsoft.Win32; | |||
using TrustedUninstaller.Shared.Exceptions; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
internal enum RegistryKeyOperation | |||
{ | |||
Delete = 0, | |||
Add = 1 | |||
} | |||
class RegistryKeyAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(string), Alias = "path")] | |||
public string KeyName { get; set; } | |||
[YamlMember(typeof(Scope), Alias = "scope")] | |||
public Scope Scope { get; set; } = Scope.AllUsers; | |||
[YamlMember(typeof(RegistryKeyOperation), Alias = "operation")] | |||
public RegistryKeyOperation Operation { get; set; } = RegistryKeyOperation.Delete; | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 1; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"RegistryKeyAction failed to {Operation.ToString().ToLower()} key '{KeyName}'."; | |||
private List<RegistryKey> GetRoots() | |||
{ | |||
var hive = KeyName.Split('\\').GetValue(0).ToString().ToUpper(); | |||
var list = new List<RegistryKey>(); | |||
if (hive.Equals("HKCU") || hive.Equals("HKEY_CURRENT_USER")) | |||
{ | |||
RegistryKey usersKey; | |||
List<string> userKeys; | |||
switch (Scope) | |||
{ | |||
case Scope.AllUsers: | |||
WinUtil.RegistryManager.HookUserHives(); | |||
usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); | |||
userKeys = usersKey.GetSubKeyNames(). | |||
Where(x => x.StartsWith("S-") && | |||
usersKey.OpenSubKey(x).GetSubKeyNames().Any(y => y.Equals("Volatile Environment"))).ToList(); | |||
userKeys.AddRange(usersKey.GetSubKeyNames().Where(x => x.StartsWith("AME_UserHive_") && !x.EndsWith("_Classes")).ToList()); | |||
userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true))); | |||
return list; | |||
case Scope.ActiveUsers: | |||
usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); | |||
userKeys = usersKey.GetSubKeyNames(). | |||
Where(x => x.StartsWith("S-") && | |||
usersKey.OpenSubKey(x).GetSubKeyNames().Any(y => y.Equals("Volatile Environment"))).ToList(); | |||
userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true))); | |||
return list; | |||
case Scope.DefaultUser: | |||
usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); | |||
userKeys = usersKey.GetSubKeyNames().Where(x => x.Equals("AME_UserHive_Default") && !x.EndsWith("_Classes")).ToList(); | |||
userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true))); | |||
return list; | |||
} | |||
} | |||
list.Add(hive switch | |||
{ | |||
"HKCU" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), | |||
"HKEY_CURRENT_USER" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), | |||
"HKLM" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), | |||
"HKEY_LOCAL_MACHINE" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), | |||
"HKCR" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), | |||
"HKEY_CLASSES_ROOT" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), | |||
"HKU" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), | |||
"HKEY_USERS" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), | |||
_ => throw new ArgumentException($"Key '{KeyName}' does not specify a valid registry hive.") | |||
}); | |||
return list; | |||
} | |||
public string GetSubKey() => KeyName.Substring(KeyName.IndexOf('\\') + 1); | |||
private RegistryKey? OpenSubKey(RegistryKey root) | |||
{ | |||
var subKeyPath = GetSubKey(); | |||
if (subKeyPath == null) throw new ArgumentException($"Key '{KeyName}' is invalid."); | |||
return root.OpenSubKey(subKeyPath, true); | |||
} | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
try | |||
{ | |||
var roots = GetRoots(); | |||
foreach (var _root in roots) | |||
{ | |||
var root = _root; | |||
var subKey = GetSubKey(); | |||
if (root.Name.Contains("AME_UserHive_") && subKey.StartsWith("SOFTWARE\\Classes", StringComparison.CurrentCultureIgnoreCase)) | |||
{ | |||
var usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); | |||
root = usersKey.OpenSubKey(root.Name.Substring(11) + "_Classes", true); | |||
subKey = Regex.Replace(subKey, @"^SOFTWARE\\*Classes\\*", "", RegexOptions.IgnoreCase); | |||
if (root == null) | |||
{ | |||
continue; | |||
} | |||
} | |||
var openedSubKey = root.OpenSubKey(subKey); | |||
if (Operation == RegistryKeyOperation.Delete && openedSubKey != null) | |||
{ | |||
return UninstallTaskStatus.ToDo; | |||
} | |||
if (Operation == RegistryKeyOperation.Add && openedSubKey == null) | |||
{ | |||
return UninstallTaskStatus.ToDo; | |||
} | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, | |||
e.StackTrace, "RegistryKeyAction Error"); | |||
return UninstallTaskStatus.ToDo; | |||
} | |||
return UninstallTaskStatus.Completed; | |||
} | |||
public async Task<bool> RunTask() | |||
{ | |||
Console.WriteLine($"{Operation.ToString().TrimEnd('e')}ing registry key '{KeyName}'..."); | |||
var roots = GetRoots(); | |||
foreach (var _root in roots) | |||
{ | |||
var root = _root; | |||
var subKey = GetSubKey(); | |||
try | |||
{ | |||
if (root.Name.Contains("AME_UserHive_") && subKey.StartsWith("SOFTWARE\\Classes", StringComparison.CurrentCultureIgnoreCase)) | |||
{ | |||
var usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); | |||
root = usersKey.OpenSubKey(root.Name.Substring(11) + "_Classes", true); | |||
subKey = Regex.Replace(subKey, @"^SOFTWARE\\*Classes\\*", "", RegexOptions.IgnoreCase); | |||
if (root == null) | |||
{ | |||
ErrorLogger.WriteToErrorLog($"User classes hive not found for hive {_root.Name}.", | |||
Environment.StackTrace, "RegistryKeyAction Error"); | |||
continue; | |||
} | |||
} | |||
if (Operation == RegistryKeyOperation.Add && root.OpenSubKey(subKey) == null) | |||
{ | |||
root.CreateSubKey(subKey); | |||
} | |||
if (Operation == RegistryKeyOperation.Delete) | |||
{ | |||
root.DeleteSubKeyTree(subKey, false); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, | |||
e.StackTrace, "RegistryKeyAction Error", root?.Name + "\\" + subKey); | |||
} | |||
} | |||
return true; | |||
} | |||
} | |||
} |
@ -0,0 +1,332 @@ | |||
#nullable enable | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Security; | |||
using System.Text.RegularExpressions; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using Microsoft.Win32; | |||
using TrustedUninstaller.Shared.Exceptions; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
public enum RegistryValueOperation | |||
{ | |||
Delete = 0, | |||
Add = 1, | |||
// This indicates to skip the action if the specified value does not already exist | |||
Set = 2 | |||
} | |||
public enum RegistryValueType | |||
{ | |||
REG_SZ = RegistryValueKind.String, | |||
REG_MULTI_SZ = RegistryValueKind.MultiString, | |||
REG_EXPAND_SZ = RegistryValueKind.ExpandString, | |||
REG_DWORD = RegistryValueKind.DWord, | |||
REG_QWORD = RegistryValueKind.QWord, | |||
REG_BINARY = RegistryValueKind.Binary, | |||
REG_NONE = RegistryValueKind.None, | |||
REG_UNKNOWN = RegistryValueKind.Unknown | |||
} | |||
public class RegistryValueAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(string), Alias = "path")] | |||
public string KeyName { get; set; } | |||
[YamlMember(typeof(string), Alias = "value")] | |||
public string Value { get; set; } = ""; | |||
[YamlMember(typeof(object), Alias = "data")] | |||
public object? Data { get; set; } | |||
[YamlMember(typeof(RegistryValueType), Alias = "type")] | |||
public RegistryValueType Type { get; set; } | |||
[YamlMember(typeof(Scope), Alias = "scope")] | |||
public Scope Scope { get; set; } = Scope.AllUsers; | |||
[YamlMember(typeof(RegistryValueOperation), Alias = "operation")] | |||
public RegistryValueOperation Operation { get; set; } = RegistryValueOperation.Add; | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 0; | |||
public int GetProgressWeight() | |||
{ | |||
int roots; | |||
try | |||
{ | |||
roots = GetRoots().Count; | |||
} | |||
catch (Exception) | |||
{ | |||
roots = 1; | |||
} | |||
return ProgressWeight + roots; | |||
} | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"RegistryValueAction failed to {Operation.ToString().ToLower()} value '{Value}' in key '{KeyName}'"; | |||
private List<RegistryKey> GetRoots() | |||
{ | |||
var hive = KeyName.Split('\\').GetValue(0).ToString().ToUpper(); | |||
var list = new List<RegistryKey>(); | |||
if (hive.Equals("HKCU") || hive.Equals("HKEY_CURRENT_USER")) | |||
{ | |||
RegistryKey usersKey; | |||
List<string> userKeys; | |||
switch (Scope) | |||
{ | |||
case Scope.AllUsers: | |||
WinUtil.RegistryManager.HookUserHives(); | |||
usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); | |||
userKeys = usersKey.GetSubKeyNames(). | |||
Where(x => x.StartsWith("S-") && | |||
usersKey.OpenSubKey(x).GetSubKeyNames().Any(y => y.Equals("Volatile Environment"))).ToList(); | |||
userKeys.AddRange(usersKey.GetSubKeyNames().Where(x => x.StartsWith("AME_UserHive_") && !x.EndsWith("_Classes")).ToList()); | |||
userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true))); | |||
return list; | |||
case Scope.ActiveUsers: | |||
usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); | |||
userKeys = usersKey.GetSubKeyNames(). | |||
Where(x => x.StartsWith("S-") && | |||
usersKey.OpenSubKey(x).GetSubKeyNames().Any(y => y.Equals("Volatile Environment"))).ToList(); | |||
userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true))); | |||
return list; | |||
case Scope.DefaultUser: | |||
usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); | |||
userKeys = usersKey.GetSubKeyNames().Where(x => x.Equals("AME_UserHive_Default") && !x.EndsWith("_Classes")).ToList(); | |||
userKeys.ForEach(x => list.Add(usersKey.OpenSubKey(x, true))); | |||
return list; | |||
} | |||
} | |||
list.Add(hive switch | |||
{ | |||
"HKCU" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), | |||
"HKEY_CURRENT_USER" => RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default), | |||
"HKLM" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), | |||
"HKEY_LOCAL_MACHINE" => RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default), | |||
"HKCR" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), | |||
"HKEY_CLASSES_ROOT" => RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default), | |||
"HKU" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), | |||
"HKEY_USERS" => RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default), | |||
_ => throw new ArgumentException($"Key '{KeyName}' does not specify a valid registry hive.") | |||
}); | |||
return list; | |||
} | |||
public string GetSubKey() => KeyName.Substring(KeyName.IndexOf('\\') + 1); | |||
private RegistryKey? OpenSubKey(RegistryKey root) | |||
{ | |||
var subKeyPath = GetSubKey(); | |||
if (subKeyPath == null) throw new ArgumentException($"Key '{KeyName}' is invalid."); | |||
return root.OpenSubKey(subKeyPath, true); | |||
} | |||
public object? GetCurrentValue(RegistryKey root) | |||
{ | |||
var subkey = GetSubKey(); | |||
return Registry.GetValue(root.Name + "\\" + subkey, Value, null); | |||
} | |||
public static byte[] StringToByteArray(string hex) { | |||
return Enumerable.Range(0, hex.Length) | |||
.Where(x => x % 2 == 0) | |||
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) | |||
.ToArray(); | |||
} | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
try | |||
{ | |||
var roots = GetRoots(); | |||
foreach (var _root in roots) | |||
{ | |||
var root = _root; | |||
var subKey = GetSubKey(); | |||
if (root.Name.Contains("AME_UserHive_") && subKey.StartsWith("SOFTWARE\\Classes", StringComparison.CurrentCultureIgnoreCase)) | |||
{ | |||
var usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); | |||
root = usersKey.OpenSubKey(root.Name.Substring(11) + "_Classes", true); | |||
subKey = Regex.Replace(subKey, @"^SOFTWARE\\*Classes\\*", "", RegexOptions.IgnoreCase); | |||
if (root == null) | |||
{ | |||
continue; | |||
} | |||
} | |||
var openedSubKey = root.OpenSubKey(subKey); | |||
if (openedSubKey == null && (Operation == RegistryValueOperation.Set || Operation == RegistryValueOperation.Delete)) | |||
continue; | |||
if (openedSubKey == null) return UninstallTaskStatus.ToDo; | |||
var value = openedSubKey.GetValue(Value); | |||
if (value == null) | |||
{ | |||
if (Operation == RegistryValueOperation.Set || Operation == RegistryValueOperation.Delete) | |||
continue; | |||
return UninstallTaskStatus.ToDo; | |||
} | |||
if (Operation == RegistryValueOperation.Delete) return UninstallTaskStatus.ToDo; | |||
if (Data == null) return UninstallTaskStatus.ToDo; | |||
bool matches; | |||
try | |||
{ | |||
matches = Type switch | |||
{ | |||
RegistryValueType.REG_SZ => | |||
Data.ToString() == value.ToString(), | |||
RegistryValueType.REG_EXPAND_SZ => | |||
// RegistryValueOptions.DoNotExpandEnvironmentNames above did not seem to work. | |||
Environment.ExpandEnvironmentVariables(Data.ToString()) == value.ToString(), | |||
RegistryValueType.REG_MULTI_SZ => | |||
Data.ToString() == "" ? | |||
((string[])value).SequenceEqual(new string[] { }) : | |||
((string[])value).SequenceEqual(Data.ToString().Split(new string[] { "\\0" }, StringSplitOptions.None)), | |||
RegistryValueType.REG_DWORD => | |||
unchecked((int)Convert.ToUInt32(Data)) == (int)value, | |||
RegistryValueType.REG_QWORD => | |||
Convert.ToUInt64(Data) == (ulong)value, | |||
RegistryValueType.REG_BINARY => | |||
((byte[])value).SequenceEqual(StringToByteArray(Data.ToString())), | |||
RegistryValueType.REG_NONE => | |||
((byte[])value).SequenceEqual(new byte[0]), | |||
RegistryValueType.REG_UNKNOWN => | |||
Data.ToString() == value.ToString(), | |||
_ => throw new ArgumentException("Impossible.") | |||
}; | |||
} | |||
catch (InvalidCastException) | |||
{ | |||
matches = false; | |||
} | |||
if (!matches) return UninstallTaskStatus.ToDo; | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, | |||
e.StackTrace, "RegistryValueAction Error"); | |||
return UninstallTaskStatus.ToDo; | |||
} | |||
return UninstallTaskStatus.Completed; | |||
} | |||
public async Task<bool> RunTask() | |||
{ | |||
Console.WriteLine($"{Operation.ToString().TrimEnd('e')}ing value '{Value}' in key '{KeyName}'..."); | |||
var roots = GetRoots(); | |||
foreach (var _root in roots) | |||
{ | |||
var root = _root; | |||
var subKey = GetSubKey(); | |||
try | |||
{ | |||
if (root.Name.Contains("AME_UserHive_") && subKey.StartsWith("SOFTWARE\\Classes", StringComparison.CurrentCultureIgnoreCase)) | |||
{ | |||
var usersKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default); | |||
root = usersKey.OpenSubKey(root.Name.Substring(11) + "_Classes", true); | |||
subKey = Regex.Replace(subKey, @"^SOFTWARE\\*Classes\\*", "", RegexOptions.IgnoreCase); | |||
if (root == null) | |||
{ | |||
ErrorLogger.WriteToErrorLog($"User classes hive not found for hive {_root.Name}.", | |||
Environment.StackTrace, "RegistryValueAction Error"); | |||
continue; | |||
} | |||
} | |||
if (GetCurrentValue(root) == Data) continue; | |||
if (root.OpenSubKey(subKey) == null && Operation == RegistryValueOperation.Set) continue; | |||
if (root.OpenSubKey(subKey) == null && Operation == RegistryValueOperation.Add) root.CreateSubKey(subKey); | |||
if (Operation == RegistryValueOperation.Delete) | |||
{ | |||
var key = root.OpenSubKey(subKey, true); | |||
key?.DeleteValue(Value); | |||
continue; | |||
} | |||
if (Type == RegistryValueType.REG_BINARY) | |||
{ | |||
var data = StringToByteArray(Data.ToString()); | |||
Registry.SetValue(root.Name + "\\" + subKey, Value, data, (RegistryValueKind)Type); | |||
} | |||
else if (Type == RegistryValueType.REG_DWORD) | |||
{ | |||
// DWORD values using the highest bit set fail without this, for example '2962489444'. | |||
// See https://stackoverflow.com/questions/6608400/how-to-put-a-dword-in-the-registry-with-the-highest-bit-set; | |||
var value = unchecked((int)Convert.ToUInt32(Data)); | |||
Registry.SetValue(root.Name + "\\" + subKey, Value, value, (RegistryValueKind)Type); | |||
} | |||
else if (Type == RegistryValueType.REG_QWORD) | |||
{ | |||
Registry.SetValue(root.Name + "\\" + subKey, Value, Convert.ToUInt64(Data), (RegistryValueKind)Type); | |||
} | |||
else if (Type == RegistryValueType.REG_NONE) | |||
{ | |||
byte[] none = new byte[0]; | |||
Registry.SetValue(root.Name + "\\" + subKey, Value, none, (RegistryValueKind)Type); | |||
} | |||
else if (Type == RegistryValueType.REG_MULTI_SZ) | |||
{ | |||
string[] data; | |||
if (Data.ToString() == "") data = new string[] { }; | |||
else data = Data.ToString().Split(new string[] { "\\0" }, StringSplitOptions.None); | |||
Registry.SetValue(root.Name + "\\" + subKey, Value, data, (RegistryValueKind)Type); | |||
} | |||
else | |||
{ | |||
Registry.SetValue(root.Name + "\\" + subKey, Value, Data, (RegistryValueKind)Type); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, | |||
e.StackTrace, "RegistryValueAction Error", root?.Name + "\\" + subKey); | |||
} | |||
} | |||
return true; | |||
} | |||
} | |||
} |
@ -0,0 +1,221 @@ | |||
using System; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
public class RunAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(string), Alias = "path")] | |||
public string RawPath { get; set; } = null; | |||
[YamlMember(typeof(string), Alias = "exe")] | |||
public string Exe { get; set; } | |||
[YamlMember(typeof(string), Alias = "args")] | |||
public string? Arguments { get; set; } | |||
[YamlMember(typeof(bool), Alias = "baseDir")] | |||
public bool BaseDir { get; set; } = false; | |||
[YamlMember(typeof(bool), Alias = "exeDir")] | |||
public bool ExeDir { get; set; } = false; | |||
[YamlMember(typeof(bool), Alias = "createWindow")] | |||
public bool CreateWindow { get; set; } = false; | |||
[YamlMember(typeof(bool), Alias = "showOutput")] | |||
public bool ShowOutput { get; set; } = true; | |||
[YamlMember(typeof(bool), Alias = "showError")] | |||
public bool ShowError { get; set; } = true; | |||
[YamlMember(typeof(int), Alias = "timeout")] | |||
public int? Timeout { get; set; } | |||
[YamlMember(typeof(string), Alias = "wait")] | |||
public bool Wait { get; set; } = true; | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 5; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } = false; | |||
public void ResetProgress() => InProgress = false; | |||
private bool HasExited { get; set; } = false; | |||
//public int ExitCode { get; set; } | |||
public string? Output { get; private set; } | |||
private string? StandardError { get; set; } | |||
public string ErrorString() => String.IsNullOrEmpty(Arguments) ? $"RunAction failed to execute '{Exe}'." : $"RunAction failed to execute '{Exe}' with arguments '{Arguments}'."; | |||
public static bool ExistsInPath(string fileName) | |||
{ | |||
if (File.Exists(fileName)) | |||
return true; | |||
var values = Environment.GetEnvironmentVariable("PATH"); | |||
foreach (var path in values.Split(Path.PathSeparator)) | |||
{ | |||
var fullPath = Path.Combine(path, fileName); | |||
if (File.Exists(fullPath)) | |||
return true; | |||
if (!fileName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) && File.Exists(fullPath + ".exe")) | |||
return true; | |||
} | |||
return false; | |||
} | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) | |||
{ | |||
return UninstallTaskStatus.InProgress; | |||
} | |||
return HasExited || !Wait ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
public async Task<bool> RunTask() | |||
{ | |||
if (RawPath != null) RawPath = Environment.ExpandEnvironmentVariables(RawPath); | |||
InProgress = true; | |||
if (Arguments == null) Console.WriteLine($"Running '{Exe}'..."); | |||
else Console.WriteLine($"Running '{Exe}' with arguments '{Arguments}'..."); | |||
var currentDir = Directory.GetCurrentDirectory(); | |||
if (ExeDir) RawPath = AmeliorationUtil.Playbook.Path + "\\Executables"; | |||
if (BaseDir) RawPath = currentDir; | |||
string file = null; | |||
if (RawPath != null && File.Exists(Path.Combine(RawPath, Exe))) | |||
file = Path.Combine(RawPath, Exe); | |||
else if (ExistsInPath(Exe) || File.Exists(Environment.ExpandEnvironmentVariables(Exe))) | |||
file = Exe; | |||
if (file == null) | |||
throw new FileNotFoundException($"Executable not found."); | |||
var startInfo = new ProcessStartInfo | |||
{ | |||
CreateNoWindow = !this.CreateWindow, | |||
UseShellExecute = false, | |||
WindowStyle = ProcessWindowStyle.Normal, | |||
RedirectStandardError = true, | |||
RedirectStandardOutput = true, | |||
FileName = file, | |||
}; | |||
if (Arguments != null) startInfo.Arguments = Environment.ExpandEnvironmentVariables(Arguments); | |||
if (ExeDir) startInfo.WorkingDirectory = AmeliorationUtil.Playbook.Path + "\\Executables"; | |||
if (!Wait) | |||
{ | |||
startInfo.RedirectStandardError = false; | |||
startInfo.RedirectStandardOutput = false; | |||
startInfo.WindowStyle = ProcessWindowStyle.Hidden; | |||
startInfo.UseShellExecute = true; | |||
} | |||
if (!ShowOutput) | |||
startInfo.RedirectStandardOutput = false; | |||
if (!ShowError) | |||
startInfo.RedirectStandardError = false; | |||
var exeProcess = new Process | |||
{ | |||
StartInfo = startInfo, | |||
EnableRaisingEvents = true | |||
}; | |||
exeProcess.Start(); | |||
if (!Wait) | |||
{ | |||
exeProcess.Dispose(); | |||
return true; | |||
} | |||
if (ShowOutput) | |||
exeProcess.OutputDataReceived += ProcOutputHandler; | |||
if (ShowError) | |||
exeProcess.ErrorDataReceived += ProcOutputHandler; | |||
if (ShowOutput) | |||
exeProcess.BeginOutputReadLine(); | |||
if (ShowError) | |||
exeProcess.BeginErrorReadLine(); | |||
if (Timeout.HasValue) | |||
{ | |||
var exited = exeProcess.WaitForExit(Timeout.Value); | |||
if (!exited) | |||
{ | |||
exeProcess.Kill(); | |||
throw new TimeoutException($"Executable run timeout exceeded."); | |||
} | |||
} | |||
else | |||
{ | |||
bool exited = exeProcess.WaitForExit(30000); | |||
// WaitForExit alone seems to not be entirely reliable | |||
while (!exited && ExeRunning(exeProcess)) | |||
{ | |||
exited = exeProcess.WaitForExit(30000); | |||
} | |||
} | |||
if (exeProcess.ExitCode != 0) | |||
{ | |||
ErrorLogger.WriteToErrorLog("Process exited with a non-zero exit code: " + exeProcess.ExitCode, null, "RunAction Error", Exe + " " + Arguments); | |||
} | |||
HasExited = true; | |||
if (ShowOutput) | |||
exeProcess.CancelOutputRead(); | |||
if (ShowError) | |||
exeProcess.CancelErrorRead(); | |||
InProgress = false; | |||
return true; | |||
} | |||
private static bool ExeRunning(Process process) | |||
{ | |||
try | |||
{ | |||
return Process.GetProcessesByName(process.ProcessName).Any(x => x.Id == process.Id); | |||
} | |||
catch (Exception) | |||
{ | |||
return false; | |||
} | |||
} | |||
private void ProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) | |||
{ | |||
// Collect the sort command output. | |||
if (!String.IsNullOrEmpty(outLine.Data)) | |||
{ | |||
var outputString = outLine.Data; | |||
if (outputString.Contains("\\AME")) | |||
{ | |||
outputString = outputString.Substring(outputString.IndexOf('>') + 1); | |||
} | |||
Console.WriteLine(outputString); | |||
Output += outputString + Environment.NewLine; | |||
} | |||
else | |||
{ | |||
Console.WriteLine(); | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,153 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.DirectoryServices.ActiveDirectory; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Microsoft.Win32.TaskScheduler; | |||
using TrustedUninstaller.Shared.Exceptions; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
internal enum ScheduledTaskOperation | |||
{ | |||
Delete = 0, | |||
Enable = 1, | |||
Disable = 2, | |||
DeleteFolder = 3 | |||
} | |||
internal class ScheduledTaskAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(ScheduledTaskOperation), Alias = "operation")] | |||
public ScheduledTaskOperation Operation { get; set; } = ScheduledTaskOperation.Delete; | |||
[YamlMember(Alias = "data")] | |||
public string? RawTask { get; set; } = null; | |||
[YamlMember(Alias = "path")] | |||
public string Path { get; set; } | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 1; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } = false; | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"ScheduledTaskAction failed to change task {Path} to state {Operation.ToString()}"; | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) | |||
{ | |||
return UninstallTaskStatus.InProgress; | |||
} | |||
using TaskService ts = new TaskService(); | |||
if (Operation != ScheduledTaskOperation.DeleteFolder) | |||
{ | |||
var task = ts.GetTask(Path); | |||
if (task is null) | |||
{ | |||
return Operation == ScheduledTaskOperation.Delete ? | |||
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
if (task.Enabled) | |||
{ | |||
return Operation == ScheduledTaskOperation.Enable ? | |||
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
return Operation == ScheduledTaskOperation.Disable ? | |||
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
else | |||
{ | |||
var folder = ts.GetFolder(Path); | |||
return folder == null ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
} | |||
public async Task<bool> RunTask() | |||
{ | |||
if (GetStatus() == UninstallTaskStatus.Completed) | |||
{ | |||
return true; | |||
} | |||
if (InProgress) throw new TaskInProgressException("Another ScheduledTask action was called while one was in progress."); | |||
Console.WriteLine($"{Operation.ToString().TrimEnd('e')}ing scheduled task '{Path}'..."); | |||
using TaskService ts = new TaskService(); | |||
InProgress = true; | |||
if (Operation != ScheduledTaskOperation.DeleteFolder) | |||
{ | |||
var task = ts.GetTask(Path); | |||
if (task is null) | |||
{ | |||
if (Operation == ScheduledTaskOperation.Delete) | |||
{ | |||
return true; | |||
} | |||
if (RawTask is null || RawTask.Length == 0) | |||
{ | |||
return false; | |||
} | |||
} | |||
switch (Operation) | |||
{ | |||
case ScheduledTaskOperation.Delete: | |||
// TODO: This will probably not work if we actually use sub-folders | |||
ts.RootFolder.DeleteTask(Path); | |||
break; | |||
case ScheduledTaskOperation.Enable: | |||
case ScheduledTaskOperation.Disable: | |||
{ | |||
if (task is null && !(RawTask is null)) | |||
{ | |||
task = ts.RootFolder.RegisterTask(Path, RawTask); | |||
} | |||
if (!(task is null)) | |||
{ | |||
task.Enabled = Operation == ScheduledTaskOperation.Enable; | |||
} | |||
else | |||
{ | |||
throw new ArgumentException($"Task provided is null."); | |||
} | |||
break; | |||
} | |||
default: | |||
throw new ArgumentException($"Argument out of range."); | |||
} | |||
InProgress = false; | |||
return true; | |||
} | |||
else | |||
{ | |||
var folder = ts.GetFolder(Path); | |||
if (folder is null) return true; | |||
folder.GetTasks().ToList().ForEach(x => folder.DeleteTask(x.Name)); | |||
folder.Parent.DeleteFolder(folder.Name); | |||
InProgress = false; | |||
return true; | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,317 @@ | |||
#nullable enable | |||
using System; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Management; | |||
using System.ServiceProcess; | |||
using System.Text.RegularExpressions; | |||
using System.Threading.Tasks; | |||
using Microsoft.Win32; | |||
using TrustedUninstaller.Shared.Exceptions; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
internal enum ServiceOperation | |||
{ | |||
Stop, | |||
Continue, | |||
Start, | |||
Pause, | |||
Delete, | |||
Change | |||
} | |||
internal class ServiceAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(ServiceOperation), Alias = "operation")] | |||
public ServiceOperation Operation { get; set; } = ServiceOperation.Delete; | |||
[YamlMember(typeof(string), Alias = "name")] | |||
public string ServiceName { get; set; } = null!; | |||
[YamlMember(typeof(int), Alias = "startup")] | |||
public int? Startup { get; set; } | |||
[YamlMember(typeof(bool), Alias = "deleteStop")] | |||
public bool DeleteStop { get; set; } = true; | |||
[YamlMember(typeof(bool), Alias = "deleteUsingRegistry")] | |||
public bool RegistryDelete { get; set; } = false; | |||
[YamlMember(typeof(string), Alias = "device")] | |||
public bool Device { get; set; } = false; | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 4; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"ServiceAction failed to {Operation.ToString().ToLower()} service {ServiceName}."; | |||
private ServiceController? GetService() | |||
{ | |||
if (ServiceName.EndsWith("*") && ServiceName.StartsWith("*")) return ServiceController.GetServices() | |||
.FirstOrDefault(service => service.ServiceName.IndexOf(ServiceName.Trim('*'), StringComparison.CurrentCultureIgnoreCase) >= 0); | |||
if (ServiceName.EndsWith("*")) return ServiceController.GetServices() | |||
.FirstOrDefault(service => service.ServiceName.StartsWith(ServiceName.TrimEnd('*'), StringComparison.CurrentCultureIgnoreCase)); | |||
if (ServiceName.StartsWith("*")) return ServiceController.GetServices() | |||
.FirstOrDefault(service => service.ServiceName.EndsWith(ServiceName.TrimStart('*'), StringComparison.CurrentCultureIgnoreCase)); | |||
return ServiceController.GetServices() | |||
.FirstOrDefault(service => service.ServiceName.Equals(ServiceName, StringComparison.CurrentCultureIgnoreCase)); | |||
} | |||
private ServiceController? GetDevice() | |||
{ | |||
if (ServiceName.EndsWith("*") && ServiceName.StartsWith("*")) return ServiceController.GetDevices() | |||
.FirstOrDefault(service => service.ServiceName.IndexOf(ServiceName.Trim('*'), StringComparison.CurrentCultureIgnoreCase) >= 0); | |||
if (ServiceName.EndsWith("*")) return ServiceController.GetDevices() | |||
.FirstOrDefault(service => service.ServiceName.StartsWith(ServiceName.TrimEnd('*'), StringComparison.CurrentCultureIgnoreCase)); | |||
if (ServiceName.StartsWith("*")) return ServiceController.GetDevices() | |||
.FirstOrDefault(service => service.ServiceName.EndsWith(ServiceName.TrimStart('*'), StringComparison.CurrentCultureIgnoreCase)); | |||
return ServiceController.GetDevices() | |||
.FirstOrDefault(service => service.ServiceName.Equals(ServiceName, StringComparison.CurrentCultureIgnoreCase)); | |||
} | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) return UninstallTaskStatus.InProgress; | |||
if (Operation == ServiceOperation.Change && Startup.HasValue) | |||
{ | |||
// TODO: Implement dev log. Example: | |||
// if (Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Services\{ServiceName}") == null) WriteToDevLog($"Warning: Service name '{ServiceName}' not found in registry."); | |||
var root = Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Services\{ServiceName}"); | |||
if (root == null) return UninstallTaskStatus.Completed; | |||
var value = root.GetValue("Start"); | |||
return (int)value == Startup.Value ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
ServiceController serviceController; | |||
if (Device) serviceController = GetDevice(); | |||
else serviceController = GetService(); | |||
if (Operation == ServiceOperation.Delete && RegistryDelete) | |||
{ | |||
// TODO: Implement dev log. Example: | |||
// if (Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Services\{ServiceName}") == null) WriteToDevLog($"Warning: Service name '{ServiceName}' not found in registry."); | |||
var root = Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Services\{ServiceName}"); | |||
return root == null ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
return Operation switch | |||
{ | |||
ServiceOperation.Stop => | |||
serviceController?.Status == ServiceControllerStatus.Stopped | |||
|| serviceController?.Status == ServiceControllerStatus.StopPending ? | |||
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo, | |||
ServiceOperation.Continue => | |||
serviceController?.Status == ServiceControllerStatus.Running | |||
|| serviceController?.Status == ServiceControllerStatus.ContinuePending ? | |||
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo, | |||
ServiceOperation.Start => | |||
serviceController?.Status == ServiceControllerStatus.StartPending | |||
|| serviceController?.Status == ServiceControllerStatus.Running ? | |||
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo, | |||
ServiceOperation.Pause => | |||
serviceController?.Status == ServiceControllerStatus.Paused | |||
|| serviceController?.Status == ServiceControllerStatus.PausePending ? | |||
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo, | |||
ServiceOperation.Delete => | |||
serviceController == null ? | |||
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo, | |||
_ => throw new ArgumentOutOfRangeException("Argument out of Range", new ArgumentOutOfRangeException()) | |||
}; | |||
} | |||
private readonly string[] RegexNoKill = { "DcomLaunch" }; | |||
public async Task<bool> RunTask() | |||
{ | |||
if (InProgress) throw new TaskInProgressException("Another Service action was called while one was in progress."); | |||
if (Operation == ServiceOperation.Change && !Startup.HasValue) throw new ArgumentException("Startup property must be specified with the change operation."); | |||
if (Operation == ServiceOperation.Change && (Startup.Value > 4 || Startup.Value < 0)) throw new ArgumentException("Startup property must be between 1 and 4."); | |||
// This is a little cursed but it works and is concise lol | |||
Console.WriteLine($"{Operation.ToString().Replace("Stop", "Stopp").TrimEnd('e')}ing services matching '{ServiceName}'..."); | |||
if (Operation == ServiceOperation.Change) | |||
{ | |||
var action = new RegistryValueAction() | |||
{ | |||
KeyName = $@"HKLM\SYSTEM\CurrentControlSet\Services\{ServiceName}", | |||
Value = "Start", | |||
Data = Startup.Value, | |||
Type = RegistryValueType.REG_DWORD, | |||
Operation = RegistryValueOperation.Set | |||
}; | |||
await action.RunTask(); | |||
InProgress = false; | |||
return true; | |||
} | |||
ServiceController? service; | |||
if (Device) service = GetDevice(); | |||
else service = GetService(); | |||
if (service == null) | |||
{ | |||
Console.WriteLine($"No services found matching '{ServiceName}'."); | |||
//ErrorLogger.WriteToErrorLog($"The service matching '{ServiceName}' does not exist.", Environment.StackTrace, "ServiceAction Error"); | |||
return false; | |||
} | |||
InProgress = true; | |||
var cmdAction = new CmdAction(); | |||
if (Operation == ServiceOperation.Delete || Operation == ServiceOperation.Stop) | |||
{ | |||
if (RegexNoKill.Any(regex => Regex.Match(ServiceName, regex, RegexOptions.IgnoreCase).Success)) | |||
{ | |||
Console.WriteLine($"Skipping {ServiceName}..."); | |||
return false; | |||
} | |||
foreach (ServiceController dependentService in service.DependentServices) | |||
{ | |||
Console.WriteLine($"Killing dependent service {dependentService.ServiceName}..."); | |||
cmdAction.Command = Environment.Is64BitOperatingSystem ? | |||
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {dependentService.ServiceName} -caction stop" : | |||
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {dependentService.ServiceName} -caction stop"; | |||
await cmdAction.RunTask(); | |||
Console.WriteLine("Waiting for the service to stop..."); | |||
int delay = 100; | |||
while (service.Status != ServiceControllerStatus.Stopped && delay <= 1000) | |||
{ | |||
service.Refresh(); | |||
//Wait for the service to stop | |||
Task.Delay(delay).Wait(); | |||
delay += 100; | |||
} | |||
if (delay >= 1000) | |||
{ | |||
Console.WriteLine("\r\nService stop timeout exceeded. Trying second method..."); | |||
try | |||
{ | |||
using var search = new ManagementObjectSearcher($"SELECT * FROM Win32_Service WHERE Name='{service.ServiceName}'"); | |||
foreach (ManagementObject queryObj in search.Get()) | |||
{ | |||
var serviceId = (UInt32)queryObj["ProcessId"]; // Access service name | |||
var killServ = new TaskKillAction() | |||
{ | |||
ProcessID = (int)serviceId | |||
}; | |||
await killServ.RunTask(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog($"Could not kill dependent service {dependentService.ServiceName}.", | |||
e.StackTrace, "ServiceAction Error"); | |||
} | |||
} | |||
} | |||
if (service.ServiceName == "SgrmAgent" && ((Operation == ServiceOperation.Delete && DeleteStop) || Operation == ServiceOperation.Stop)) | |||
{ | |||
await new TaskKillAction() { ProcessName = "SgrmBroker" }.RunTask(); | |||
} | |||
} | |||
if (Operation == ServiceOperation.Delete) | |||
{ | |||
if (DeleteStop && service.Status != ServiceControllerStatus.StopPending && service.Status != ServiceControllerStatus.Stopped) | |||
{ | |||
cmdAction.Command = Environment.Is64BitOperatingSystem ? | |||
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction stop" : | |||
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction stop"; | |||
await cmdAction.RunTask(); | |||
} | |||
Console.WriteLine("Waiting for the service to stop..."); | |||
int delay = 100; | |||
while (DeleteStop && service.Status != ServiceControllerStatus.Stopped && delay <= 1500) | |||
{ | |||
service.Refresh(); | |||
//Wait for the service to stop | |||
await Task.Delay(delay); | |||
delay += 100; | |||
} | |||
if (delay >= 1500) | |||
{ | |||
Console.WriteLine("\r\nService stop timeout exceeded. Trying second method..."); | |||
try | |||
{ | |||
using var search = new ManagementObjectSearcher($"SELECT * FROM Win32_Service WHERE Name='{service.ServiceName}'"); | |||
foreach (ManagementObject queryObj in search.Get()) | |||
{ | |||
var serviceId = (UInt32)queryObj["ProcessId"]; // Access service name | |||
var killServ = new TaskKillAction() | |||
{ | |||
ProcessID = (int)serviceId | |||
}; | |||
await killServ.RunTask(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog($"Could not kill service {service.ServiceName}.", | |||
e.StackTrace, "ServiceAction Error"); | |||
} | |||
} | |||
if (RegistryDelete) | |||
{ | |||
var action = new RegistryKeyAction() | |||
{ | |||
KeyName = $@"HKLM\SYSTEM\CurrentControlSet\Services\{ServiceName}", | |||
Operation = RegistryKeyOperation.Delete | |||
}; | |||
await action.RunTask(); | |||
} | |||
else | |||
{ | |||
cmdAction.Command = Environment.Is64BitOperatingSystem ? | |||
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction delete" : | |||
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {service.ServiceName} -caction delete"; | |||
await cmdAction.RunTask(); | |||
} | |||
} | |||
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()}"; | |||
await cmdAction.RunTask(); | |||
} | |||
service?.Dispose(); | |||
await Task.Delay(100); | |||
InProgress = false; | |||
return true; | |||
} | |||
} | |||
} |
@ -0,0 +1,63 @@ | |||
using System; | |||
using System.IO; | |||
using System.Threading.Tasks; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
using IWshRuntimeLibrary; | |||
using File = System.IO.File; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
class ShortcutAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(string), Alias = "path")] | |||
public string RawPath { get; set; } | |||
[YamlMember(typeof(string), Alias = "name")] | |||
public string Name { get; set; } | |||
[YamlMember(typeof(string), Alias = "destination")] | |||
public string Destination { get; set; } | |||
[YamlMember(typeof(string), Alias = "description")] | |||
public string Description { get; set; } | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 1; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"ShortcutAction failed to create shortcut to '{Destination}' from '{RawPath}' with name {Name}."; | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
//If the shortcut already exists return Completed | |||
return File.Exists(Path.Combine(this.Destination, this.Name + ".lnk")) ? | |||
UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
public async Task<bool> RunTask() | |||
{ | |||
RawPath = Environment.ExpandEnvironmentVariables(RawPath); | |||
Console.WriteLine($"Creating shortcut from '{Destination}' to '{RawPath}'..."); | |||
if (File.Exists(this.RawPath)) | |||
{ | |||
WshShell shell = new WshShell(); | |||
var sc = (IWshShortcut)shell.CreateShortcut(Path.Combine(this.Destination, this.Name + ".lnk")); | |||
sc.Description = this.Description; | |||
sc.TargetPath = this.RawPath; | |||
sc.Save(); | |||
} | |||
else | |||
{ | |||
throw new FileNotFoundException($"File '{RawPath}' not found."); | |||
} | |||
return true; | |||
} | |||
} | |||
} |
@ -0,0 +1,150 @@ | |||
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; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Text; | |||
using System.Threading; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
// Integrate ame-assassin later | |||
internal class SystemPackageAction : ITaskAction | |||
{ | |||
public enum Architecture | |||
{ | |||
amd64 = 0, | |||
wow64 = 1, | |||
msil = 2, | |||
x86 = 3, | |||
All = 4 | |||
} | |||
[YamlMember(typeof(string), Alias = "name")] | |||
public string Name { get; set; } | |||
[YamlMember(typeof(string), Alias = "arch")] | |||
public Architecture Arch { get; set; } = Architecture.All; | |||
[YamlMember(typeof(string), Alias = "language")] | |||
public string Language { get; set; } = "*"; | |||
[YamlMember(typeof(string), Alias = "regexExcludeFiles")] | |||
public string[]? RegexExcludeList { get; set; } | |||
[YamlMember(typeof(string), Alias = "excludeDependents")] | |||
public string[]? ExcludeDependentsList { get; set; } | |||
[YamlMember(typeof(string[]), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 15; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"SystemPackageAction failed to remove '{Name}'."; | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) return UninstallTaskStatus.InProgress; | |||
return HasFinished ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
private bool HasFinished = false; | |||
public async Task<bool> RunTask() | |||
{ | |||
if (InProgress) throw new TaskInProgressException("Another Appx action was called while one was in progress."); | |||
InProgress = true; | |||
Console.WriteLine($"Removing system package '{Name}'..."); | |||
var excludeArgs = new StringBuilder(""); | |||
if (RegexExcludeList != null) | |||
{ | |||
foreach (var regex in RegexExcludeList) | |||
{ | |||
excludeArgs.Append(@$" -xf ""{regex}"""); | |||
} | |||
} | |||
var excludeDependsArgs = new StringBuilder(""); | |||
if (ExcludeDependentsList != null) | |||
{ | |||
foreach (var dependent in ExcludeDependentsList) | |||
{ | |||
excludeDependsArgs.Append(@$" -xdependent ""{dependent}"""); | |||
} | |||
} | |||
var psi = new ProcessStartInfo() | |||
{ | |||
UseShellExecute = false, | |||
CreateNoWindow = true, | |||
Arguments = $@"-SystemPackage ""{Name}"" -Arch {Arch.ToString()} -Language ""{Language}""" + excludeArgs + excludeDependsArgs, | |||
FileName = Directory.GetCurrentDirectory() + "\\ame-assassin\\ame-assassin.exe", | |||
RedirectStandardOutput = true, | |||
RedirectStandardError = true | |||
}; | |||
var proc = Process.Start(psi); | |||
proc.OutputDataReceived += ProcOutputHandler; | |||
proc.ErrorDataReceived += ProcOutputHandler; | |||
proc.BeginOutputReadLine(); | |||
proc.BeginErrorReadLine(); | |||
bool exited = proc.WaitForExit(30000); | |||
// WaitForExit alone seems to not be entirely reliable | |||
while (!exited && ExeRunning(proc)) | |||
{ | |||
exited = proc.WaitForExit(30000); | |||
} | |||
using (var log = new StreamWriter("Logs\\Packages.txt", true)) | |||
log.Write(output.ToString()); | |||
HasFinished = true; | |||
InProgress = false; | |||
return true; | |||
} | |||
private StringBuilder output = new StringBuilder(""); | |||
private bool PleaseWait = false; | |||
private void ProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) | |||
{ | |||
var write = outLine == null ? "" : outLine.Data; | |||
output.Append(write + Environment.NewLine); | |||
if (String.IsNullOrEmpty(write)) return; | |||
if (write.StartsWith("--- Removing")) | |||
{ | |||
Console.WriteLine(write.Substring(4, write.Length - 4)); | |||
PleaseWait = true; | |||
} | |||
if (write.StartsWith("Waiting for the service to stop...") && PleaseWait) | |||
{ | |||
PleaseWait = false; | |||
Console.WriteLine("This may take some time..."); | |||
} | |||
} | |||
private static bool ExeRunning(Process process) | |||
{ | |||
try | |||
{ | |||
return Process.GetProcessesByName(process.ProcessName).Any(x => x.Id == process.Id); | |||
} | |||
catch (Exception) | |||
{ | |||
return false; | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,314 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.Linq; | |||
using System.Management; | |||
using System.Runtime.InteropServices; | |||
using System.Text.RegularExpressions; | |||
using System.Threading.Tasks; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
class TaskKillAction : ITaskAction | |||
{ | |||
[DllImport("kernel32.dll", SetLastError=true)] | |||
[return: MarshalAs(UnmanagedType.Bool)] | |||
static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode); | |||
[YamlMember(typeof(string), Alias = "name")] | |||
public string? ProcessName { get; set; } | |||
[YamlMember(typeof(string), Alias = "pathContains")] | |||
public string? PathContains { get; set; } | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 2; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public int? ProcessID { get; set; } | |||
public string ErrorString() | |||
{ | |||
string text = $"TaskKillAction failed to kill processes matching '{ProcessName}'."; | |||
try | |||
{ | |||
var processes = GetProcess().Select(process => process.ProcessName).Distinct().ToList(); | |||
if (processes.Count > 1) | |||
{ | |||
text = $"TaskKillAction failed to kill processes:"; | |||
foreach (var process in processes) | |||
{ | |||
text += "|NEWLINE|" + process; | |||
} | |||
} | |||
else if (processes.Count == 1) text = $"TaskKillAction failed to kill process {processes[0]}."; | |||
} catch (Exception) { } | |||
return text; | |||
} | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) | |||
{ | |||
return UninstallTaskStatus.InProgress; | |||
} | |||
List<Process> processToTerminate = new List<Process>(); | |||
if (ProcessID.HasValue) | |||
{ | |||
try { processToTerminate.Add(Process.GetProcessById((int)ProcessID)); } catch (Exception) { } | |||
} | |||
else | |||
{ | |||
processToTerminate = GetProcess().ToList(); | |||
} | |||
return processToTerminate.Any() ? UninstallTaskStatus.ToDo : UninstallTaskStatus.Completed; | |||
} | |||
private IEnumerable<Process> GetProcess() | |||
{ | |||
if (ProcessName == null) return new List<Process>(); | |||
if (ProcessName.EndsWith("*") && ProcessName.StartsWith("*")) return Process.GetProcesses() | |||
.Where(process => process.ProcessName.IndexOf(ProcessName.Trim('*'), StringComparison.CurrentCultureIgnoreCase) >= 0); | |||
if (ProcessName.EndsWith("*")) return Process.GetProcesses() | |||
.Where(process => process.ProcessName.StartsWith(ProcessName.TrimEnd('*'), StringComparison.CurrentCultureIgnoreCase)); | |||
if (ProcessName.StartsWith("*")) return Process.GetProcesses() | |||
.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); | |||
private readonly string[] RegexNoKill = { "lsass", "csrss", "winlogon", "TrustedUninstaller\\.CLI", "dwm", "conhost", "ame.?wizard", "ame.?assassin" }; | |||
// These processes give access denied errors when getting their handle for IsProcessCritical. | |||
// TODO: Investigate how to properly acquire permissions. | |||
private readonly string[] RegexNotCritical = { "SecurityHealthService", "wscsvc", "MsMpEng", "SgrmBroker" }; | |||
public async Task<bool> RunTask() | |||
{ | |||
InProgress = true; | |||
if (string.IsNullOrEmpty(ProcessName) && ProcessID.HasValue) | |||
{ | |||
Console.WriteLine($"Killing process with PID '{ProcessID.Value}'..."); | |||
} | |||
else | |||
{ | |||
if (ProcessName != null && RegexNoKill.Any(regex => Regex.Match(ProcessName, regex, RegexOptions.IgnoreCase).Success)) | |||
{ | |||
Console.WriteLine($"Skipping {ProcessName}..."); | |||
return false; | |||
} | |||
Console.WriteLine($"Killing processes matching '{ProcessName}'..."); | |||
} | |||
var cmdAction = new CmdAction(); | |||
if (ProcessName != null) | |||
{ | |||
//If the service is svchost, we stop the service instead of killing it. | |||
if (ProcessName.Contains("svchost")) | |||
{ | |||
// bool serviceFound = false; | |||
try | |||
{ | |||
using var search = new ManagementObjectSearcher($"select * from Win32_Service where ProcessId = '{ProcessID}'"); | |||
foreach (ManagementObject queryObj in search.Get()) | |||
{ | |||
var serviceName = (string)queryObj["Name"]; // Access service name | |||
var stopServ = new ServiceAction() | |||
{ | |||
ServiceName = serviceName, | |||
Operation = ServiceOperation.Stop | |||
}; | |||
await stopServ.RunTask(); | |||
} | |||
} | |||
catch (NullReferenceException e) | |||
{ | |||
Console.WriteLine($"A service with PID: {ProcessID} could not be found."); | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, $"Could not find service with PID {ProcessID}."); | |||
} | |||
/* foreach (var serv in servicesToDelete) | |||
{ | |||
//The ID can only be associated with one of the services, there's no need to loop through | |||
//them all if we already found the service. | |||
if (serviceFound) | |||
{ | |||
break; | |||
} | |||
try | |||
{ | |||
using var search = new ManagementObjectSearcher($"select ProcessId from Win32_Service where Name = '{serv}'").Get(); | |||
var servID = (uint)search.OfType<ManagementObject>().FirstOrDefault()["ProcessID"]; | |||
if (servID == ProcessID) | |||
{ | |||
serviceFound = true; | |||
} | |||
search.Dispose(); | |||
} | |||
catch (Exception e) | |||
{ | |||
var search = new ManagementObjectSearcher($"select Name from Win32_Service where ProcessID = '{ProcessID}'").Get(); | |||
var servName = search.OfType<ManagementObject>().FirstOrDefault()["Name"]; | |||
Console.WriteLine($"Could not find {servName} but PID {ProcessID} still exists."); | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, $"Exception Type: {e.GetType()}"); | |||
return false; | |||
} | |||
}*/ | |||
//None of the services listed, we shouldn't kill svchost. | |||
/* if (!serviceFound) | |||
{ | |||
var search = new ManagementObjectSearcher($"select Name from Win32_Service where ProcessID = '{ProcessID}'").Get(); | |||
var servName = search.OfType<ManagementObject>().FirstOrDefault()["Name"]; | |||
Console.WriteLine($"A critical system process \"{servName}\" with PID {ProcessID} caused the Wizard to fail."); | |||
await WinUtil.UninstallDriver(); | |||
Environment.Exit(-1); | |||
return false; | |||
}*/ | |||
await Task.Delay(100); | |||
InProgress = false; | |||
return true; | |||
} | |||
if (PathContains != null && !ProcessID.HasValue) | |||
{ | |||
var processes = GetProcess().ToList(); | |||
if (processes.Count > 0) Console.WriteLine("Processes:"); | |||
foreach (var process in processes.Where(x => x.MainModule.FileName.Contains(PathContains))) | |||
{ | |||
Console.WriteLine(process.ProcessName + " - " + process.Id); | |||
if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success)) { | |||
bool isCritical = false; | |||
IsProcessCritical(process.Handle, ref isCritical); | |||
if (isCritical) | |||
{ | |||
Console.WriteLine($"{process.ProcessName} is a critical process, skipping..."); | |||
continue; | |||
} | |||
} | |||
cmdAction.Command = Environment.Is64BitOperatingSystem ? | |||
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.Id} -caction terminate" : | |||
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.Id} -caction terminate"; | |||
await cmdAction.RunTask(); | |||
int i = 0; | |||
while (i <= 15 && GetProcess().Any(x => x.Id == process.Id && x.ProcessName == process.ProcessName)) | |||
{ | |||
await Task.Delay(300); | |||
i++; | |||
} | |||
if (i >= 15) ErrorLogger.WriteToErrorLog($"Task kill timeout exceeded.", Environment.StackTrace, "TaskKillAction Error"); | |||
} | |||
InProgress = false; | |||
return true; | |||
} | |||
} | |||
if (ProcessID.HasValue) | |||
{ | |||
if (ProcessName != null && ProcessName.Equals("explorer", StringComparison.OrdinalIgnoreCase)) | |||
{ | |||
try | |||
{ | |||
var process = Process.GetProcessById(ProcessID.Value); | |||
TerminateProcess(process.Handle, 1); | |||
} catch (Exception) | |||
{ | |||
cmdAction.Command = Environment.Is64BitOperatingSystem ? | |||
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID} -caction terminate" : | |||
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID} -caction terminate"; | |||
await cmdAction.RunTask(); | |||
} | |||
} | |||
else | |||
{ | |||
var process = Process.GetProcessById(ProcessID.Value); | |||
if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success)) | |||
{ | |||
bool isCritical = false; | |||
IsProcessCritical(process.Handle, ref isCritical); | |||
if (isCritical) | |||
{ | |||
Console.WriteLine($"{process.ProcessName} is a critical process, skipping..."); | |||
return false; | |||
} | |||
} | |||
cmdAction.Command = Environment.Is64BitOperatingSystem ? | |||
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID} -caction terminate" : | |||
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {ProcessID} -caction terminate"; | |||
await cmdAction.RunTask(); | |||
} | |||
await Task.Delay(100); | |||
} | |||
else | |||
{ | |||
var processes = GetProcess().ToList(); | |||
if (processes.Count > 0) Console.WriteLine("Processes:"); | |||
foreach (var process in processes) | |||
{ | |||
Console.WriteLine(process.ProcessName + " - " + process.Id); | |||
cmdAction.Command = Environment.Is64BitOperatingSystem ? | |||
$"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.ProcessName}.exe -caction terminate" : | |||
$"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype process -cobject {process.ProcessName}.exe -caction terminate"; | |||
if (process.ProcessName == "explorer") TerminateProcess(process.Handle, 1); | |||
else | |||
{ | |||
if (!RegexNotCritical.Any(x => Regex.Match(process.ProcessName, x, RegexOptions.IgnoreCase).Success)) | |||
{ | |||
bool isCritical = false; | |||
IsProcessCritical(process.Handle, ref isCritical); | |||
if (isCritical) | |||
{ | |||
Console.WriteLine($"{process.ProcessName} is a critical process, skipping..."); | |||
continue; | |||
} | |||
} | |||
await cmdAction.RunTask(); | |||
} | |||
int i = 0; | |||
while (i <= 15 && GetProcess().Any(x => x.Id == process.Id && x.ProcessName == process.ProcessName)) | |||
{ | |||
await Task.Delay(300); | |||
i++; | |||
} | |||
if (i >= 15) ErrorLogger.WriteToErrorLog($"Task kill timeout exceeded.", Environment.StackTrace, "TaskKillAction Error"); | |||
} | |||
} | |||
InProgress = false; | |||
return true; | |||
} | |||
} | |||
} |
@ -0,0 +1,61 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
internal class UpdateAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(string), Alias = "name")] | |||
public string PackageName { get; set; } | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 1; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"UpdateAction failed to remove update package {PackageName}."; | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) | |||
{ | |||
return UninstallTaskStatus.InProgress; | |||
} | |||
return UninstallTaskStatus.Completed; | |||
} | |||
public async Task<bool> RunTask() | |||
{ | |||
if (InProgress) | |||
{ | |||
Console.WriteLine("An update action is already in progress..."); | |||
return false; | |||
} | |||
InProgress = true; | |||
Console.WriteLine($"Removing update package '{PackageName}'..."); | |||
CmdAction removeUpdate = new CmdAction() | |||
{ | |||
Command = @$"DISM.exe /Online /Remove-Package /PackageName:{PackageName} /quiet /norestart" | |||
}; | |||
while(removeUpdate.GetStatus() != UninstallTaskStatus.Completed) | |||
{ | |||
await removeUpdate.RunTask(); | |||
} | |||
InProgress = false; | |||
return true; | |||
} | |||
} | |||
} |
@ -0,0 +1,101 @@ | |||
using System; | |||
using System.DirectoryServices; | |||
using System.Threading.Tasks; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
using System.DirectoryServices.AccountManagement; | |||
using System.Security.Principal; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
public class UserAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(string), Alias = "name")] | |||
public string Username { get; set; } = ""; | |||
[YamlMember(typeof(bool), Alias = "admin")] | |||
public bool IsAdmin { get; set; } = false; | |||
[YamlMember(typeof(string), Alias = "weight")] | |||
public int ProgressWeight { get; set; } = 1; | |||
public int GetProgressWeight() => ProgressWeight; | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => $"UserAction failed to change permissions for user {Username}."; | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
using var pc = new PrincipalContext(ContextType.Machine); | |||
var up = UserPrincipal.FindByIdentity( | |||
pc, | |||
IdentityType.SamAccountName, | |||
this.Username); | |||
var userExists = (up != null); | |||
if (!IsAdmin || !userExists) return userExists ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
var identity = new WindowsIdentity(up.UserPrincipalName); | |||
var principal = new WindowsPrincipal(identity); | |||
var isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator); | |||
return isAdmin ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
public async Task<bool> RunTask() | |||
{ | |||
if (this.GetStatus() != UninstallTaskStatus.ToDo) | |||
{ | |||
return false; | |||
} | |||
Console.WriteLine($"Changing permissions for user '{Username}'..."); | |||
return await Task.Run(() => | |||
{ | |||
using var pc = new PrincipalContext(ContextType.Machine); | |||
var up = UserPrincipal.FindByIdentity( | |||
pc, | |||
IdentityType.SamAccountName, | |||
this.Username); | |||
var userExists = (up != null); | |||
var ad = new DirectoryEntry("WinNT://" + | |||
Environment.MachineName + ",computer"); | |||
if (!userExists) | |||
{ | |||
var newUser = ad.Children.Add(this.Username, "user"); | |||
newUser.Invoke("SetPassword", "user"); | |||
newUser.Invoke("Put", "Description", "Created by the AME Wizard"); | |||
newUser.CommitChanges(); | |||
if (IsAdmin) | |||
{ | |||
var group = ad.Children.Find("Administrators", "group"); | |||
group.Invoke("Add", newUser.Path); | |||
group.CommitChanges(); | |||
} | |||
} | |||
else | |||
{ | |||
if (IsAdmin) | |||
{ | |||
var group = ad.Children.Find("Administrators", "group"); | |||
group.Invoke("Add", up.UserPrincipalName); | |||
group.CommitChanges(); | |||
} | |||
} | |||
return true; | |||
}); | |||
} | |||
} | |||
} |
@ -0,0 +1,41 @@ | |||
using System; | |||
using System.Threading.Tasks; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Actions | |||
{ | |||
public class WriteStatusAction : ITaskAction | |||
{ | |||
[YamlMember(typeof(string), Alias = "status")] | |||
public string Status { get; set; } | |||
private bool InProgress { get; set; } | |||
public void ResetProgress() => InProgress = false; | |||
public string ErrorString() => ""; | |||
public int GetProgressWeight() => 0; | |||
public UninstallTaskStatus GetStatus() | |||
{ | |||
if (InProgress) | |||
{ | |||
return UninstallTaskStatus.InProgress; | |||
} | |||
return hasCompleted ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; | |||
} | |||
private bool hasCompleted; | |||
public async Task<bool> RunTask() | |||
{ | |||
if (String.IsNullOrEmpty(Status)) | |||
{ | |||
Console.WriteLine(":AME-STATUS: " + "Running actions"); | |||
} | |||
else | |||
{ | |||
Console.WriteLine(":AME-STATUS: " + Status); | |||
} | |||
hasCompleted = true; | |||
return true; | |||
} | |||
} | |||
} |
@ -0,0 +1,589 @@ | |||
using Newtonsoft.Json.Linq; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.IO.MemoryMappedFiles; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Net.Http; | |||
using System.Runtime.InteropServices; | |||
using System.Security.Cryptography; | |||
using System.ServiceProcess; | |||
using System.Text; | |||
using System.Text.RegularExpressions; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using System.Windows.Forms; | |||
using System.Xml; | |||
using System.Xml.Serialization; | |||
using TrustedUninstaller.Shared.Actions; | |||
using TrustedUninstaller.Shared.Parser; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using MessageBox = System.Windows.MessageBox; | |||
namespace TrustedUninstaller.Shared | |||
{ | |||
public static class AmeliorationUtil | |||
{ | |||
private static readonly ConfigParser Parser = new ConfigParser(); | |||
private static readonly HttpClient Client = new HttpClient(); | |||
//TODO: custom.yml path or .apbx path? | |||
public static Playbook Playbook { set; get; } | |||
public static readonly List<string> ErrorDisplayList = new List<string>(); | |||
public static int GetProgressMaximum() | |||
{ | |||
return Parser.Tasks.Sum(task => task.Actions.Sum(action => action.GetProgressWeight())); | |||
} | |||
public static bool AddTasks(string configPath, string file) | |||
{ | |||
try | |||
{ | |||
//This allows for a proper detection of if any error occurred, and if so the CLI will relay an :AME-Fatal Error: | |||
//This is important, as we want the process to stop immediately if a YAML syntax error was detected. | |||
bool hadError = false; | |||
//Adds the config file to the parser's task list | |||
Parser.Add(Path.Combine(configPath, file)); | |||
var currentTask = Parser.Tasks[Parser.Tasks.Count - 1]; | |||
if (File.Exists("TasksAdded.txt")) | |||
{ | |||
var doneTasks = File.ReadAllText("TasksAdded.txt").Split(new[] { "\r\n" }, StringSplitOptions.None); | |||
if (doneTasks.Contains(currentTask.Title)) | |||
{ | |||
Parser.Tasks.Remove(currentTask); | |||
return true; | |||
} | |||
} | |||
//Get the features of the last added task (the task that was just added from the config file) | |||
var features = currentTask.Features; | |||
//Each feature would reference a directory that has a YAML file, we take those directories and then run the | |||
//AddTasks function again, until we reach a file that doesn't reference any other YAML files, and add them | |||
//all to the parser's tasks list. | |||
if (features == null) return true; | |||
foreach (var feature in features) | |||
{ | |||
var subResult = AddTasks(configPath, feature); | |||
// We could return false here, however we want to output ALL detected YAML errors, | |||
// which is why we continue here. | |||
if (!subResult) hadError = true; | |||
} | |||
return hadError ? false : true; | |||
} | |||
catch (Exception e) | |||
{ | |||
Console.WriteLine($"Error adding tasks in {configPath + "\\" + file}:\r\n{e.Message}"); | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, $"Error adding tasks in {configPath + "\\" + file}."); | |||
return false; | |||
} | |||
} | |||
public static async Task<int> DoActions(UninstallTask task, UninstallTaskPrivilege privilege) | |||
{ | |||
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.TrustedInstaller && !WinUtil.IsTrustedInstaller()) | |||
{ | |||
Console.WriteLine("Relaunching as Trusted Installer!"); | |||
var mmf = MemoryMappedFile.CreateNew("ImgA", 5000000); | |||
WinUtil.RelaunchAsTrustedInstaller(); | |||
if (NativeProcess.Process == null) | |||
{ | |||
ErrorLogger.WriteToErrorLog($"Could not launch TrustedInstaller process. Return output was null.", | |||
Environment.StackTrace, "Error while attempting to sync with TrustedInstaller process."); | |||
Console.WriteLine(":AME-Fatal Error: Could not launch TrustedInstaller process."); | |||
Environment.Exit(-1); | |||
} | |||
var delay = 20; | |||
while (!NativeProcess.Process.HasExited) | |||
{ | |||
if (delay > 3500) | |||
{ | |||
NativeProcess.Process.Kill(); | |||
ErrorLogger.WriteToErrorLog($"Could not initialize memory data exchange. Timeframe exceeded.", | |||
Environment.StackTrace, "Error while attempting to sync with TrustedInstaller process."); | |||
Console.WriteLine(":AME-Fatal Error: Could not initialize memory data exchange."); | |||
Environment.Exit(-1); | |||
} | |||
Task.Delay(delay).Wait(); | |||
// Kind of inefficient looping this, however it's likely to cause access errors otherwise | |||
using var stream = mmf.CreateViewStream(); | |||
using BinaryReader binReader = new BinaryReader(stream); | |||
{ | |||
var res = binReader.ReadBytes((int)stream.Length); | |||
var data = Encoding.UTF8.GetString(res); | |||
var end = data.IndexOf('\0'); | |||
if (end == 0) | |||
{ | |||
delay += 200; | |||
} | |||
else | |||
{ | |||
break; | |||
} | |||
} | |||
} | |||
var offset = 0; | |||
var read = false; | |||
using (var stream = mmf.CreateViewStream()) | |||
{ | |||
while (!NativeProcess.Process.HasExited || read) | |||
{ | |||
read = false; | |||
BinaryReader binReader = new BinaryReader(stream); | |||
binReader.BaseStream.Seek(offset, SeekOrigin.Begin); | |||
var res = binReader.ReadBytes((int)stream.Length - offset); | |||
var data = Encoding.UTF8.GetString(res); | |||
var end = data.IndexOf("\0"); | |||
var content = data.Substring(0, end); | |||
offset += Encoding.UTF8.GetBytes(content).Length; | |||
var output = content.Split(new [] {Environment.NewLine}, StringSplitOptions.None); | |||
if (output.Length > 0) output = output.Take(output.Length - 1).ToArray(); | |||
foreach (var line in output) | |||
{ | |||
Console.WriteLine(line); | |||
read = true; | |||
// Introducing ANY delay here makes it lag behind, which isn't ideal | |||
//Task.Delay(5).Wait(); | |||
} | |||
Task.Delay(20).Wait(); | |||
} | |||
} | |||
mmf.Dispose(); | |||
return 0; //Only returns after TI is done | |||
} | |||
//Goes through the list of tasks that are inside the parser class, | |||
//and runs the task using the RunTask method | |||
//Check the Actions folder inside the Shared folder for reference. | |||
foreach (ITaskAction action in task.Actions) | |||
{ | |||
int i = 0; | |||
//var actionType = action.GetType().ToString().Replace("TrustedUninstaller.Shared.Actions.", ""); | |||
do | |||
{ | |||
//Console.WriteLine($"Running {actionType}"); | |||
Console.WriteLine(); | |||
try | |||
{ | |||
await action.RunTask(); | |||
action.ResetProgress(); | |||
} | |||
catch (Exception e) | |||
{ | |||
action.ResetProgress(); | |||
if (e.InnerException != null) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.InnerException.Message, e.InnerException.StackTrace, e.Message); | |||
} | |||
else | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, action.ErrorString()); | |||
List<string> ExceptionBreakList = new List<string>() { "System.ArgumentException", "System.SecurityException", "System.UnauthorizedAccessException", "System.UnauthorizedAccessException", "System.TimeoutException" }; | |||
if (ExceptionBreakList.Any(x => x.Equals(e.GetType().ToString()))) | |||
{ | |||
i = 10; | |||
break; | |||
} | |||
} | |||
} | |||
Console.WriteLine($"Status: {action.GetStatus()}"); | |||
if (i > 0) Thread.Sleep(50); | |||
i++; | |||
} while (action.GetStatus() != UninstallTaskStatus.Completed && i < 10); | |||
if (i == 10) | |||
{ | |||
var errorString = action.ErrorString(); | |||
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}"); | |||
//Environment.Exit(-2); | |||
Console.WriteLine($"Action completed. Weight:{action.GetProgressWeight()}"); | |||
continue; | |||
} | |||
Console.WriteLine($"Action completed. Weight:{action.GetProgressWeight()}"); | |||
} | |||
Console.WriteLine("Task completed."); | |||
File.AppendAllText("TasksAdded.txt", task.Title + Environment.NewLine); | |||
} | |||
catch (Exception e) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, | |||
"Encountered an error while doing task actions."); | |||
} | |||
return 0; | |||
} | |||
public static Task<Playbook> DeserializePlaybook(string dir) | |||
{ | |||
Playbook pb; | |||
XmlSerializer serializer = new XmlSerializer(typeof(Playbook)); | |||
using (XmlReader reader = XmlReader.Create($"{dir}\\playbook.conf")) | |||
{ | |||
pb = (Playbook)(serializer.Deserialize(reader)); | |||
} | |||
pb.Path = dir; | |||
return Task.FromResult(pb); | |||
} | |||
public static async Task<int> StartAmelioration() | |||
{ | |||
//Needed after defender removal's reboot, the "current directory" will be set to System32 | |||
//After the auto start up. | |||
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); | |||
if (File.Exists("TasksAdded.txt") && !WinUtil.IsTrustedInstaller()) | |||
{ | |||
File.Delete("TasksAdded.txt"); | |||
} | |||
if (Directory.Exists("Logs") && !WinUtil.IsTrustedInstaller()) | |||
{ | |||
if (File.Exists("Logs\\AdminOutput.txt")) | |||
{ | |||
File.Delete("Logs\\AdminOutput.txt"); | |||
} | |||
if (File.Exists("Logs\\TIOutput.txt")) | |||
{ | |||
File.Delete("Logs\\TIOutput.txt"); | |||
} | |||
if (File.Exists("Logs\\FileChecklist.txt")) | |||
{ | |||
File.Delete("Logs\\FileChecklist.txt"); | |||
} | |||
} | |||
//Check if KPH is installed. | |||
ServiceController service = ServiceController.GetDevices() | |||
.FirstOrDefault(s => s.DisplayName == "KProcessHacker2"); | |||
if (service == null) | |||
{ | |||
//Installs KPH | |||
await WinUtil.RemoveProtectionAsync(); | |||
} | |||
var langsFile = Path.Combine($"{Playbook.Path}\\Configuration", "langs.txt"); | |||
//Download language packs that were selected by the user | |||
if (!File.Exists(langsFile)) | |||
{ | |||
File.Create(langsFile); | |||
} | |||
//var langsSelected = File.ReadLines(langsFile); | |||
//await DownloadLanguagesAsync(langsSelected); | |||
//Start adding tasks from the top level configuration folder. | |||
if (!AddTasks($"{Playbook.Path}\\Configuration", "custom.yml")) | |||
{ | |||
Console.WriteLine($":AME-Fatal Error: Error adding tasks."); | |||
Environment.Exit(1); | |||
} | |||
if (!Parser.Tasks.Any()) | |||
{ | |||
Console.Error.WriteLine($"Couldn't find any tasks."); | |||
return -1; | |||
} | |||
//Sort the list based on the priority value. | |||
if (Parser.Tasks.Any(x => x.Priority != Parser.Tasks.First().Priority)) | |||
Parser.Tasks.Sort(new TaskComparer()); | |||
UninstallTaskPrivilege prevPriv = UninstallTaskPrivilege.Admin; | |||
foreach (var task in Parser.Tasks.Where(task => task.Actions.Count != 0)) | |||
{ | |||
try | |||
{ | |||
if (prevPriv == UninstallTaskPrivilege.TrustedInstaller && task.Privilege == UninstallTaskPrivilege.TrustedInstaller && !WinUtil.IsTrustedInstaller()) | |||
{ | |||
continue; | |||
} | |||
await DoActions(task, task.Privilege); | |||
prevPriv = task.Privilege; | |||
} | |||
catch (Exception ex) | |||
{ | |||
ErrorLogger.WriteToErrorLog(ex.Message, ex.StackTrace, "Error during DoAction loop."); | |||
} | |||
} | |||
if (WinUtil.IsTrustedInstaller()) return 0; | |||
WinUtil.RegistryManager.UnhookUserHives(); | |||
//Check how many files were successfully and unsuccessfully deleted. | |||
var deletedItemsCount = 0; | |||
var failedDeletedItemsCount = 0; | |||
if (File.Exists("Logs\\FileChecklist.txt")) | |||
{ | |||
using (var reader = new StreamReader("Logs\\FileChecklist.txt")) | |||
{ | |||
var data = reader.ReadToEnd(); | |||
var listData = data.Split(new [] { Environment.NewLine }, StringSplitOptions.None).ToList(); | |||
deletedItemsCount = listData.FindAll(s => s == "Deleted: True").Count(); | |||
failedDeletedItemsCount = listData.FindAll(s => s == "Deleted: False").Count(); | |||
} | |||
using (var writer = new StreamWriter("Logs\\FileChecklist.txt", true)) | |||
{ | |||
writer.WriteLine($"{deletedItemsCount} files were deleted successfully. " + | |||
$"{failedDeletedItemsCount} files couldn't be deleted."); | |||
} | |||
} | |||
Console.WriteLine($"{deletedItemsCount} files were deleted successfully. " + | |||
$"{failedDeletedItemsCount} files couldn't be deleted."); | |||
//Check if the kernel driver is installed. | |||
service = ServiceController.GetDevices() | |||
.FirstOrDefault(s => s.DisplayName == "KProcessHacker2"); | |||
if (service != null) | |||
{ | |||
//Remove Process Hacker's kernel driver. | |||
await WinUtil.UninstallDriver(); | |||
} | |||
File.Delete("TasksAdded.txt"); | |||
Console.WriteLine(); | |||
Console.WriteLine("Playbook finished."); | |||
return 0; | |||
} | |||
public static async Task DownloadLanguagesAsync(IEnumerable<string> langsSelected) | |||
{ | |||
foreach (var lang in langsSelected) | |||
{ | |||
var lowerLang = lang.ToLower(); | |||
var arch = RuntimeInformation.OSArchitecture; | |||
var winVersion = Environment.OSVersion.Version.Build; | |||
var convertedArch = ""; | |||
switch (arch) | |||
{ | |||
case Architecture.X64: | |||
convertedArch = "amd64"; | |||
break; | |||
case Architecture.Arm64: | |||
convertedArch = "arm64"; | |||
break; | |||
case Architecture.X86: | |||
convertedArch = "x86"; | |||
break; | |||
} | |||
var uuidOfWindowsVersion = ""; | |||
var uuidResponse = | |||
await Client.GetAsync( | |||
$"https://api.uupdump.net/listid.php?search={winVersion}%20{convertedArch}&sortByDate=1"); | |||
switch (uuidResponse.StatusCode) | |||
{ | |||
//200 Status code | |||
case HttpStatusCode.OK: | |||
{ | |||
var result = uuidResponse.Content.ReadAsStringAsync().Result; | |||
//Gets the UUID of the first build object in the response, we take the first since it's the newest. | |||
uuidOfWindowsVersion = (string)(JToken.Parse(result)["response"]?["builds"]?.Children().First() | |||
.Children().First().Last()); | |||
break; | |||
} | |||
//400 Status code | |||
case HttpStatusCode.BadRequest: | |||
{ | |||
var result = uuidResponse.Content.ReadAsStringAsync().Result; | |||
dynamic data = JObject.Parse(result); | |||
Console.WriteLine($"Bad request.\r\nError:{data["response"]["error"]}"); | |||
break; | |||
} | |||
//429 Status code | |||
case (HttpStatusCode)429: | |||
{ | |||
var result = uuidResponse.Content.ReadAsStringAsync().Result; | |||
dynamic data = JObject.Parse(result); | |||
Console.WriteLine($"Too many requests, try again later.\r\nError:{data["response"]["error"]}"); | |||
break; | |||
} | |||
//500 Status code | |||
case HttpStatusCode.InternalServerError: | |||
{ | |||
var result = uuidResponse.Content.ReadAsStringAsync().Result; | |||
dynamic data = JObject.Parse(result); | |||
Console.WriteLine($"Internal Server Error.\r\nError:{data["response"]["error"]}"); | |||
break; | |||
} | |||
default: | |||
throw new ArgumentOutOfRangeException(); | |||
} | |||
var responseString = | |||
await Client.GetAsync( | |||
$"https://api.uupdump.net/get.php?id={uuidOfWindowsVersion}&lang={lowerLang}"); | |||
switch (responseString.StatusCode) | |||
{ | |||
//200 Status code | |||
case HttpStatusCode.OK: | |||
{ | |||
var result = responseString.Content.ReadAsStringAsync().Result; | |||
dynamic data = JObject.Parse(result); | |||
//Add different urls to different packages to a list | |||
var urls = new Dictionary<string, string> | |||
{ | |||
{ | |||
"basic", (string) data["response"]["files"][ | |||
$"microsoft-windows-languagefeatures-basic-{lowerLang}-package-{convertedArch}.cab"] | |||
[ | |||
"url"] | |||
}, | |||
{ | |||
"hw", (string) data["response"]["files"][ | |||
$"microsoft-windows-languagefeatures-handwriting-{lowerLang}-package-{convertedArch}.cab"] | |||
[ | |||
"url"] | |||
}, | |||
{ | |||
"ocr", (string) data["response"]["files"][ | |||
$"microsoft-windows-languagefeatures-ocr-{lowerLang}-package-{convertedArch}.cab"][ | |||
"url"] | |||
}, | |||
{ | |||
"speech", (string) data["response"]["files"][ | |||
$"microsoft-windows-languagefeatures-speech-{lowerLang}-package-{convertedArch}.cab"] | |||
[ | |||
"url"] | |||
}, | |||
{ | |||
"tts", (string) data["response"]["files"][ | |||
$"microsoft-windows-languagefeatures-texttospeech-{lowerLang}-package-{convertedArch}.cab"] | |||
[ | |||
"url"] | |||
} | |||
}; | |||
var amePath = Path.Combine(Path.GetTempPath(), "AME\\"); | |||
//Create the directory if it doesn't exist. | |||
var file = new FileInfo(amePath); | |||
file.Directory?.Create(); //Does nothing if the directory already exists | |||
//Final result being "temp\AME\Languages\file.cab" | |||
var downloadPath = Path.Combine(amePath, "Languages\\"); | |||
file = new FileInfo(downloadPath); | |||
file.Directory?.Create(); | |||
using (var webClient = new WebClient()) | |||
{ | |||
Console.WriteLine($"Downloading {lowerLang}.cab file, please wait.."); | |||
foreach (var url in urls) | |||
{ | |||
//Check if the file exists, if it does exist, skip it. | |||
if (File.Exists(Path.Combine(downloadPath, $"{url.Key}_{lowerLang}.cab"))) | |||
{ | |||
Console.WriteLine($"{url.Key}_{lowerLang} already exists, skipping."); | |||
continue; | |||
} | |||
//Output file format: featureName_languageCode.cab: speech_de-de.cab | |||
webClient.DownloadFile(url.Value, $@"{downloadPath}\{url.Key}_{lowerLang}.cab"); | |||
} | |||
} | |||
break; | |||
} | |||
//400 Status code | |||
case HttpStatusCode.BadRequest: | |||
{ | |||
var result = responseString.Content.ReadAsStringAsync().Result; | |||
dynamic data = JObject.Parse(result); | |||
Console.WriteLine($"Bad request.\r\nError:{data["response"]["error"]}"); | |||
break; | |||
} | |||
//429 Status code | |||
case (HttpStatusCode)429: | |||
{ | |||
var result = responseString.Content.ReadAsStringAsync().Result; | |||
dynamic data = JObject.Parse(result); | |||
Console.WriteLine($"Too many requests, try again later.\r\nError:{data["response"]["error"]}"); | |||
break; | |||
} | |||
//500 Status code | |||
case HttpStatusCode.InternalServerError: | |||
{ | |||
var result = responseString.Content.ReadAsStringAsync().Result; | |||
dynamic data = JObject.Parse(result); | |||
Console.WriteLine($"Internal Server Error.\r\nError:{data["response"]["error"]}"); | |||
break; | |||
} | |||
default: | |||
throw new ArgumentOutOfRangeException(); | |||
} | |||
} | |||
} | |||
public static async Task<bool> SafeRunAction(ITaskAction action) | |||
{ | |||
try | |||
{ | |||
return await action.RunTask(); | |||
} | |||
catch (Exception e) | |||
{ | |||
action.ResetProgress(); | |||
if (e.InnerException != null) | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.InnerException.Message, e.InnerException.StackTrace, e.Message); | |||
} | |||
else | |||
{ | |||
ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, action.ErrorString()); | |||
} | |||
} | |||
return false; | |||
} | |||
} | |||
} |
@ -0,0 +1,12 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace TrustedUninstaller.Shared | |||
{ | |||
class Class1 | |||
{ | |||
} | |||
} |
@ -0,0 +1,115 @@ | |||
using System; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.IO.MemoryMappedFiles; | |||
using System.Text; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using System.Windows.Forms; | |||
using System.Text.RegularExpressions; | |||
namespace TrustedUninstaller.Shared | |||
{ | |||
public static class DualOut | |||
{ | |||
private static TextWriter _current; | |||
static MemoryMappedFile mmf = null; | |||
static MemoryMappedViewAccessor accessor = null; | |||
static int absolutePosition = 0; | |||
static int relativePosition = 0; | |||
private static string logDir = Directory.GetCurrentDirectory() + "\\Logs"; | |||
private class OutputWriter : TextWriter | |||
{ | |||
public override Encoding Encoding | |||
{ | |||
get | |||
{ | |||
return _current.Encoding; | |||
} | |||
} | |||
public override void WriteLine(string value) | |||
{ | |||
value = Regex.Replace(value, "(?<!\\r)\\n", "\r\n"); | |||
_current.WriteLine(value); | |||
if (!WinUtil.IsTrustedInstaller()) | |||
{ | |||
File.AppendAllText($"{logDir}\\AdminOutput.txt", value + Environment.NewLine); | |||
} | |||
else | |||
{ | |||
// Small window size to enforce roll-over for testing. | |||
var windowSize = 2000000; | |||
value += Environment.NewLine; | |||
var bytes = Encoding.UTF8.GetBytes(value); | |||
try | |||
{ | |||
accessor = mmf.CreateViewAccessor(absolutePosition, windowSize, MemoryMappedFileAccess.ReadWrite); | |||
if (bytes.Length + relativePosition > windowSize) | |||
{ | |||
absolutePosition += relativePosition; | |||
relativePosition = 0; | |||
accessor.Dispose(); | |||
accessor = mmf.CreateViewAccessor(absolutePosition, windowSize, MemoryMappedFileAccess.ReadWrite); | |||
} | |||
accessor.WriteArray(relativePosition, bytes, 0, bytes.Length); | |||
relativePosition += bytes.Length; | |||
} | |||
finally | |||
{ | |||
if (accessor != null) | |||
accessor.Dispose(); | |||
} | |||
File.AppendAllText($"{logDir}\\TIOutput.txt", value + Environment.NewLine); | |||
// Small delay, less far less than a millisecond on most processors | |||
Thread.SpinWait(40000); | |||
} | |||
} | |||
public override void WriteLine() | |||
{ | |||
WriteLine(""); | |||
} | |||
} | |||
public static void Init() | |||
{ | |||
if (!Directory.Exists(logDir)) | |||
{ | |||
Directory.CreateDirectory(logDir); | |||
} | |||
if (WinUtil.IsTrustedInstaller()) | |||
{ | |||
var i = 0; | |||
while (true) | |||
{ | |||
try | |||
{ | |||
mmf = MemoryMappedFile.OpenExisting("ImgA"); | |||
break; | |||
} | |||
catch (FileNotFoundException) | |||
{ | |||
if (i > 300) throw new Exception("Memory file not found."); | |||
Task.Delay(200).Wait(); | |||
i++; | |||
} | |||
} | |||
} | |||
_current = Console.Out; | |||
Console.SetOut(new OutputWriter()); | |||
} | |||
} | |||
} |
@ -0,0 +1,58 @@ | |||
| |||
using System; | |||
using System.IO; | |||
using System.Windows.Forms; | |||
namespace TrustedUninstaller.Shared | |||
{ | |||
public class ErrorLogger | |||
{ | |||
public static void WriteToErrorLog(string msg, string stkTrace, string title, string? item = null) | |||
{ | |||
if (!(Directory.Exists(Directory.GetCurrentDirectory() + "\\Logs"))) | |||
{ | |||
Directory.CreateDirectory(Directory.GetCurrentDirectory() + "\\Logs"); | |||
} | |||
try | |||
{ | |||
FileStream fs = new FileStream(Directory.GetCurrentDirectory() + "\\Logs\\ErrorLog.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite); | |||
StreamWriter s = new StreamWriter(fs); | |||
s.Close(); | |||
fs.Close(); | |||
FileStream fs1 = new FileStream(Directory.GetCurrentDirectory() + "\\Logs\\ErrorLog.txt", FileMode.Append, FileAccess.Write); | |||
StreamWriter s1 = new StreamWriter(fs1); | |||
s1.WriteLine("Title: " + title); | |||
s1.WriteLine("Message: " + msg.TrimEnd('\n').TrimEnd('\r')); | |||
if (stkTrace != null) s1.WriteLine(Environment.NewLine + "StackTrace: " + stkTrace + Environment.NewLine); | |||
if (item != null) s1.WriteLine("Object: " + item); | |||
s1.WriteLine("Date/Time: " + DateTime.Now); | |||
s1.WriteLine | |||
("============================================"); | |||
s1.Close(); | |||
fs1.Close(); | |||
} | |||
catch (Exception e) | |||
{ | |||
Console.WriteLine("ERROR WRITING INTO THE ERROR LOG: " + e.Message); | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,11 @@ | |||
using System; | |||
namespace TrustedUninstaller.Shared.Exceptions | |||
{ | |||
public class InvalidRegistryEntryException : Exception | |||
{ | |||
public InvalidRegistryEntryException() { } | |||
public InvalidRegistryEntryException(string message) : base(message) { } | |||
public InvalidRegistryEntryException(string message, Exception inner) : base(message, inner) { } | |||
} | |||
} |
@ -0,0 +1,11 @@ | |||
using System; | |||
namespace TrustedUninstaller.Shared.Exceptions | |||
{ | |||
public class TaskInProgressException : Exception | |||
{ | |||
public TaskInProgressException() {} | |||
public TaskInProgressException(string message) : base(message) {} | |||
public TaskInProgressException(string message, Exception inner) : base(message, inner) {} | |||
} | |||
} |
@ -0,0 +1,20 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Microsoft.Win32; | |||
namespace TrustedUninstaller.Shared | |||
{ | |||
public class Globals | |||
{ | |||
public const string CurrentVersion = "0.5"; | |||
public const double CurrentVersionNumber = 0.5; | |||
#if DEBUG | |||
public static readonly int WinVer = 19045; | |||
#else | |||
public static readonly int WinVer = Int32.Parse(Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue("CurrentBuildNumber").ToString()); | |||
#endif | |||
} | |||
} |
@ -0,0 +1,189 @@ | |||
using Microsoft.Win32.SafeHandles; | |||
using System; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Runtime.InteropServices; | |||
namespace TrustedUninstaller.Shared | |||
{ | |||
[StructLayout(LayoutKind.Sequential)] | |||
public struct SECURITY_ATTRIBUTES | |||
{ | |||
public int nLength; | |||
public IntPtr lpSecurityDescriptor; | |||
public int bInheritHandle; | |||
} | |||
[StructLayout(LayoutKind.Sequential)] | |||
internal struct PROCESS_INFORMATION | |||
{ | |||
public IntPtr hProcess; | |||
public IntPtr hThread; | |||
public int dwProcessId; | |||
public int dwThreadId; | |||
} | |||
// This also works with CharSet.Ansi as long as the calling function uses the same character set. | |||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |||
public struct STARTUPINFO | |||
{ | |||
public Int32 cb; | |||
public string lpReserved; | |||
public string lpDesktop; | |||
public string lpTitle; | |||
public Int32 dwX; | |||
public Int32 dwY; | |||
public Int32 dwXSize; | |||
public Int32 dwYSize; | |||
public Int32 dwXCountChars; | |||
public Int32 dwYCountChars; | |||
public Int32 dwFillAttribute; | |||
public Int32 dwFlags; | |||
public Int16 wShowWindow; | |||
public Int16 cbReserved2; | |||
public IntPtr lpReserved2; | |||
public IntPtr hStdInput; | |||
public IntPtr hStdOutput; | |||
public IntPtr hStdError; | |||
} | |||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |||
public struct STARTUPINFOEX | |||
{ | |||
public STARTUPINFO StartupInfo; | |||
public IntPtr lpAttributeList; | |||
} | |||
public class NativeProcess | |||
{ | |||
[DllImport("kernel32.dll")] | |||
[return: MarshalAs(UnmanagedType.Bool)] | |||
private static extern bool CreateProcess( | |||
string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, | |||
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, | |||
IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, | |||
out PROCESS_INFORMATION lpProcessInformation); | |||
[DllImport("kernel32.dll", SetLastError = true)] | |||
[return: MarshalAs(UnmanagedType.Bool)] | |||
private static extern bool UpdateProcThreadAttribute( | |||
IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue, | |||
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize); | |||
[DllImport("kernel32.dll", SetLastError = true)] | |||
[return: MarshalAs(UnmanagedType.Bool)] | |||
private static extern bool InitializeProcThreadAttributeList( | |||
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize); | |||
[DllImport("kernel32.dll", SetLastError = true)] | |||
[return: MarshalAs(UnmanagedType.Bool)] | |||
private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList); | |||
[DllImport("kernel32.dll", SetLastError = true)] | |||
private static extern bool CloseHandle(IntPtr hObject); | |||
private const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000; | |||
private const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000; | |||
private const int CREATE_NO_WINDOW = 0x08000000; | |||
private const int CREATE_NEW_CONSOLE = 0x00000010; | |||
[DllImport("kernel32.dll", SetLastError = true)] | |||
static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); | |||
const UInt32 INFINITE = 0xFFFFFFFF; | |||
const UInt32 WAIT_ABANDONED = 0x00000080; | |||
const UInt32 WAIT_OBJECT_0 = 0x00000000; | |||
const UInt32 WAIT_TIMEOUT = 0x00000102; | |||
public static Process? Process; | |||
public static bool StartProcess(string path, int parent, string playbookDir) | |||
{ | |||
Process = null; | |||
// Create a new process with a different parent process | |||
// https://stackoverflow.com/questions/10554913/how-to-call-createprocess-with-startupinfoex-from-c-sharp-and-re-parent-the-ch | |||
var pInfo = new PROCESS_INFORMATION(); | |||
var sInfoEx = new STARTUPINFOEX(); | |||
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx); | |||
var lpValue = IntPtr.Zero; | |||
try | |||
{ | |||
var lpSize = IntPtr.Zero; | |||
var success = InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize); | |||
if (success || lpSize == IntPtr.Zero) | |||
{ | |||
return false; | |||
} | |||
sInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize); | |||
success = InitializeProcThreadAttributeList(sInfoEx.lpAttributeList, 1, 0, ref lpSize); | |||
if (!success) | |||
{ | |||
return false; | |||
} | |||
var parentHandle = Process.GetProcessById(parent).Handle; | |||
// This value should persist until the attribute list is destroyed using the DeleteProcThreadAttributeList function | |||
lpValue = Marshal.AllocHGlobal(IntPtr.Size); | |||
Marshal.WriteIntPtr(lpValue, parentHandle); | |||
success = UpdateProcThreadAttribute( | |||
sInfoEx.lpAttributeList, | |||
0, | |||
(IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, | |||
lpValue, | |||
(IntPtr)IntPtr.Size, | |||
IntPtr.Zero, | |||
IntPtr.Zero); | |||
if (!success) | |||
{ | |||
return false; | |||
} | |||
var pSec = new SECURITY_ATTRIBUTES(); | |||
var tSec = new SECURITY_ATTRIBUTES(); | |||
pSec.nLength = Marshal.SizeOf(pSec); | |||
tSec.nLength = Marshal.SizeOf(tSec); | |||
CreateProcess(null, | |||
$"\"{path}\" \"{playbookDir}\"", | |||
ref pSec, | |||
ref tSec, | |||
false, | |||
EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW, | |||
IntPtr.Zero, | |||
null, | |||
ref sInfoEx, | |||
out pInfo); | |||
//WaitForSingleObject(pInfo.hProcess, INFINITE); | |||
Process = Process.GetProcessById(pInfo.dwProcessId); | |||
return true; | |||
} | |||
finally | |||
{ | |||
// Free the attribute list | |||
if (sInfoEx.lpAttributeList != IntPtr.Zero) | |||
{ | |||
DeleteProcThreadAttributeList(sInfoEx.lpAttributeList); | |||
Marshal.FreeHGlobal(sInfoEx.lpAttributeList); | |||
} | |||
Marshal.FreeHGlobal(lpValue); | |||
// Close process and thread handles | |||
if (pInfo.hProcess != IntPtr.Zero) | |||
{ | |||
CloseHandle(pInfo.hProcess); | |||
} | |||
if (pInfo.hThread != IntPtr.Zero) | |||
{ | |||
CloseHandle(pInfo.hThread); | |||
} | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,10 @@ | |||
namespace TrustedUninstaller.Shared | |||
{ | |||
internal enum NtStatus | |||
{ | |||
NT_SUCCESS, | |||
NT_INFORMATION, | |||
NT_WARNING, | |||
NT_ERROR, | |||
} | |||
} |
@ -0,0 +1,102 @@ | |||
#nullable enable | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using System.Windows; | |||
using Microsoft.Win32; | |||
using TrustedUninstaller.Shared.Actions; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Serialization; | |||
using YamlDotNet.Serialization.NamingConventions; | |||
using YamlDotNet.Serialization.TypeResolvers; | |||
namespace TrustedUninstaller.Shared.Parser | |||
{ | |||
public class TaskComparer : IComparer<UninstallTask> { | |||
public int Compare(UninstallTask x, UninstallTask y) | |||
{ | |||
return ReferenceEquals(x, y) ? 0 : x.Priority.CompareTo(y.Priority); | |||
} | |||
} | |||
public class ConfigParser | |||
{ | |||
public List<UninstallTask> Tasks { get; set; } | |||
private IDeserializer Deserializer { get; } | |||
private ISerializer Serializer { get; } | |||
public ConfigParser() | |||
{ | |||
Tasks = new List<UninstallTask>(); | |||
Deserializer = new DeserializerBuilder() | |||
.WithNamingConvention(CamelCaseNamingConvention.Instance) | |||
.WithTagMapping("!file", typeof(FileAction)) | |||
.WithTagMapping("!service", typeof(ServiceAction)) | |||
.WithTagMapping("!registryKey", typeof(RegistryKeyAction)) | |||
.WithTagMapping("!registryValue", typeof(RegistryValueAction)) | |||
.WithTagMapping("!appx", typeof(AppxAction)) | |||
.WithTagMapping("!systemPackage", typeof(SystemPackageAction)) | |||
.WithTagMapping("!lineInFile", typeof(LineInFileAction)) | |||
.WithTagMapping("!scheduledTask", typeof(ScheduledTaskAction)) | |||
.WithTagMapping("!user", typeof(UserAction)) | |||
.WithTagMapping("!run", typeof(RunAction)) | |||
.WithTagMapping("!powerShell", typeof(PowerShellAction)) | |||
.WithTagMapping("!shortcut", typeof(ShortcutAction)) | |||
.WithTagMapping("!cmd", typeof(CmdAction)) | |||
.WithTagMapping("!uninstallTask", typeof(UninstallTask)) | |||
.WithTagMapping("!taskKill", typeof(TaskKillAction)) | |||
.WithTagMapping("!update", typeof(UpdateAction)) | |||
.WithTagMapping("!writeStatus", typeof(WriteStatusAction)) | |||
.WithNodeTypeResolver(new TaskActionResolver()) | |||
.Build(); | |||
Serializer = new SerializerBuilder() | |||
.WithNamingConvention(CamelCaseNamingConvention.Instance) | |||
.WithTagMapping("!file", typeof(FileAction)) | |||
.WithTagMapping("!service", typeof(ServiceAction)) | |||
.WithTagMapping("!registryKey", typeof(RegistryKeyAction)) | |||
.WithTagMapping("!registryValue", typeof(RegistryValueAction)) | |||
.WithTagMapping("!appx", typeof(AppxAction)) | |||
.WithTagMapping("!systemPackage", typeof(SystemPackageAction)) | |||
.WithTagMapping("!lineInFile", typeof(LineInFileAction)) | |||
.WithTagMapping("!scheduledTask", typeof(ScheduledTaskAction)) | |||
.WithTagMapping("!user", typeof(UserAction)) | |||
.WithTagMapping("!run", typeof(RunAction)) | |||
.WithTagMapping("!powerShell", typeof(PowerShellAction)) | |||
.WithTagMapping("!shortcut", typeof(ShortcutAction)) | |||
.WithTagMapping("!cmd", typeof(CmdAction)) | |||
.WithTagMapping("!uninstallTask", typeof(UninstallTask)) | |||
.WithTagMapping("!taskKill", typeof(TaskKillAction)) | |||
.WithTagMapping("!update", typeof(UpdateAction)) | |||
.WithTagMapping("!writeStatus", typeof(WriteStatusAction)) | |||
.WithTypeResolver(new DynamicTypeResolver()) | |||
.EnsureRoundtrip() | |||
.Build(); | |||
} | |||
public void SerializeItem(TextWriter tw, object item) | |||
{ | |||
Serializer.Serialize(tw, item); | |||
} | |||
public bool Add(string filename) | |||
{ | |||
var configData = File.ReadAllText(filename); | |||
var taskData = Deserializer.Deserialize<UninstallTask>(configData); | |||
if (taskData.SupportedBuilds != null && !taskData.SupportedBuilds.Contains(Globals.WinVer.ToString())) | |||
{ | |||
return false; | |||
} | |||
taskData.Update(); | |||
Tasks.Add(taskData); | |||
return true; | |||
} | |||
} | |||
} |
@ -0,0 +1,75 @@ | |||
| |||
using System; | |||
using TrustedUninstaller.Shared.Actions; | |||
using TrustedUninstaller.Shared.Tasks; | |||
using YamlDotNet.Core.Events; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Parser | |||
{ | |||
internal class TaskActionResolver : INodeTypeResolver | |||
{ | |||
public bool Resolve(NodeEvent? nodeEvent, ref Type currentType) | |||
{ | |||
if (!currentType.IsInterface || currentType != typeof(ITaskAction)) | |||
{ | |||
return false; | |||
} | |||
switch (nodeEvent?.Tag.Value) | |||
{ | |||
case "!file:": | |||
currentType = typeof(FileAction); | |||
return true; | |||
case "!service:": | |||
currentType = typeof(ServiceAction); | |||
return true; | |||
case "!user:": | |||
currentType = typeof(UserAction); | |||
return true; | |||
case "!run:": | |||
currentType = typeof(RunAction); | |||
return true; | |||
case "!powerShell:": | |||
currentType = typeof(PowerShellAction); | |||
return true; | |||
case "!shortcut:": | |||
currentType = typeof(ShortcutAction); | |||
return true; | |||
case "!cmd:": | |||
currentType = typeof(CmdAction); | |||
return true; | |||
case "!scheduledTask:": | |||
currentType = typeof(ScheduledTaskAction); | |||
return true; | |||
case "!lineInFile:": | |||
currentType = typeof(LineInFileAction); | |||
return true; | |||
case "!registryKey:": | |||
currentType = typeof(RegistryKeyAction); | |||
return true; | |||
case "!registryValue:": | |||
currentType = typeof(RegistryValueAction); | |||
return true; | |||
case "!appx:": | |||
currentType = typeof(AppxAction); | |||
return true; | |||
case "!systemPackage:": | |||
currentType = typeof(SystemPackageAction); | |||
return true; | |||
case "!taskKill:": | |||
currentType = typeof(TaskKillAction); | |||
return true; | |||
case "!update:": | |||
currentType = typeof(UpdateAction); | |||
return true; | |||
case "!writeStatus:": | |||
currentType = typeof(WriteStatusAction); | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,42 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using System.Xml.Serialization; | |||
namespace TrustedUninstaller.Shared | |||
{ | |||
public class Playbook | |||
{ | |||
public string Name { get; set; } | |||
public string ShortDescription { get; set; } | |||
public string Description { get; set; } | |||
public string Title { get; set; } | |||
public string Username { get; set; } | |||
public string Details { get; set; } | |||
public string Version { 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 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 string Path { get; set; } | |||
public override string ToString() | |||
{ | |||
return $"Name: {Name}\nDescription: {Description}\nUsername: {Username}\nDetails: {Details}\nRequirements: {Requirements}."; | |||
} | |||
} | |||
} |
@ -0,0 +1,13 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace TrustedUninstaller.Shared.Predicates | |||
{ | |||
interface IPredicate | |||
{ | |||
public Task<bool> Evaluate(); | |||
} | |||
} |
@ -0,0 +1,37 @@ | |||
using System.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
using TrustedUninstaller.Shared; | |||
// General Information about an assembly is controlled through the following | |||
// set of attributes. Change these attribute values to modify the information | |||
// associated with an assembly. | |||
[assembly: AssemblyTitle("TrustedUninstaller.Shared")] | |||
[assembly: AssemblyDescription("")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("Ameliorated LLC")] | |||
[assembly: AssemblyProduct("TrustedUninstaller.Shared")] | |||
[assembly: AssemblyCopyright("MIT License")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
// Setting ComVisible to false makes the types in this assembly not visible | |||
// to COM components. If you need to access a type in this assembly from | |||
// COM, set the ComVisible attribute to true on that type. | |||
[assembly: ComVisible(false)] | |||
// The following GUID is for the ID of the typelib if this project is exposed to COM | |||
[assembly: Guid("9bda9d32-e9a1-4db8-9d90-443792107e28")] | |||
// Version information for an assembly consists of the following four values: | |||
// | |||
// Major Version | |||
// Minor Version | |||
// Build Number | |||
// Revision | |||
// | |||
// You can specify all the values or you can default the Build and Revision Numbers | |||
// by using the '*' as shown below: | |||
// [assembly: AssemblyVersion("1.0.*")] | |||
[assembly: AssemblyVersion(Globals.CurrentVersion)] | |||
[assembly: AssemblyFileVersion(Globals.CurrentVersion)] |
@ -0,0 +1,44 @@ | |||
using System; | |||
using System.Runtime.InteropServices; | |||
namespace TrustedUninstaller.Shared | |||
{ | |||
//Byte 0 | |||
[Flags] | |||
public enum SignatureStatusFlags : byte | |||
{ | |||
UpToDate = 0, | |||
OutOfDate = 16 | |||
} | |||
// Byte 1 | |||
[Flags] | |||
public enum AVStatusFlags : byte | |||
{ | |||
Unknown = 1, | |||
Enabled = 16 | |||
} | |||
// Byte 2 | |||
[Flags] | |||
public enum ProviderFlags : byte | |||
{ | |||
FIREWALL = 1, | |||
AUTOUPDATE_SETTINGS = 2, | |||
ANTIVIRUS = 4, | |||
ANTISPYWARE = 8, | |||
INTERNET_SETTINGS = 16, | |||
USER_ACCOUNT_CONTROL = 32, | |||
SERVICE = 64, | |||
NONE = 0, | |||
} | |||
[StructLayout(LayoutKind.Sequential)] | |||
public struct ProviderStatus | |||
{ | |||
public SignatureStatusFlags SignatureStatus; | |||
public AVStatusFlags AVStatus; | |||
public ProviderFlags SecurityProvider; | |||
public string DisplayName; | |||
} | |||
} |
@ -0,0 +1,320 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.Globalization; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Management; | |||
using System.Net; | |||
using System.Reflection; | |||
using System.Runtime.InteropServices; | |||
using System.Threading.Tasks; | |||
using System.Windows; | |||
using System.Xml.Serialization; | |||
using Microsoft.Win32; | |||
using TrustedUninstaller.Shared; | |||
using TrustedUninstaller.Shared.Actions; | |||
namespace TrustedUninstaller.Shared | |||
{ | |||
public static class Requirements | |||
{ | |||
[Serializable] | |||
public enum Requirement | |||
{ | |||
[XmlEnum("Internet")] | |||
Internet = 0, | |||
[XmlEnum("NoInternet")] | |||
NoInternet = 1, | |||
[XmlEnum("DefenderDisabled")] | |||
DefenderDisabled = 2, | |||
[XmlEnum("DefenderToggled")] | |||
DefenderToggled = 3, | |||
[XmlEnum("NoPendingUpdates")] | |||
NoPendingUpdates = 4, | |||
[XmlEnum("Activation")] | |||
Activation = 5, | |||
[XmlEnum("NoAntivirus")] | |||
NoAntivirus = 6 | |||
} | |||
public static async Task<Requirement[]> MetRequirements(this Requirement[] requirements) | |||
{ | |||
var requirementEnum = (Requirement[])Enum.GetValues(typeof(Requirement)); | |||
if (requirements == null) | |||
{ | |||
return requirementEnum; | |||
} | |||
// Add all requirements that are not included | |||
var metRequirements = requirementEnum.Except(requirements).ToList(); | |||
if (requirements.Contains (Requirement.Internet)) | |||
if (await new Internet().IsMet()) metRequirements.Add(Requirement.Internet); | |||
else metRequirements.Add(Requirement.NoInternet); | |||
if (requirements.Contains (Requirement.NoAntivirus)) | |||
if (await new NoAntivirus().IsMet()) metRequirements.Add(Requirement.NoAntivirus); | |||
if (requirements.Contains (Requirement.NoPendingUpdates)) | |||
if (await new NoPendingUpdates().IsMet()) metRequirements.Add(Requirement.NoPendingUpdates); | |||
if (requirements.Contains (Requirement.Activation)) | |||
if (await new Activation().IsMet()) metRequirements.Add(Requirement.Activation); | |||
if (requirements.Contains (Requirement.DefenderDisabled)) | |||
if (await new DefenderDisabled().IsMet()) metRequirements.Add(Requirement.DefenderDisabled); | |||
if (requirements.Contains (Requirement.DefenderToggled)) | |||
if (await new DefenderDisabled().IsMet()) metRequirements.Add(Requirement.DefenderToggled); | |||
return metRequirements.ToArray(); | |||
} | |||
public interface IRequirements | |||
{ | |||
Task<bool> IsMet(); | |||
Task<bool> Meet(); | |||
} | |||
public class RequirementBase | |||
{ | |||
public class ProgressEventArgs : EventArgs | |||
{ | |||
public int PercentAdded; | |||
public ProgressEventArgs(int percent) | |||
{ | |||
PercentAdded = percent; | |||
} | |||
} | |||
public event EventHandler<ProgressEventArgs> ProgressChanged; | |||
protected void OnProgressAdded(int percent) | |||
{ | |||
ProgressChanged?.Invoke(this, new ProgressEventArgs(percent)); | |||
} | |||
} | |||
public class Internet : RequirementBase, IRequirements | |||
{ | |||
[DllImport("wininet.dll", SetLastError = true)] | |||
private static extern bool InternetCheckConnection(string lpszUrl, int dwFlags, int dwReserved); | |||
[DllImport("wininet.dll", SetLastError=true)] | |||
extern static bool InternetGetConnectedState(out int lpdwFlags, int dwReserved); | |||
public async Task<bool> IsMet() | |||
{ | |||
try | |||
{ | |||
try | |||
{ | |||
if (!InternetCheckConnection("http://archlinux.org", 1, 0)) | |||
{ | |||
if (!InternetCheckConnection("http://google.com", 1, 0)) | |||
return false; | |||
} | |||
return true; | |||
} | |||
catch | |||
{ | |||
var request = (HttpWebRequest)WebRequest.Create("http://google.com"); | |||
request.KeepAlive = false; | |||
request.Timeout = 5000; | |||
using (var response = (HttpWebResponse)request.GetResponse()) | |||
return true; | |||
} | |||
} | |||
catch | |||
{ | |||
return false; | |||
} | |||
} | |||
public Task<bool> Meet() => throw new NotImplementedException(); | |||
} | |||
public class DefenderDisabled : RequirementBase, IRequirements | |||
{ | |||
public async Task<bool> IsMet() | |||
{ | |||
if (Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\WinDefend") != null) | |||
return false; | |||
return Process.GetProcessesByName("MsMpEng").Length == 0; | |||
} | |||
public async Task<bool> Meet() | |||
{ | |||
OnProgressAdded(30); | |||
try | |||
{ | |||
//Scheduled task to run the program on logon, and remove defender notifications | |||
var runOnLogOn = new CmdAction() | |||
{ | |||
Command = $"schtasks /create /tn \"AME Wizard\" /tr \"{Assembly.GetExecutingAssembly().Location}\" /sc onlogon /RL HIGHEST /f", | |||
Wait = false | |||
}; | |||
await runOnLogOn.RunTask(); | |||
OnProgressAdded(10); | |||
var disableNotifs = new CmdAction() | |||
{ | |||
Command = $"reg add \"HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows Defender Security Center\\Notifications\" /v DisableNotifications /t REG_DWORD /d 1 /f" | |||
}; | |||
await disableNotifs.RunTask(); | |||
OnProgressAdded(10); | |||
var defenderService = new RunAction() | |||
{ | |||
Exe = $"NSudoLC.exe", | |||
Arguments = "-U:T -P:E -M:S -Priority:RealTime -UseCurrentConsole -Wait reg delete \"HKLM\\SYSTEM\\CurrentControlSet\\Services\\WinDefend\" /f", | |||
BaseDir = true, | |||
CreateWindow = false | |||
}; | |||
await defenderService.RunTask(); | |||
OnProgressAdded(20); | |||
// MpOAV.dll normally in use by a lot of processes. This prevents that. | |||
var MpOAVCLSID = new RunAction() | |||
{ | |||
Exe = $"NSudoLC.exe", | |||
Arguments = @"-U:T -P:E -M:S -Priority:RealTime -Wait reg delete ""HKCR\CLSID\{2781761E-28E0-4109-99FE-B9D127C57AFE}\InprocServer32"" /f", | |||
BaseDir = true, | |||
CreateWindow = false | |||
}; | |||
await MpOAVCLSID.RunTask(); | |||
OnProgressAdded(20); | |||
if (Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\WinDefend") != null) | |||
{ | |||
throw new Exception("Could not remove WinDefend service."); | |||
} | |||
OnProgressAdded(10); | |||
return true; | |||
} | |||
catch (Exception exception) | |||
{ | |||
ErrorLogger.WriteToErrorLog(exception.Message, exception.StackTrace, | |||
$"Could not remove Windows Defender."); | |||
return false; | |||
// TODO: Move this to requirements page view if any Meet calls return false | |||
try | |||
{ | |||
var saveLogDir = System.Windows.Forms.Application.StartupPath + "\\AME Logs"; | |||
if (Directory.Exists(saveLogDir)) Directory.Delete(saveLogDir, true); | |||
Directory.Move(Directory.GetCurrentDirectory() + "\\Logs", saveLogDir); | |||
} | |||
catch (Exception) { } | |||
//MessageBox.Show("Could not remove Windows Defender. Check the error logs and contact the team " + | |||
// "for more information and assistance.", "Could not remove Windows Defender.", MessageBoxButton.OK, MessageBoxImage.Error); | |||
} | |||
} | |||
} | |||
public class DefenderToggled : RequirementBase, IRequirements | |||
{ | |||
public async Task<bool> IsMet() | |||
{ | |||
var defenderKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows Defender"); | |||
RegistryKey realtimeKey = null; | |||
try | |||
{ | |||
realtimeKey = defenderKey.OpenSubKey("Real-Time Protection"); | |||
} | |||
catch | |||
{ | |||
} | |||
if (realtimeKey != null) | |||
{ | |||
try | |||
{ | |||
if (!((int)realtimeKey.GetValue("DisableRealtimeMonitoring") != 1)) | |||
return false; | |||
} | |||
catch (Exception exception) | |||
{ | |||
return false; | |||
} | |||
} | |||
try | |||
{ | |||
if (!((int)defenderKey.OpenSubKey("SpyNet").GetValue("SpyNetReporting") != 0)) | |||
return false; | |||
} | |||
catch | |||
{ | |||
} | |||
try | |||
{ | |||
if (!((int)defenderKey.OpenSubKey("SpyNet").GetValue("SubmitSamplesConsent") != 0)) | |||
return false; | |||
} | |||
catch | |||
{ | |||
} | |||
try | |||
{ | |||
if (!((int)defenderKey.OpenSubKey("Features").GetValue("TamperProtection") != 4)) | |||
return false; | |||
} | |||
catch | |||
{ | |||
} | |||
return true; | |||
} | |||
public async Task<bool> Meet() | |||
{ | |||
throw new NotImplementedException(); | |||
} | |||
} | |||
public class NoPendingUpdates : RequirementBase, IRequirements | |||
{ | |||
public async Task<bool> IsMet() | |||
{ | |||
//TODO: This | |||
return true; | |||
} | |||
public Task<bool> Meet() => throw new NotImplementedException(); | |||
} | |||
public class NoAntivirus : RequirementBase, IRequirements | |||
{ | |||
public async Task<bool> IsMet() | |||
{ | |||
return !WinUtil.GetEnabledAvList(false).Any(); | |||
} | |||
public Task<bool> Meet() => throw new NotImplementedException(); | |||
} | |||
public class Activation : RequirementBase, IRequirements | |||
{ | |||
public async Task<bool> IsMet() | |||
{ | |||
return WinUtil.IsGenuineWindows(); | |||
} | |||
public Task<bool> Meet() => throw new NotImplementedException(); | |||
} | |||
public class WindowsBuild | |||
{ | |||
public bool IsMet(string[] builds) | |||
{ | |||
return builds.Any(x => x.Equals(Globals.WinVer.ToString())); | |||
} | |||
public Task<bool> Meet() => throw new NotImplementedException(); | |||
} | |||
} | |||
} |
@ -0,0 +1,24 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace TrustedUninstaller.Shared.Tasks | |||
{ | |||
public enum Scope | |||
{ | |||
AllUsers = 0, | |||
CurrentUser = 1, | |||
ActiveUsers = 2, | |||
DefaultUser = 3 | |||
} | |||
public interface ITaskAction | |||
{ | |||
public int GetProgressWeight(); | |||
public void ResetProgress(); | |||
public string ErrorString(); | |||
public UninstallTaskStatus GetStatus(); | |||
public Task<bool> RunTask(); | |||
} | |||
} |
@ -0,0 +1,9 @@ | |||
using System.Collections.Generic; | |||
namespace TrustedUninstaller.Shared.Tasks | |||
{ | |||
public class TaskList | |||
{ | |||
public List<UninstallTask> uninstallTasks { get; set; } = new List<UninstallTask> { }; | |||
} | |||
} |
@ -0,0 +1,58 @@ | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using TrustedUninstaller.Shared.Parser; | |||
using YamlDotNet.Serialization; | |||
namespace TrustedUninstaller.Shared.Tasks | |||
{ | |||
public class UninstallTask | |||
{ | |||
public string Title { get; set; } | |||
#nullable enable | |||
public string? Description { get; set; } | |||
public string[]? SupportedBuilds { get; set; } | |||
public int? MinVersion { get; set; } | |||
public int? MaxVersion { get; set; } | |||
#nullable disable | |||
public UninstallTaskStatus Status { get; set; } = UninstallTaskStatus.ToDo; | |||
public List<ITaskAction> Actions { get; set; } | |||
public int Priority { get; set; } = 1; | |||
public UninstallTaskPrivilege Privilege { get; set; } = UninstallTaskPrivilege.Admin; | |||
public List<string> Features { get; set; } = new List<string>(); | |||
public void Update() | |||
{ | |||
/* | |||
var statusList = Actions.Select(entry => entry.GetStatus()).ToList(); | |||
if (statusList.Any(entry => entry == UninstallTaskStatus.InProgress)) | |||
{ | |||
Status = UninstallTaskStatus.InProgress; | |||
} | |||
else if (statusList.All(entry => entry == UninstallTaskStatus.Completed)) | |||
{ | |||
Status = UninstallTaskStatus.Completed; | |||
} | |||
else | |||
{ | |||
Status = UninstallTaskStatus.ToDo; | |||
} | |||
*/ | |||
} | |||
public override string ToString() | |||
{ | |||
var sb = new StringBuilder(); | |||
var sw = new StringWriter(sb); | |||
var parser = new ConfigParser(); | |||
parser.SerializeItem(sw, this); | |||
return sb.ToString(); | |||
} | |||
} | |||
} |
@ -0,0 +1,8 @@ | |||
namespace TrustedUninstaller.Shared.Tasks | |||
{ | |||
public enum UninstallTaskPrivilege | |||
{ | |||
Admin, | |||
TrustedInstaller | |||
} | |||
} |
@ -0,0 +1,9 @@ | |||
namespace TrustedUninstaller.Shared.Tasks | |||
{ | |||
public enum UninstallTaskStatus | |||
{ | |||
Completed, | |||
InProgress, | |||
ToDo | |||
} | |||
} |
@ -0,0 +1,148 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | |||
<PropertyGroup> | |||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
<ProjectGuid>{9BDA9D32-E9A1-4DB8-9D90-443792107E28}</ProjectGuid> | |||
<OutputType>Library</OutputType> | |||
<AppDesignerFolder>Properties</AppDesignerFolder> | |||
<RootNamespace>TrustedUninstaller.Shared</RootNamespace> | |||
<AssemblyName>TrustedUninstaller.Shared</AssemblyName> | |||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> | |||
<LangVersion>8</LangVersion> | |||
<FileAlignment>512</FileAlignment> | |||
<Deterministic>true</Deterministic> | |||
<TargetFrameworkProfile /> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> | |||
<DebugSymbols>true</DebugSymbols> | |||
<OutputPath>bin\x64\Debug\</OutputPath> | |||
<DefineConstants>DEBUG;TRACE</DefineConstants> | |||
<DebugType>full</DebugType> | |||
<PlatformTarget>x64</PlatformTarget> | |||
<ErrorReport>prompt</ErrorReport> | |||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> | |||
<Nullable>enable</Nullable> | |||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> | |||
<OutputPath>bin\x64\Release\</OutputPath> | |||
<DefineConstants>TRACE</DefineConstants> | |||
<Optimize>true</Optimize> | |||
<DebugType>embedded</DebugType> | |||
<PlatformTarget>x64</PlatformTarget> | |||
<ErrorReport>prompt</ErrorReport> | |||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> | |||
<Nullable>enable</Nullable> | |||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="PresentationFramework" /> | |||
<Reference Include="System" /> | |||
<Reference Include="System.ComponentModel.Composition" /> | |||
<Reference Include="System.Core" /> | |||
<Reference Include="System.DirectoryServices" /> | |||
<Reference Include="System.DirectoryServices.AccountManagement" /> | |||
<Reference Include="System.Drawing" /> | |||
<Reference Include="System.IO.Compression, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"> | |||
<SpecificVersion>False</SpecificVersion> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System.Management" /> | |||
<Reference Include="System.Net.Http" /> | |||
<Reference Include="System.ServiceProcess" /> | |||
<Reference Include="System.Windows.Forms" /> | |||
<Reference Include="System.Xml.Linq" /> | |||
<Reference Include="System.Data.DataSetExtensions" /> | |||
<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> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="Actions\RegistryValueAction.cs" /> | |||
<Compile Include="Actions\SystemPackageAction.cs" /> | |||
<Compile Include="Actions\TaskKillAction.cs" /> | |||
<Compile Include="Actions\ScheduledTaskAction.cs" /> | |||
<Compile Include="Actions\LineInFileAction.cs" /> | |||
<Compile Include="Actions\CmdAction.cs" /> | |||
<Compile Include="Actions\ShortcutAction.cs" /> | |||
<Compile Include="Actions\PowerShellAction.cs" /> | |||
<Compile Include="Actions\RunAction.cs" /> | |||
<Compile Include="Actions\FileAction.cs" /> | |||
<Compile Include="Actions\AppxAction.cs" /> | |||
<Compile Include="Actions\LanguageAction.cs" /> | |||
<Compile Include="Actions\RegistryKeyAction.cs" /> | |||
<Compile Include="Actions\ServiceAction.cs" /> | |||
<Compile Include="Actions\UpdateAction.cs" /> | |||
<Compile Include="Actions\UserAction.cs" /> | |||
<Compile Include="Actions\WriteStatusAction.cs" /> | |||
<Compile Include="AmeliorationUtil.cs" /> | |||
<Compile Include="DualOut.cs" /> | |||
<Compile Include="Globals.cs" /> | |||
<Compile Include="Playbook.cs" /> | |||
<Compile Include="ProviderStatus.cs" /> | |||
<Compile Include="ErrorLogger.cs" /> | |||
<Compile Include="Exceptions\InvalidRegistryEntryException.cs" /> | |||
<Compile Include="Exceptions\TaskInProgressException.cs" /> | |||
<Compile Include="NativeProcess.cs" /> | |||
<Compile Include="Parser\ConfigParser.cs" /> | |||
<Compile Include="Parser\TaskActionResolver.cs" /> | |||
<Compile Include="Predicates\IPredicate.cs" /> | |||
<Compile Include="Requirements.cs" /> | |||
<Compile Include="Tasks\UninstallTaskPrivilege.cs" /> | |||
<Compile Include="Tasks\ITaskAction.cs" /> | |||
<Compile Include="Tasks\UninstallTaskStatus.cs" /> | |||
<Compile Include="Tasks\UninstallTask.cs" /> | |||
<Compile Include="WinUtil.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<COMReference Include="IWshRuntimeLibrary"> | |||
<Guid>{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}</Guid> | |||
<VersionMajor>1</VersionMajor> | |||
<VersionMinor>0</VersionMinor> | |||
<Lcid>0</Lcid> | |||
<WrapperTool>tlbimp</WrapperTool> | |||
<Isolated>False</Isolated> | |||
<EmbedInteropTypes>True</EmbedInteropTypes> | |||
</COMReference> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<WCFMetadata Include="Connected Services\" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.PowerShell.5.ReferenceAssemblies" Version="1.1.0" /> | |||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> | |||
<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" /> | |||
<PackageReference Include="System.Runtime" Version="4.3.0" /> | |||
<PackageReference Include="System.Security.Cryptography.Algorithms" Version="4.3.1" /> | |||
<PackageReference Include="System.Security.Cryptography.Encoding" Version="4.3.0" /> | |||
<PackageReference Include="System.Security.Cryptography.Primitives" Version="4.3.0" /> | |||
<PackageReference Include="System.Security.Cryptography.X509Certificates" Version="4.3.0" /> | |||
<PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" /> | |||
<PackageReference Include="TaskScheduler" Version="2.10.1" /> | |||
<PackageReference Include="YamlDotNet" Version="11.2.1" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<EmbeddedResource Include="Properties\UsrClass.dat" /> | |||
</ItemGroup> | |||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
<PropertyGroup> | |||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<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" & cmd /c "exit /b 0")</PostBuildEvent> | |||
</PropertyGroup> | |||
</Project> |
@ -0,0 +1,855 @@ | |||
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 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."); | |||
} | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,31 @@ | |||
| |||
Microsoft Visual Studio Solution File, Format Version 12.00 | |||
# Visual Studio Version 17 | |||
VisualStudioVersion = 17.1.31911.260 | |||
MinimumVisualStudioVersion = 10.0.40219.1 | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrustedUninstaller.CLI", "TrustedUninstaller.CLI\TrustedUninstaller.CLI.csproj", "{476D3799-2CFC-4670-94E5-9AF51B234B07}" | |||
EndProject | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrustedUninstaller.Shared", "TrustedUninstaller.Shared\TrustedUninstaller.Shared.csproj", "{9BDA9D32-E9A1-4DB8-9D90-443792107E28}" | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
Debug|x64 = Debug|x64 | |||
Release|x64 = Release|x64 | |||
EndGlobalSection | |||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||
{476D3799-2CFC-4670-94E5-9AF51B234B07}.Debug|x64.ActiveCfg = Debug|x64 | |||
{476D3799-2CFC-4670-94E5-9AF51B234B07}.Debug|x64.Build.0 = Debug|x64 | |||
{476D3799-2CFC-4670-94E5-9AF51B234B07}.Release|x64.ActiveCfg = Release|x64 | |||
{476D3799-2CFC-4670-94E5-9AF51B234B07}.Release|x64.Build.0 = Release|x64 | |||
{9BDA9D32-E9A1-4DB8-9D90-443792107E28}.Debug|x64.ActiveCfg = Debug|x64 | |||
{9BDA9D32-E9A1-4DB8-9D90-443792107E28}.Debug|x64.Build.0 = Debug|x64 | |||
{9BDA9D32-E9A1-4DB8-9D90-443792107E28}.Release|x64.ActiveCfg = Release|x64 | |||
{9BDA9D32-E9A1-4DB8-9D90-443792107E28}.Release|x64.Build.0 = Release|x64 | |||
EndGlobalSection | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
EndGlobalSection | |||
GlobalSection(ExtensibilityGlobals) = postSolution | |||
SolutionGuid = {FA892C1E-4359-4EA2-896C-89C65B23423A} | |||
EndGlobalSection | |||
EndGlobal |