From 759cd123793a1b4ed12dbe45461d4a0d26e1554d Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Thu, 15 Apr 2021 19:48:36 -0600
Subject: [PATCH 01/43] Reoder Windows functions
---
scripts/wk/os/win.py | 202 ++++++++++++++++++++++---------------------
1 file changed, 102 insertions(+), 100 deletions(-)
diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py
index eadcab71..8f1cc52c 100644
--- a/scripts/wk/os/win.py
+++ b/scripts/wk/os/win.py
@@ -56,7 +56,7 @@ REG_MSISERVER = r'HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot\Network\MSIServ
SLMGR = pathlib.Path(f'{os.environ.get("SYSTEMROOT")}/System32/slmgr.vbs')
-# Functions
+# Activation Functions
def activate_with_bios():
"""Attempt to activate Windows with a key stored in the BIOS."""
# Code borrowed from https://github.com/aeruder/get_win8key
@@ -96,36 +96,6 @@ def activate_with_bios():
raise GenericError('Activation Failed')
-def disable_safemode():
- """Edit BCD to remove safeboot value."""
- cmd = ['bcdedit', '/deletevalue', '{default}', 'safeboot']
- run_program(cmd)
-
-
-def disable_safemode_msi():
- """Disable MSI access under safemode."""
- cmd = ['reg', 'delete', REG_MSISERVER, '/f']
- run_program(cmd)
-
-
-def enable_safemode():
- """Edit BCD to set safeboot as default."""
- cmd = ['bcdedit', '/set', '{default}', 'safeboot', 'network']
- run_program(cmd)
-
-
-def enable_safemode_msi():
- """Enable MSI access under safemode."""
- cmd = ['reg', 'add', REG_MSISERVER, '/f']
- run_program(cmd)
- cmd = [
- 'reg', 'add', REG_MSISERVER, '/ve',
- '/t', 'REG_SZ',
- '/d', 'Service', '/f',
- ]
- run_program(cmd)
-
-
def get_activation_string():
"""Get activation status, returns str."""
cmd = ['cscript', '//nologo', SLMGR, '/xpr']
@@ -144,75 +114,6 @@ def is_activated():
return act_str and 'permanent' in act_str
-def run_chkdsk_offline():
- """Set filesystem 'dirty bit' to force a CHKDSK during startup."""
- cmd = f'fsutil dirty set {os.environ.get("SYSTEMDRIVE")}'
- proc = run_program(cmd.split(), check=False)
-
- # Check result
- if proc.returncode > 0:
- raise GenericError('Failed to set dirty bit.')
-
-
-def run_chkdsk_online():
- """Run CHKDSK in a split window.
-
- NOTE: If run on Windows 8+ online repairs are attempted.
- """
- cmd = ['CHKDSK', os.environ.get('SYSTEMDRIVE', 'C:')]
- if OS_VERSION >= 8:
- cmd.extend(['/scan', '/perf'])
- log_path = format_log_path(log_name='CHKDSK', tool=True)
- err_path = log_path.with_suffix('.err')
-
- # Run scan
- proc = run_program(cmd, check=False)
-
- # Check result
- if proc.returncode == 1:
- raise GenericWarning('Repaired (or manually aborted)')
- if proc.returncode > 1:
- raise GenericError('Issue(s) detected')
-
- # Save output
- os.makedirs(log_path.parent, exist_ok=True)
- with open(log_path, 'w') as _f:
- _f.write(proc.stdout)
- with open(err_path, 'w') as _f:
- _f.write(proc.stderr)
-
-
-def run_sfc_scan():
- """Run SFC and save results."""
- cmd = ['sfc', '/scannow']
- log_path = format_log_path(log_name='SFC', tool=True)
- err_path = log_path.with_suffix('.err')
-
- # Run SFC
- proc = run_program(cmd, check=False, encoding='utf-16')
-
- # Fix paths
- log_path = non_clobber_path(log_path)
- err_path = non_clobber_path(err_path)
-
- # Save output
- os.makedirs(log_path.parent, exist_ok=True)
- with open(log_path, 'w') as _f:
- _f.write(proc.stdout)
- with open(err_path, 'w') as _f:
- _f.write(proc.stderr)
-
- # Check result
- if 'did not find any integrity violations' in proc.stdout:
- pass
- elif 'successfully repaired' in proc.stdout:
- raise GenericWarning('Repaired')
- elif 'found corrupt files' in proc.stdout:
- raise GenericError('Corruption detected')
- else:
- raise OSError
-
-
# Registry Functions
def reg_delete_key(hive, key, recurse=False):
# pylint: disable=raise-missing-from
@@ -407,5 +308,106 @@ def reg_set_value(hive, key, name, data, data_type, option=None):
winreg.SetValue(hive, key, data_type, data)
+# Repair Functions
+def run_chkdsk_offline():
+ """Set filesystem 'dirty bit' to force a CHKDSK during startup."""
+ cmd = f'fsutil dirty set {os.environ.get("SYSTEMDRIVE")}'
+ proc = run_program(cmd.split(), check=False)
+
+ # Check result
+ if proc.returncode > 0:
+ raise GenericError('Failed to set dirty bit.')
+
+
+def run_chkdsk_online():
+ """Run CHKDSK in a split window.
+
+ NOTE: If run on Windows 8+ online repairs are attempted.
+ """
+ cmd = ['CHKDSK', os.environ.get('SYSTEMDRIVE', 'C:')]
+ if OS_VERSION >= 8:
+ cmd.extend(['/scan', '/perf'])
+ log_path = format_log_path(log_name='CHKDSK', tool=True)
+ err_path = log_path.with_suffix('.err')
+
+ # Run scan
+ proc = run_program(cmd, check=False)
+
+ # Check result
+ if proc.returncode == 1:
+ raise GenericWarning('Repaired (or manually aborted)')
+ if proc.returncode > 1:
+ raise GenericError('Issue(s) detected')
+
+ # Save output
+ os.makedirs(log_path.parent, exist_ok=True)
+ with open(log_path, 'w') as _f:
+ _f.write(proc.stdout)
+ with open(err_path, 'w') as _f:
+ _f.write(proc.stderr)
+
+
+def run_sfc_scan():
+ """Run SFC and save results."""
+ cmd = ['sfc', '/scannow']
+ log_path = format_log_path(log_name='SFC', tool=True)
+ err_path = log_path.with_suffix('.err')
+
+ # Run SFC
+ proc = run_program(cmd, check=False, encoding='utf-16')
+
+ # Fix paths
+ log_path = non_clobber_path(log_path)
+ err_path = non_clobber_path(err_path)
+
+ # Save output
+ os.makedirs(log_path.parent, exist_ok=True)
+ with open(log_path, 'w') as _f:
+ _f.write(proc.stdout)
+ with open(err_path, 'w') as _f:
+ _f.write(proc.stderr)
+
+ # Check result
+ if 'did not find any integrity violations' in proc.stdout:
+ pass
+ elif 'successfully repaired' in proc.stdout:
+ raise GenericWarning('Repaired')
+ elif 'found corrupt files' in proc.stdout:
+ raise GenericError('Corruption detected')
+ else:
+ raise OSError
+
+
+# Safe Mode Functions
+def disable_safemode():
+ """Edit BCD to remove safeboot value."""
+ cmd = ['bcdedit', '/deletevalue', '{default}', 'safeboot']
+ run_program(cmd)
+
+
+def disable_safemode_msi():
+ """Disable MSI access under safemode."""
+ cmd = ['reg', 'delete', REG_MSISERVER, '/f']
+ run_program(cmd)
+
+
+def enable_safemode():
+ """Edit BCD to set safeboot as default."""
+ cmd = ['bcdedit', '/set', '{default}', 'safeboot', 'network']
+ run_program(cmd)
+
+
+def enable_safemode_msi():
+ """Enable MSI access under safemode."""
+ cmd = ['reg', 'add', REG_MSISERVER, '/f']
+ run_program(cmd)
+ cmd = [
+ 'reg', 'add', REG_MSISERVER, '/ve',
+ '/t', 'REG_SZ',
+ '/d', 'Service', '/f',
+ ]
+ run_program(cmd)
+
+
if __name__ == '__main__':
print("This file is not meant to be called directly.")
From ed6f188eb2a66c712d94203430aa2352d2ef9fba Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Thu, 15 Apr 2021 20:04:33 -0600
Subject: [PATCH 02/43] Avoid pylint errors under Linux/macOS
---
scripts/wk/os/win.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py
index 8f1cc52c..44e7efb4 100644
--- a/scripts/wk/os/win.py
+++ b/scripts/wk/os/win.py
@@ -5,10 +5,15 @@ import logging
import os
import pathlib
import platform
-import winreg
from contextlib import suppress
+try:
+ import winreg
+except ImportError as err:
+ if platform.system() == 'Windows':
+ raise err
+
from wk.borrowed import acpi
from wk.exe import run_program
from wk.io import non_clobber_path
@@ -126,7 +131,7 @@ def reg_delete_key(hive, key, recurse=False):
# Delete subkeys first
if recurse:
- with suppress(WindowsError), winreg.OpenKey(hive, key) as open_key:
+ with suppress(OSError), winreg.OpenKey(hive, key) as open_key:
while True:
subkey = fr'{key}\{winreg.EnumKey(open_key, 0)}'
reg_delete_key(hive, subkey, recurse=recurse)
From 943c1e11b9574f8b0e38aa96048a5cc4ce6547e7 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Thu, 15 Apr 2021 21:13:28 -0600
Subject: [PATCH 03/43] Retry CHKDSK on failures
Fixes issue #159
---
scripts/wk/os/win.py | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py
index 44e7efb4..63100b7c 100644
--- a/scripts/wk/os/win.py
+++ b/scripts/wk/os/win.py
@@ -334,21 +334,26 @@ def run_chkdsk_online():
cmd.extend(['/scan', '/perf'])
log_path = format_log_path(log_name='CHKDSK', tool=True)
err_path = log_path.with_suffix('.err')
+ retried = False
# Run scan
proc = run_program(cmd, check=False)
+ if proc.returncode > 1:
+ # Try again
+ retried = True
+ proc = run_program(cmd, check=False)
# Check result
- if proc.returncode == 1:
+ if (proc.returncode == 0 and retried) or proc.returncode == 1:
raise GenericWarning('Repaired (or manually aborted)')
if proc.returncode > 1:
raise GenericError('Issue(s) detected')
# Save output
os.makedirs(log_path.parent, exist_ok=True)
- with open(log_path, 'w') as _f:
+ with open(log_path, 'a') as _f:
_f.write(proc.stdout)
- with open(err_path, 'w') as _f:
+ with open(err_path, 'a') as _f:
_f.write(proc.stderr)
From 47b49077da5142b9a82bc514dc10a11bc8357306 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Thu, 15 Apr 2021 21:16:02 -0600
Subject: [PATCH 04/43] Show CHKDSK progress in separate pane under ConEmu
---
scripts/wk/exe.py | 7 ++++++-
scripts/wk/os/win.py | 34 ++++++++++++++++------------------
2 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/scripts/wk/exe.py b/scripts/wk/exe.py
index 87b90935..82e9dff3 100644
--- a/scripts/wk/exe.py
+++ b/scripts/wk/exe.py
@@ -130,7 +130,7 @@ def get_json_from_command(cmd, check=True, encoding='utf-8', errors='ignore'):
return json_data
-def get_procs(name, exact=True):
+def get_procs(name, exact=True, try_again=True):
"""Get process object(s) based on name, returns list of proc objects."""
LOG.debug('name: %s, exact: %s', name, exact)
processes = []
@@ -141,6 +141,11 @@ def get_procs(name, exact=True):
if re.search(regex, proc.name(), re.IGNORECASE):
processes.append(proc)
+ # Try again?
+ if not processes and try_again:
+ time.sleep(1)
+ processes = get_procs(name, exact, try_again=False)
+
# Done
return processes
diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py
index 63100b7c..919a4406 100644
--- a/scripts/wk/os/win.py
+++ b/scripts/wk/os/win.py
@@ -15,7 +15,7 @@ except ImportError as err:
raise err
from wk.borrowed import acpi
-from wk.exe import run_program
+from wk.exe import get_procs, run_program
from wk.io import non_clobber_path
from wk.log import format_log_path
from wk.std import GenericError, GenericWarning, sleep
@@ -23,6 +23,7 @@ from wk.std import GenericError, GenericWarning, sleep
# STATIC VARIABLES
LOG = logging.getLogger(__name__)
+CONEMU = 'ConEmuPID' in os.environ
KNOWN_DATA_TYPES = {
'BINARY': winreg.REG_BINARY,
'DWORD': winreg.REG_DWORD,
@@ -316,8 +317,8 @@ def reg_set_value(hive, key, name, data, data_type, option=None):
# Repair Functions
def run_chkdsk_offline():
"""Set filesystem 'dirty bit' to force a CHKDSK during startup."""
- cmd = f'fsutil dirty set {os.environ.get("SYSTEMDRIVE")}'
- proc = run_program(cmd.split(), check=False)
+ cmd = ['fsutil', 'dirty', 'set', os.environ.get('SYSTEMDRIVE', 'C:')]
+ proc = run_program(cmd, check=False)
# Check result
if proc.returncode > 0:
@@ -325,37 +326,34 @@ def run_chkdsk_offline():
def run_chkdsk_online():
- """Run CHKDSK in a split window.
+ """Run CHKDSK.
NOTE: If run on Windows 8+ online repairs are attempted.
"""
cmd = ['CHKDSK', os.environ.get('SYSTEMDRIVE', 'C:')]
if OS_VERSION >= 8:
cmd.extend(['/scan', '/perf'])
- log_path = format_log_path(log_name='CHKDSK', tool=True)
- err_path = log_path.with_suffix('.err')
+ if CONEMU:
+ cmd.extend(['-new_console:n', '-new_console:s33V'])
retried = False
# Run scan
- proc = run_program(cmd, check=False)
- if proc.returncode > 1:
+ run_program(cmd, check=False)
+ proc = get_procs('chkdsk.exe')[0]
+ return_code = proc.wait()
+ if return_code > 1:
# Try again
retried = True
- proc = run_program(cmd, check=False)
+ run_program(cmd, check=False)
+ proc = get_procs('chkdsk.exe')[0]
+ return_code = proc.wait()
# Check result
- if (proc.returncode == 0 and retried) or proc.returncode == 1:
+ if (return_code == 0 and retried) or return_code == 1:
raise GenericWarning('Repaired (or manually aborted)')
- if proc.returncode > 1:
+ if return_code > 1:
raise GenericError('Issue(s) detected')
- # Save output
- os.makedirs(log_path.parent, exist_ok=True)
- with open(log_path, 'a') as _f:
- _f.write(proc.stdout)
- with open(err_path, 'a') as _f:
- _f.write(proc.stderr)
-
def run_sfc_scan():
"""Run SFC and save results."""
From e088f705ba9324c6c817691831ebbc236b7d4e1a Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Thu, 15 Apr 2021 23:33:11 -0600
Subject: [PATCH 05/43] Add run_dism()
---
scripts/wk/exe.py | 4 ++--
scripts/wk/os/win.py | 37 ++++++++++++++++++++++++++++++++++++-
2 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/scripts/wk/exe.py b/scripts/wk/exe.py
index 82e9dff3..5edeb041 100644
--- a/scripts/wk/exe.py
+++ b/scripts/wk/exe.py
@@ -247,10 +247,10 @@ def wait_for_procs(name, exact=True, timeout=None):
"""Wait for all process matching name."""
LOG.debug('name: %s, exact: %s, timeout: %s', name, exact, timeout)
target_procs = get_procs(name, exact=exact)
- results = psutil.wait_procs(target_procs, timeout=timeout)
+ procs = psutil.wait_procs(target_procs, timeout=timeout)
# Raise exception if necessary
- if results[1]: # Alive processes
+ if procs[1]: # Alive processes
raise psutil.TimeoutExpired(name=name, seconds=timeout)
diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py
index 919a4406..415679e5 100644
--- a/scripts/wk/os/win.py
+++ b/scripts/wk/os/win.py
@@ -15,7 +15,7 @@ except ImportError as err:
raise err
from wk.borrowed import acpi
-from wk.exe import get_procs, run_program
+from wk.exe import get_procs, run_program, wait_for_procs
from wk.io import non_clobber_path
from wk.log import format_log_path
from wk.std import GenericError, GenericWarning, sleep
@@ -355,6 +355,41 @@ def run_chkdsk_online():
raise GenericError('Issue(s) detected')
+def run_dism(repair=True):
+ """Run DISM to either scan or repair component store health."""
+ conemu_args = ['-new_console:n', '-new_console:s33V'] if CONEMU else []
+
+ # Bail early
+ if OS_VERSION < 8:
+ raise GenericWarning('Unsupported OS')
+
+ # Run (repair) scan
+ log_path = format_log_path(
+ log_name=f'DISM_{"Restore" if repair else "Scan"}Health', tool=True,
+ )
+ cmd = [
+ 'DISM', '/Online', '/Cleanup-Image',
+ '/RestoreHealth' if repair else '/ScanHealth',
+ f'/LogPath:{log_path}',
+ *conemu_args,
+ ]
+ run_program(cmd, check=False, pipe=False)
+ wait_for_procs('dism.exe')
+
+ # Run check health
+ log_path = format_log_path(log_name='DISM_CheckHealth.log', tool=True)
+ cmd = [
+ 'DISM', '/Online', '/Cleanup-Image',
+ '/CheckHealth',
+ f'/LogPath:{log_path}',
+ ]
+ proc = run_program(cmd, check=False)
+
+ # Check for errors
+ if 'no component store corruption detected' not in proc.stdout.lower():
+ raise GenericError('Issue(s) detected')
+
+
def run_sfc_scan():
"""Run SFC and save results."""
cmd = ['sfc', '/scannow']
From 7064472e0bcc0ba6aa1fa192c05b0eed1ca12021 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Fri, 16 Apr 2021 03:33:47 -0600
Subject: [PATCH 06/43] Fix SFC scan
---
scripts/wk/os/win.py | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py
index 415679e5..27bab457 100644
--- a/scripts/wk/os/win.py
+++ b/scripts/wk/os/win.py
@@ -16,7 +16,6 @@ except ImportError as err:
from wk.borrowed import acpi
from wk.exe import get_procs, run_program, wait_for_procs
-from wk.io import non_clobber_path
from wk.log import format_log_path
from wk.std import GenericError, GenericWarning, sleep
@@ -397,17 +396,13 @@ def run_sfc_scan():
err_path = log_path.with_suffix('.err')
# Run SFC
- proc = run_program(cmd, check=False, encoding='utf-16')
-
- # Fix paths
- log_path = non_clobber_path(log_path)
- err_path = non_clobber_path(err_path)
+ proc = run_program(cmd, check=False, encoding='utf-16le')
# Save output
os.makedirs(log_path.parent, exist_ok=True)
- with open(log_path, 'w') as _f:
+ with open(log_path, 'a') as _f:
_f.write(proc.stdout)
- with open(err_path, 'w') as _f:
+ with open(err_path, 'a') as _f:
_f.write(proc.stderr)
# Check result
From 9351b597c21af3e720ef91707f31b9e63b30d47d Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 17 Apr 2021 11:38:06 -0600
Subject: [PATCH 07/43] Avoid potential crash in run_chkdsk_online()
---
scripts/wk/os/win.py | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py
index 27bab457..a4ad5495 100644
--- a/scripts/wk/os/win.py
+++ b/scripts/wk/os/win.py
@@ -338,16 +338,26 @@ def run_chkdsk_online():
# Run scan
run_program(cmd, check=False)
- proc = get_procs('chkdsk.exe')[0]
- return_code = proc.wait()
+ try:
+ proc = get_procs('chkdsk.exe')[0]
+ return_code = proc.wait()
+ except IndexError:
+ # Failed to get CHKDSK process, set return_code to force a retry
+ return_code = 255
if return_code > 1:
# Try again
retried = True
run_program(cmd, check=False)
- proc = get_procs('chkdsk.exe')[0]
- return_code = proc.wait()
+ try:
+ proc = get_procs('chkdsk.exe')[0]
+ return_code = proc.wait()
+ except IndexError:
+ # Failed to get CHKDSK process
+ return_code = -1
# Check result
+ if return_code == -1:
+ raise GenericError('Failed to find CHKDSK process.')
if (return_code == 0 and retried) or return_code == 1:
raise GenericWarning('Repaired (or manually aborted)')
if return_code > 1:
From fd7a8c40665b1c4f6c7f73c6725e04b3f5a6c729 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 17 Apr 2021 12:13:24 -0600
Subject: [PATCH 08/43] Add Windows service functions
---
scripts/wk/os/win.py | 71 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py
index a4ad5495..f4a03f30 100644
--- a/scripts/wk/os/win.py
+++ b/scripts/wk/os/win.py
@@ -7,6 +7,7 @@ import pathlib
import platform
from contextlib import suppress
+import psutil
try:
import winreg
@@ -457,5 +458,75 @@ def enable_safemode_msi():
run_program(cmd)
+# Service Functions
+def disable_service(service_name):
+ """Set service startup to disabled."""
+ cmd = ['sc', 'config', service_name, 'start=', 'disabled']
+ run_program(cmd, check=False)
+
+ # Verify service was disabled
+ if get_service_start_type(service_name) != 'disabled':
+ raise GenericError(f'Failed to disable service {service_name}')
+
+
+def enable_service(service_name, start_type='auto'):
+ """Enable service by setting start type."""
+ cmd = ['sc', 'config', service_name, 'start=', start_type]
+ psutil_type = 'automatic'
+ if start_type == 'demand':
+ psutil_type = 'manual'
+
+ # Enable service
+ run_program(cmd, check=False)
+
+ # Verify service was enabled
+ if get_service_start_type(service_name) != psutil_type:
+ raise GenericError(f'Failed to enable service {service_name}')
+
+
+def get_service_status(service_name):
+ """Get service status using psutil, returns str."""
+ status = 'unknown'
+ try:
+ service = psutil.win_service_get(service_name)
+ status = service.status()
+ except psutil.NoSuchProcess:
+ status = 'missing?'
+
+ return status
+
+
+def get_service_start_type(service_name):
+ """Get service startup type using psutil, returns str."""
+ start_type = 'unknown'
+ try:
+ service = psutil.win_service_get(service_name)
+ start_type = service.start_type()
+ except psutil.NoSuchProcess:
+ start_type = 'missing?'
+
+ return start_type
+
+
+def start_service(service_name):
+ """Stop service."""
+ cmd = ['net', 'start', service_name]
+ run_program(cmd, check=False)
+
+ # Verify service was started
+ if not get_service_status(service_name) in ('running', 'start_pending'):
+ raise GenericError(f'Failed to start service {service_name}')
+
+
+def stop_service(service_name):
+ """Stop service."""
+ cmd = ['net', 'stop', service_name]
+ run_program(cmd, check=False)
+
+ # Verify service was stopped
+ if not get_service_status(service_name) == 'stopped':
+ raise GenericError(f'Failed to stop service {service_name}')
+
+
if __name__ == '__main__':
print("This file is not meant to be called directly.")
From 65cb8481bc9ec3db79759f7ccfbc47dc67daef44 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 17 Apr 2021 13:26:21 -0600
Subject: [PATCH 09/43] Add wk.repairs section
---
scripts/auto_repairs.py | 18 +++
scripts/wk/__init__.py | 1 +
scripts/wk/os/win.py | 116 +-------------
scripts/wk/repairs/__init__.py | 7 +
scripts/wk/repairs/win.py | 277 +++++++++++++++++++++++++++++++++
5 files changed, 304 insertions(+), 115 deletions(-)
create mode 100644 scripts/auto_repairs.py
create mode 100644 scripts/wk/repairs/__init__.py
create mode 100644 scripts/wk/repairs/win.py
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
new file mode 100644
index 00000000..4409e3ab
--- /dev/null
+++ b/scripts/auto_repairs.py
@@ -0,0 +1,18 @@
+"""Wizard Kit: Auto-Repair Tool"""
+# vim: sts=2 sw=2 ts=2
+
+import os
+import sys
+
+os.chdir(os.path.dirname(os.path.realpath(__file__)))
+sys.path.append(os.getcwd())
+import wk
+
+
+if __name__ == '__main__':
+ try:
+ wk.repairs.win.run_auto_repair()
+ except SystemExit:
+ raise
+ except: #pylint: disable=bare-except
+ wk.std.major_exception()
diff --git a/scripts/wk/__init__.py b/scripts/wk/__init__.py
index b6a11b56..25620f35 100644
--- a/scripts/wk/__init__.py
+++ b/scripts/wk/__init__.py
@@ -13,6 +13,7 @@ from wk import kit
from wk import log
from wk import net
from wk import os
+from wk import repairs
from wk import std
from wk import sw
from wk import tmux
diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py
index f4a03f30..20ee4917 100644
--- a/scripts/wk/os/win.py
+++ b/scripts/wk/os/win.py
@@ -16,8 +16,7 @@ except ImportError as err:
raise err
from wk.borrowed import acpi
-from wk.exe import get_procs, run_program, wait_for_procs
-from wk.log import format_log_path
+from wk.exe import run_program
from wk.std import GenericError, GenericWarning, sleep
@@ -314,119 +313,6 @@ def reg_set_value(hive, key, name, data, data_type, option=None):
winreg.SetValue(hive, key, data_type, data)
-# Repair Functions
-def run_chkdsk_offline():
- """Set filesystem 'dirty bit' to force a CHKDSK during startup."""
- cmd = ['fsutil', 'dirty', 'set', os.environ.get('SYSTEMDRIVE', 'C:')]
- proc = run_program(cmd, check=False)
-
- # Check result
- if proc.returncode > 0:
- raise GenericError('Failed to set dirty bit.')
-
-
-def run_chkdsk_online():
- """Run CHKDSK.
-
- NOTE: If run on Windows 8+ online repairs are attempted.
- """
- cmd = ['CHKDSK', os.environ.get('SYSTEMDRIVE', 'C:')]
- if OS_VERSION >= 8:
- cmd.extend(['/scan', '/perf'])
- if CONEMU:
- cmd.extend(['-new_console:n', '-new_console:s33V'])
- retried = False
-
- # Run scan
- run_program(cmd, check=False)
- try:
- proc = get_procs('chkdsk.exe')[0]
- return_code = proc.wait()
- except IndexError:
- # Failed to get CHKDSK process, set return_code to force a retry
- return_code = 255
- if return_code > 1:
- # Try again
- retried = True
- run_program(cmd, check=False)
- try:
- proc = get_procs('chkdsk.exe')[0]
- return_code = proc.wait()
- except IndexError:
- # Failed to get CHKDSK process
- return_code = -1
-
- # Check result
- if return_code == -1:
- raise GenericError('Failed to find CHKDSK process.')
- if (return_code == 0 and retried) or return_code == 1:
- raise GenericWarning('Repaired (or manually aborted)')
- if return_code > 1:
- raise GenericError('Issue(s) detected')
-
-
-def run_dism(repair=True):
- """Run DISM to either scan or repair component store health."""
- conemu_args = ['-new_console:n', '-new_console:s33V'] if CONEMU else []
-
- # Bail early
- if OS_VERSION < 8:
- raise GenericWarning('Unsupported OS')
-
- # Run (repair) scan
- log_path = format_log_path(
- log_name=f'DISM_{"Restore" if repair else "Scan"}Health', tool=True,
- )
- cmd = [
- 'DISM', '/Online', '/Cleanup-Image',
- '/RestoreHealth' if repair else '/ScanHealth',
- f'/LogPath:{log_path}',
- *conemu_args,
- ]
- run_program(cmd, check=False, pipe=False)
- wait_for_procs('dism.exe')
-
- # Run check health
- log_path = format_log_path(log_name='DISM_CheckHealth.log', tool=True)
- cmd = [
- 'DISM', '/Online', '/Cleanup-Image',
- '/CheckHealth',
- f'/LogPath:{log_path}',
- ]
- proc = run_program(cmd, check=False)
-
- # Check for errors
- if 'no component store corruption detected' not in proc.stdout.lower():
- raise GenericError('Issue(s) detected')
-
-
-def run_sfc_scan():
- """Run SFC and save results."""
- cmd = ['sfc', '/scannow']
- log_path = format_log_path(log_name='SFC', tool=True)
- err_path = log_path.with_suffix('.err')
-
- # Run SFC
- proc = run_program(cmd, check=False, encoding='utf-16le')
-
- # Save output
- os.makedirs(log_path.parent, exist_ok=True)
- with open(log_path, 'a') as _f:
- _f.write(proc.stdout)
- with open(err_path, 'a') as _f:
- _f.write(proc.stderr)
-
- # Check result
- if 'did not find any integrity violations' in proc.stdout:
- pass
- elif 'successfully repaired' in proc.stdout:
- raise GenericWarning('Repaired')
- elif 'found corrupt files' in proc.stdout:
- raise GenericError('Corruption detected')
- else:
- raise OSError
-
-
# Safe Mode Functions
def disable_safemode():
"""Edit BCD to remove safeboot value."""
diff --git a/scripts/wk/repairs/__init__.py b/scripts/wk/repairs/__init__.py
new file mode 100644
index 00000000..cbdf27e4
--- /dev/null
+++ b/scripts/wk/repairs/__init__.py
@@ -0,0 +1,7 @@
+"""WizardKit: repairs module init"""
+# vim: sts=2 sw=2 ts=2
+
+import platform
+
+if platform.system() == 'Windows':
+ from wk.repairs import win
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
new file mode 100644
index 00000000..8abf080a
--- /dev/null
+++ b/scripts/wk/repairs/win.py
@@ -0,0 +1,277 @@
+"""WizardKit: Repairs - Windows"""
+# vim: sts=2 sw=2 ts=2
+
+import atexit
+import logging
+import os
+import platform
+import sys
+
+from wk.cfg.main import KIT_NAME_FULL
+from wk.exe import get_procs, run_program, popen_program, wait_for_procs
+from wk.log import format_log_path, update_log_path
+from wk.os.win import * # pylint: disable=wildcard-import
+from wk.std import (
+ GenericError, GenericWarning, TryAndPrint,
+ abort,
+ clear_screen, print_info,
+ pause, sleep,
+ set_title,
+ )
+
+
+# STATIC VARIABLES
+LOG = logging.getLogger(__name__)
+AUTO_REPAIR_DELAY_IN_SECONDS = 30
+AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\AutoRepair'
+CONEMU = 'ConEmuPID' in os.environ
+OS_VERSION = float(platform.win32_ver()[0])
+TRY_PRINT = TryAndPrint()
+
+
+# AutoRepair Functions
+def end_session():
+ """End AutoRepair session."""
+ print_info('Ending repair session')
+ reg_delete_value('HKCU', AUTO_REPAIR_KEY, 'SessionStarted')
+
+ # Remove logon task
+ cmd = [
+ 'schtasks', '/delete', '/f',
+ '/tn', f'{KIT_NAME_FULL}-AutoRepair',
+ ]
+ run_program(cmd)
+
+ # Disable Autologon
+ # TODO: Run Autologon
+ reg_set_value(
+ 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
+ 'AutoAdminLogon', '0', 'SZ',
+ )
+ reg_delete_value(
+ 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
+ 'DefaultUserName',
+ )
+ reg_delete_value(
+ 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
+ 'DefaultDomainName',
+ )
+
+
+def init():
+ """Initialize AutoRepair."""
+ session_started = False
+ try:
+ session_started = reg_read_value('HKCU', AUTO_REPAIR_KEY, 'SessionStarted')
+ except FileNotFoundError:
+ pass
+
+ # Start or resume a repair session
+ if not session_started:
+ init_run()
+ init_session()
+ else:
+ print_info('Resuming session, press CTRL+c to cancel')
+ try:
+ for _x in range(AUTO_REPAIR_DELAY_IN_SECONDS, 0, -1):
+ print(f' {_x} second{"" if _x==1 else "s"} remaining... \r', end='')
+ sleep(1)
+ except KeyboardInterrupt:
+ abort()
+ print('')
+ init_run()
+
+ # Done
+ return session_started
+
+
+def init_run():
+ """Initialize AutoRepair Run."""
+ atexit.register(start_explorer)
+ # TODO: Sync Clock
+ kill_explorer()
+ # TODO: RKill
+
+
+def init_session():
+ """Initialize AutoRepair session."""
+ print_info('Starting repair session')
+ reg_set_value('HKCU', AUTO_REPAIR_KEY, 'SessionStarted', 1, 'DWORD')
+
+ # Create logon task for AutoRepair
+ cmd = [
+ 'schtasks', '/create', '/f',
+ '/sc', 'ONLOGON',
+ '/tn', f'{KIT_NAME_FULL}-AutoRepair',
+ '/rl', 'HIGHEST',
+ '/tr', fr'C:\Windows\System32\cmd.exe "/C {sys.executable} {sys.argv[0]}"',
+ ]
+ run_program(cmd)
+
+ # One-time tasks
+ # TODO: Backup Registry
+ # TODO: Enable and create restore point
+ # TODO: Run Autologon
+ # TODO: Disable Windows updates
+ # TODO: Reset Windows updates
+ reboot()
+
+
+def run_auto_repair():
+ """Run AutoRepair."""
+ update_log_path(dest_name='Auto-Repair Tool', timestamp=True)
+ title = f'{KIT_NAME_FULL}: Auto-Repair Tool'
+ clear_screen()
+ set_title(title)
+ print_info(title)
+ print('')
+
+ # Show Menu on first run
+ session_started = init()
+ if not session_started:
+ # TODO: Show Menu
+ pass
+
+ # Run repairs
+ # TODO: Run repairs
+ end_session()
+
+ # Done
+ print_info('Done')
+ pause('Press Enter to exit...')
+
+
+# OS Built-in Functions
+def kill_explorer():
+ """Kill all Explorer processes."""
+ cmd = ['taskkill', '/im', 'explorer.exe', '/f']
+ run_program(cmd, check=False)
+
+
+def reboot():
+ """Reboot the system."""
+ atexit.unregister(start_explorer)
+ cmd = ['shutdown', '-r', '-t', '0']
+ run_program(cmd, check=False)
+ raise SystemExit
+
+
+def run_chkdsk_offline():
+ """Set filesystem 'dirty bit' to force a CHKDSK during startup."""
+ cmd = ['fsutil', 'dirty', 'set', os.environ.get('SYSTEMDRIVE', 'C:')]
+ proc = run_program(cmd, check=False)
+
+ # Check result
+ if proc.returncode > 0:
+ raise GenericError('Failed to set dirty bit.')
+
+
+def run_chkdsk_online():
+ """Run CHKDSK.
+
+ NOTE: If run on Windows 8+ online repairs are attempted.
+ """
+ cmd = ['CHKDSK', os.environ.get('SYSTEMDRIVE', 'C:')]
+ if OS_VERSION >= 8:
+ cmd.extend(['/scan', '/perf'])
+ if CONEMU:
+ cmd.extend(['-new_console:n', '-new_console:s33V'])
+ retried = False
+
+ # Run scan
+ run_program(cmd, check=False)
+ try:
+ proc = get_procs('chkdsk.exe')[0]
+ return_code = proc.wait()
+ except IndexError:
+ # Failed to get CHKDSK process, set return_code to force a retry
+ return_code = 255
+ if return_code > 1:
+ # Try again
+ retried = True
+ run_program(cmd, check=False)
+ try:
+ proc = get_procs('chkdsk.exe')[0]
+ return_code = proc.wait()
+ except IndexError:
+ # Failed to get CHKDSK process
+ return_code = -1
+
+ # Check result
+ if return_code == -1:
+ raise GenericError('Failed to find CHKDSK process.')
+ if (return_code == 0 and retried) or return_code == 1:
+ raise GenericWarning('Repaired (or manually aborted)')
+ if return_code > 1:
+ raise GenericError('Issue(s) detected')
+
+
+def run_dism(repair=True):
+ """Run DISM to either scan or repair component store health."""
+ conemu_args = ['-new_console:n', '-new_console:s33V'] if CONEMU else []
+
+ # Bail early
+ if OS_VERSION < 8:
+ raise GenericWarning('Unsupported OS')
+
+ # Run (repair) scan
+ log_path = format_log_path(
+ log_name=f'DISM_{"Restore" if repair else "Scan"}Health', tool=True,
+ )
+ cmd = [
+ 'DISM', '/Online', '/Cleanup-Image',
+ '/RestoreHealth' if repair else '/ScanHealth',
+ f'/LogPath:{log_path}',
+ *conemu_args,
+ ]
+ run_program(cmd, check=False, pipe=False)
+ wait_for_procs('dism.exe')
+
+ # Run check health
+ log_path = format_log_path(log_name='DISM_CheckHealth.log', tool=True)
+ cmd = [
+ 'DISM', '/Online', '/Cleanup-Image',
+ '/CheckHealth',
+ f'/LogPath:{log_path}',
+ ]
+ proc = run_program(cmd, check=False)
+
+ # Check for errors
+ if 'no component store corruption detected' not in proc.stdout.lower():
+ raise GenericError('Issue(s) detected')
+
+
+def run_sfc_scan():
+ """Run SFC and save results."""
+ cmd = ['sfc', '/scannow']
+ log_path = format_log_path(log_name='SFC', tool=True)
+ err_path = log_path.with_suffix('.err')
+
+ # Run SFC
+ proc = run_program(cmd, check=False, encoding='utf-16le')
+
+ # Save output
+ os.makedirs(log_path.parent, exist_ok=True)
+ with open(log_path, 'a') as _f:
+ _f.write(proc.stdout)
+ with open(err_path, 'a') as _f:
+ _f.write(proc.stderr)
+
+ # Check result
+ if 'did not find any integrity violations' in proc.stdout:
+ pass
+ elif 'successfully repaired' in proc.stdout:
+ raise GenericWarning('Repaired')
+ elif 'found corrupt files' in proc.stdout:
+ raise GenericError('Corruption detected')
+ else:
+ raise OSError
+
+
+def start_explorer():
+ """Start Explorer."""
+ popen_program(['explorer.exe'])
+
+
+if __name__ == '__main__':
+ print("This file is not meant to be called directly.")
From b8335188ce4b09caffd0e0d446c4d5213787fc19 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 17 Apr 2021 17:49:04 -0600
Subject: [PATCH 10/43] Add wk.kit.tools
For code related to downloading, finding, and running tools on the kit.
---
scripts/wk/kit/__init__.py | 2 +
scripts/wk/kit/tools.py | 79 ++++++++++++++++++++++++++++++++++++++
scripts/wk/repairs/win.py | 25 +++++++-----
3 files changed, 96 insertions(+), 10 deletions(-)
create mode 100644 scripts/wk/kit/tools.py
diff --git a/scripts/wk/kit/__init__.py b/scripts/wk/kit/__init__.py
index 0869d9c6..4641fae7 100644
--- a/scripts/wk/kit/__init__.py
+++ b/scripts/wk/kit/__init__.py
@@ -3,5 +3,7 @@
import platform
+from wk.kit import tools
+
if platform.system() == 'Linux':
from wk.kit import ufd
diff --git a/scripts/wk/kit/tools.py b/scripts/wk/kit/tools.py
new file mode 100644
index 00000000..9c7ce4ba
--- /dev/null
+++ b/scripts/wk/kit/tools.py
@@ -0,0 +1,79 @@
+"""WizardKit: Tool Functions"""
+# vim: sts=2 sw=2 ts=2
+
+import pathlib
+import sys
+
+from wk.exe import popen_program, run_program
+
+
+# STATIC VARIABLES
+ARCH = '64' if sys.maxsize > 2**32 else '32'
+
+
+# "GLOBAL" VARIABLES
+CACHED_DIRS = {}
+
+
+# Functions
+def find_kit_dir(name=None):
+ """Find folder in kit, returns pathlib.Path.
+
+ Search is performed in the script's path and then recursively upwards.
+ If name is given then search for that instead."""
+ cur_path = pathlib.Path(__file__).resolve().parent
+ search = name if name else '.bin'
+
+ # Search
+ if name in CACHED_DIRS:
+ return CACHED_DIRS[name]
+ while not cur_path.match(cur_path.anchor):
+ if cur_path.joinpath(search).exists():
+ break
+ cur_path = cur_path.parent
+
+ # Check
+ if cur_path.match(cur_path.anchor):
+ raise FileNotFoundError(f'Failed to find kit dir, {name=}')
+ if name:
+ cur_path = cur_path.joinpath(name)
+
+ # Done
+ CACHED_DIRS[name] = cur_path
+ return cur_path
+
+
+def get_tool_path(folder, name):
+ """Get tool path, returns pathlib.Path"""
+ bin_dir = find_kit_dir('.bin')
+
+ # "Search"
+ tool_path = bin_dir.joinpath(f'{folder}/{name}{ARCH}.exe')
+ if not tool_path.exists():
+ tool_path = tool_path.with_stem(name)
+
+ # Missing?
+ if not tool_path.exists():
+ raise FileNotFoundError(f'Failed to find tool, {folder=}, {name=}')
+
+ # Done
+ return tool_path
+
+
+def run_tool(folder, name, *args, popen=False):
+ """Run tool from kit."""
+ cmd = [get_tool_path(folder, name), *args]
+ proc = None
+
+ # Run
+ if popen:
+ proc = popen_program(cmd)
+ else:
+ proc = run_program(cmd, check=False)
+
+ # Done
+ return proc
+
+
+if __name__ == '__main__':
+ print("This file is not meant to be called directly.")
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 8abf080a..6cba5133 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -7,16 +7,21 @@ import os
import platform
import sys
-from wk.cfg.main import KIT_NAME_FULL
-from wk.exe import get_procs, run_program, popen_program, wait_for_procs
-from wk.log import format_log_path, update_log_path
-from wk.os.win import * # pylint: disable=wildcard-import
-from wk.std import (
- GenericError, GenericWarning, TryAndPrint,
+from wk.cfg.main import KIT_NAME_FULL
+from wk.exe import get_procs, run_program, popen_program, wait_for_procs
+from wk.kit.tools import run_tool
+from wk.log import format_log_path, update_log_path
+from wk.os.win import reg_delete_value, reg_read_value, reg_set_value
+from wk.std import (
+ GenericError,
+ GenericWarning,
+ TryAndPrint,
abort,
- clear_screen, print_info,
- pause, sleep,
+ clear_screen,
+ pause,
+ print_info,
set_title,
+ sleep,
)
@@ -43,7 +48,7 @@ def end_session():
run_program(cmd)
# Disable Autologon
- # TODO: Run Autologon
+ run_tool('Sysinternals', 'Autologon')
reg_set_value(
'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
'AutoAdminLogon', '0', 'SZ',
@@ -111,7 +116,7 @@ def init_session():
# One-time tasks
# TODO: Backup Registry
# TODO: Enable and create restore point
- # TODO: Run Autologon
+ run_tool('Sysinternals', 'Autologon')
# TODO: Disable Windows updates
# TODO: Reset Windows updates
reboot()
From cb825e37ba042eb5280d456026055d2a81f24b34 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Mon, 19 Apr 2021 01:04:18 -0600
Subject: [PATCH 11/43] Add support to run tools from .cbin or online
---
scripts/wk/cfg/__init__.py | 1 +
scripts/wk/cfg/tools.py | 72 +++++++++++++++++++++++++++++++
scripts/wk/kit/tools.py | 87 +++++++++++++++++++++++++++++++++++---
3 files changed, 155 insertions(+), 5 deletions(-)
create mode 100644 scripts/wk/cfg/tools.py
diff --git a/scripts/wk/cfg/__init__.py b/scripts/wk/cfg/__init__.py
index 23ca608f..d35b8874 100644
--- a/scripts/wk/cfg/__init__.py
+++ b/scripts/wk/cfg/__init__.py
@@ -5,4 +5,5 @@ from wk.cfg import hw
from wk.cfg import log
from wk.cfg import main
from wk.cfg import net
+from wk.cfg import tools
from wk.cfg import ufd
diff --git a/scripts/wk/cfg/tools.py b/scripts/wk/cfg/tools.py
new file mode 100644
index 00000000..e32d3c2e
--- /dev/null
+++ b/scripts/wk/cfg/tools.py
@@ -0,0 +1,72 @@
+"""WizardKit: Config - Tools"""
+# pylint: disable=line-too-long
+# vim: sts=2 sw=2 ts=2
+
+
+# Download frequency in days
+DOWNLOAD_FREQUENCY = 7
+
+
+# Sources
+SOURCES = {
+ 'Adobe Reader DC': 'https://ardownload2.adobe.com/pub/adobe/reader/win/AcrobatDC/2100120145/AcroRdrDC2100120145_en_US.exe',
+ 'AdwCleaner': 'https://downloads.malwarebytes.com/file/adwcleaner',
+ 'AIDA64': 'https://download.aida64.com/aida64engineer633.zip',
+ 'aria2': 'https://github.com/aria2/aria2/releases/download/release-1.35.0/aria2-1.35.0-win-32bit-build1.zip',
+ 'Autoruns': 'https://download.sysinternals.com/files/Autoruns.zip',
+ 'BleachBit': 'https://download.bleachbit.org/BleachBit-4.2.0-portable.zip',
+ 'BlueScreenView32': 'http://www.nirsoft.net/utils/bluescreenview.zip',
+ 'BlueScreenView64': 'http://www.nirsoft.net/utils/bluescreenview-x64.zip',
+ 'Caffeine': 'http://www.zhornsoftware.co.uk/caffeine/caffeine.zip',
+ 'ClassicStartSkin': 'http://www.classicshell.net/forum/download/file.php?id=3001&sid=9a195960d98fd754867dcb63d9315335',
+ 'Du': 'https://download.sysinternals.com/files/DU.zip',
+ 'ERUNT': 'http://www.aumha.org/downloads/erunt.zip',
+ 'ESET AVRemover32': 'https://download.eset.com/com/eset/tools/installers/av_remover/latest/avremover_nt32_enu.exe',
+ 'ESET AVRemover64': 'https://download.eset.com/com/eset/tools/installers/av_remover/latest/avremover_nt64_enu.exe',
+ 'ESET NOD32 AV': 'https://download.eset.com/com/eset/apps/home/eav/windows/latest/eav_nt64.exe',
+ 'ESET Online Scanner': 'https://download.eset.com/com/eset/tools/online_scanner/latest/esetonlinescanner_enu.exe',
+ 'Everything32': 'https://www.voidtools.com/Everything-1.4.1.1005.x86.en-US.zip',
+ 'Everything64': 'https://www.voidtools.com/Everything-1.4.1.1005.x64.en-US.zip',
+ 'FastCopy': 'https://ftp.vector.co.jp/73/10/2323/FastCopy392_installer.exe',
+ 'FurMark': 'https://geeks3d.com/dl/get/569',
+ 'Firefox uBO': 'https://addons.mozilla.org/firefox/downloads/file/3740966/ublock_origin-1.34.0-an+fx.xpi',
+ 'HitmanPro32': 'https://dl.surfright.nl/HitmanPro.exe',
+ 'HitmanPro64': 'https://dl.surfright.nl/HitmanPro_x64.exe',
+ 'HWiNFO': 'https://files1.majorgeeks.com/c8a055180587599139f8f454712dcc618cd1740e/systeminfo/hwi_702.zip',
+ 'Intel SSD Toolbox': r'https://downloadmirror.intel.com/28593/eng/Intel%20SSD%20Toolbox%20-%20v3.5.9.exe',
+ 'IOBit_Uninstaller': r'https://portableapps.com/redirect/?a=IObitUninstallerPortable&s=s&d=pa&f=IObitUninstallerPortable_7.5.0.7.paf.exe',
+ 'KVRT': 'https://devbuilds.s.kaspersky-labs.com/devbuilds/KVRT/latest/full/KVRT.exe',
+ 'LibreOffice': 'https://download.documentfoundation.org/libreoffice/stable/7.1.2/win/x86_64/LibreOffice_7.1.2_Win_x64.msi',
+ 'Linux Reader': 'https://www.diskinternals.com/download/Linux_Reader.exe',
+ 'Macs Fan Control': 'https://www.crystalidea.com/downloads/macsfancontrol_setup.exe',
+ 'NirCmd32': 'https://www.nirsoft.net/utils/nircmd.zip',
+ 'NirCmd64': 'https://www.nirsoft.net/utils/nircmd-x64.zip',
+ 'NotepadPlusPlus': 'https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v7.9.5/npp.7.9.5.portable.minimalist.7z',
+ 'Office Deployment Tool': 'https://download.microsoft.com/download/2/7/A/27AF1BE6-DD20-4CB4-B154-EBAB8A7D4A7E/officedeploymenttool_11617-33601.exe',
+ 'ProduKey32': 'http://www.nirsoft.net/utils/produkey.zip',
+ 'ProduKey64': 'http://www.nirsoft.net/utils/produkey-x64.zip',
+ 'PuTTY': 'https://the.earth.li/~sgtatham/putty/latest/w32/putty.zip',
+ 'RKill': 'https://download.bleepingcomputer.com/grinler/rkill.exe',
+ 'Samsung Magician': 'https://s3.ap-northeast-2.amazonaws.com/global.semi.static/SAMSUNG_SSD_v5_3_0_181121/CD0C7CC1BE00525FAC4675B9E502899B41D5C3909ECE3AA2FB6B74A766B2A1EA/Samsung_Magician_Installer.zip',
+ 'SDIO Themes': 'http://snappy-driver-installer.org/downloads/SDIO_Themes.zip',
+ 'SDIO Torrent': 'http://snappy-driver-installer.org/downloads/SDIO_Update.torrent',
+ 'ShutUp10': 'https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe',
+ 'smartmontools': 'https://1278-105252244-gh.circle-artifacts.com/0/builds/smartmontools-win32-setup-7.3-r5216.exe',
+ 'TDSSKiller': 'https://media.kaspersky.com/utilities/VirusUtilities/EN/tdsskiller.exe',
+ 'TestDisk': 'https://www.cgsecurity.org/testdisk-7.2-WIP.win.zip',
+ 'wimlib32': 'https://wimlib.net/downloads/wimlib-1.13.3-windows-i686-bin.zip',
+ 'wimlib64': 'https://wimlib.net/downloads/wimlib-1.13.3-windows-x86_64-bin.zip',
+ 'WinAIO Repair': 'http://www.tweaking.com/files/setups/tweaking.com_windows_repair_aio.zip',
+ 'Winapp2': 'https://github.com/MoscaDotTo/Winapp2/archive/master.zip',
+ 'WizTree': 'https://wiztreefree.com/files/wiztree_3_39_portable.zip',
+ 'XMPlay 7z': 'https://support.xmplay.com/files/16/xmp-7z.zip?v=800962',
+ 'XMPlay Game': 'https://support.xmplay.com/files/12/xmp-gme.zip?v=515637',
+ 'XMPlay RAR': 'https://support.xmplay.com/files/16/xmp-rar.zip?v=409646',
+ 'XMPlay WAModern': 'https://support.xmplay.com/files/10/WAModern.zip?v=207099',
+ 'XMPlay': 'https://support.xmplay.com/files/20/xmplay383.zip?v=298195',
+ 'XYplorerFree': 'https://www.xyplorer.com/download/xyplorer_free_noinstall.zip',
+}
+
+
+if __name__ == '__main__':
+ print("This file is not meant to be called directly.")
diff --git a/scripts/wk/kit/tools.py b/scripts/wk/kit/tools.py
index 9c7ce4ba..c1404b69 100644
--- a/scripts/wk/kit/tools.py
+++ b/scripts/wk/kit/tools.py
@@ -1,14 +1,22 @@
"""WizardKit: Tool Functions"""
# vim: sts=2 sw=2 ts=2
+from datetime import datetime, timedelta
+import logging
import pathlib
import sys
+import requests
+
+from wk.cfg.main import ARCHIVE_PASSWORD
+from wk.cfg.tools import SOURCES, DOWNLOAD_FREQUENCY
from wk.exe import popen_program, run_program
+from wk.std import GenericError
# STATIC VARIABLES
ARCH = '64' if sys.maxsize > 2**32 else '32'
+LOG = logging.getLogger(__name__)
# "GLOBAL" VARIABLES
@@ -16,6 +24,41 @@ CACHED_DIRS = {}
# Functions
+def download_file(out_path, source_url):
+ """Download a file using requests."""
+ out_path = pathlib.Path(out_path).resolve()
+ out_path.parent.mkdir(parents=True, exist_ok=True)
+
+ # Request download
+ response = requests.get(source_url, stream=True)
+ if not response.ok:
+ LOG.error(
+ 'Failed to download file (status %s): %s',
+ response.status_code, out_path.name,
+ )
+ raise GenericError(f'Failed to download file: {out_path.name}')
+
+ # Write to file
+ with open(out_path, 'wb') as _f:
+ for chunk in response.iter_content(chunk_size=128):
+ _f.write(chunk)
+
+ # Done
+ return out_path
+
+
+def extract_archive(archive, out_path, *args, mode='x', silent=True):
+ """Extract an archive to out_path."""
+ out_path = pathlib.Path(out_path).resolve()
+ out_path.parent.mkdir(parents=True, exist_ok=True)
+ cmd = [get_tool_path('7-Zip', '7za'), mode, archive, f'-o{out_path}', *args]
+ if silent:
+ cmd.extend(['-bso0', '-bse0', '-bsp0'])
+
+ # Extract
+ run_program(cmd)
+
+
def find_kit_dir(name=None):
"""Find folder in kit, returns pathlib.Path.
@@ -60,16 +103,50 @@ def get_tool_path(folder, name):
return tool_path
-def run_tool(folder, name, *args, popen=False):
- """Run tool from kit."""
- cmd = [get_tool_path(folder, name), *args]
+def run_tool(
+ folder, name, *run_args,
+ cbin=False, cwd=False, download=False, popen=False,
+ **run_kwargs,
+ ):
+ """Run tool from the kit or the Internet, returns proc obj.
+
+ proc will be either subprocess.CompletedProcess or subprocess.Popen."""
proc = None
+ # Extract from .cbin
+ if cbin:
+ extract_archive(
+ find_kit_dir('.cbin').joinpath(folder).with_suffix('.7z'),
+ find_kit_dir('.bin').joinpath(folder),
+ '-aos', f'-p{ARCHIVE_PASSWORD}',
+ )
+
+ # Download tool
+ if download:
+ out_path = find_kit_dir('.bin').joinpath(f'{folder}/{name}.exe')
+ outdated = False
+ try:
+ mod_time = datetime.fromtimestamp(out_path.stat().st_ctime)
+ outdated = datetime.now() - mod_time > timedelta(days=DOWNLOAD_FREQUENCY)
+ except FileNotFoundError:
+ # Ignore and download
+ pass
+ if not out_path.exists() or outdated:
+ LOG.info('Downloading tool: %s', name)
+ source_url = SOURCES[name]
+ download_file(out_path, source_url)
+ else:
+ LOG.info('Skip downloading tool: %s', name)
+
# Run
+ tool_path = get_tool_path(folder, name)
+ cmd = [tool_path, *run_args]
+ if cwd:
+ run_kwargs['cwd'] = tool_path.parent
if popen:
- proc = popen_program(cmd)
+ proc = popen_program(cmd, **run_kwargs)
else:
- proc = run_program(cmd, check=False)
+ proc = run_program(cmd, check=False, **run_kwargs)
# Done
return proc
From b44fda2ccdf402b7836878e3772ef3fd913274b2 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Tue, 20 Apr 2021 21:14:31 -0600
Subject: [PATCH 12/43] Avoid clobbering files when downloading tools.
---
scripts/wk/kit/tools.py | 48 ++++++++++++++++++++++++++++++-----------
1 file changed, 35 insertions(+), 13 deletions(-)
diff --git a/scripts/wk/kit/tools.py b/scripts/wk/kit/tools.py
index c1404b69..12e7d3df 100644
--- a/scripts/wk/kit/tools.py
+++ b/scripts/wk/kit/tools.py
@@ -24,19 +24,30 @@ CACHED_DIRS = {}
# Functions
-def download_file(out_path, source_url):
- """Download a file using requests."""
+def download_file(out_path, source_url, as_new=False, overwrite=False):
+ """Download a file using requests, returns pathlib.Path."""
out_path = pathlib.Path(out_path).resolve()
+
+ # Avoid clobbering
+ if out_path.exists() and not overwrite:
+ raise FileExistsError(f'Refusing to clobber {out_path}')
+
+ # Create destination directory
out_path.parent.mkdir(parents=True, exist_ok=True)
+ if as_new:
+ out_path = out_path.with_suffix(f'{out_path.suffix}.new')
# Request download
response = requests.get(source_url, stream=True)
if not response.ok:
+ name = out_path.name
+ if as_new:
+ name = name[:-4]
LOG.error(
'Failed to download file (status %s): %s',
- response.status_code, out_path.name,
+ response.status_code, name,
)
- raise GenericError(f'Failed to download file: {out_path.name}')
+ raise GenericError(f'Failed to download file: {name}')
# Write to file
with open(out_path, 'wb') as _f:
@@ -113,6 +124,18 @@ def run_tool(
proc will be either subprocess.CompletedProcess or subprocess.Popen."""
proc = None
+ def _is_outdated(file_path):
+ """Check if the ctime is older than the threshold, returns bool."""
+ outdated = False
+ try:
+ ctime = datetime.fromtimestamp(file_path.stat().st_ctime)
+ outdated = datetime.now() - ctime > timedelta(days=DOWNLOAD_FREQUENCY)
+ except FileNotFoundError:
+ LOG.error("This shouldn't happen right?")
+
+ # Done
+ return outdated
+
# Extract from .cbin
if cbin:
extract_archive(
@@ -124,17 +147,16 @@ def run_tool(
# Download tool
if download:
out_path = find_kit_dir('.bin').joinpath(f'{folder}/{name}.exe')
- outdated = False
- try:
- mod_time = datetime.fromtimestamp(out_path.stat().st_ctime)
- outdated = datetime.now() - mod_time > timedelta(days=DOWNLOAD_FREQUENCY)
- except FileNotFoundError:
- # Ignore and download
- pass
- if not out_path.exists() or outdated:
+ if not out_path.exists() or _is_outdated(out_path):
LOG.info('Downloading tool: %s', name)
source_url = SOURCES[name]
- download_file(out_path, source_url)
+ try:
+ new_file = download_file(out_path, source_url, as_new=True)
+ new_file.replace(out_path)
+ except GenericError:
+ # Ignore as long as there's still a version present
+ if not out_path.exists():
+ raise
else:
LOG.info('Skip downloading tool: %s', name)
From ee3203c485bbe9df0534a1c1cd3f5375c7ae4bf2 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Tue, 20 Apr 2021 22:06:03 -0600
Subject: [PATCH 13/43] Show message when downloading tools
---
scripts/wk/kit/tools.py | 3 +++
scripts/wk/std.py | 11 ++++++++---
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/scripts/wk/kit/tools.py b/scripts/wk/kit/tools.py
index 12e7d3df..4f23b9a9 100644
--- a/scripts/wk/kit/tools.py
+++ b/scripts/wk/kit/tools.py
@@ -27,6 +27,8 @@ CACHED_DIRS = {}
def download_file(out_path, source_url, as_new=False, overwrite=False):
"""Download a file using requests, returns pathlib.Path."""
out_path = pathlib.Path(out_path).resolve()
+ cursor_left = '\u001B[14D'
+ print(f'Downloading...{cursor_left}', end='', flush=True)
# Avoid clobbering
if out_path.exists() and not overwrite:
@@ -55,6 +57,7 @@ def download_file(out_path, source_url, as_new=False, overwrite=False):
_f.write(chunk)
# Done
+ print(f' {cursor_left}', end='', flush=True)
return out_path
diff --git a/scripts/wk/std.py b/scripts/wk/std.py
index 5dba174c..8da412f1 100644
--- a/scripts/wk/std.py
+++ b/scripts/wk/std.py
@@ -455,7 +455,7 @@ class TryAndPrint():
# Done
return message
- def _format_function_output(self, output):
+ def _format_function_output(self, output, msg_good):
"""Format function output for use in try_and_print(), returns str."""
LOG.debug('Formatting output: %s', output)
@@ -468,6 +468,10 @@ class TryAndPrint():
if not isinstance(stdout, str):
stdout = stdout.decode('utf8')
output = stdout.strip().splitlines()
+ if not output:
+ # Going to treat these as successes (for now)
+ LOG.warning('Program output was empty, assuming good result.')
+ return color_string(msg_good, 'GREEN')
else:
try:
output = list(output)
@@ -560,6 +564,7 @@ class TryAndPrint():
verbose,
)
f_exception = None
+ msg_good = msg_good if msg_good else self.msg_good
output = None
result_msg = 'UNKNOWN'
if catch_all is None:
@@ -600,10 +605,10 @@ class TryAndPrint():
else:
# Success
if output:
- result_msg = self._format_function_output(output)
+ result_msg = self._format_function_output(output, msg_good)
print(result_msg)
else:
- result_msg = msg_good if msg_good else self.msg_good
+ result_msg = msg_good
print_success(result_msg, log=False)
# Done
From 3c748520e10851d4bf542f039b793e1d2b75e0a5 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Tue, 20 Apr 2021 22:23:05 -0600
Subject: [PATCH 14/43] Don't suppress bells in ConEmu
---
scripts/wk/std.py | 1 -
setup/windows/bin/ConEmu/ConEmu.xml | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/scripts/wk/std.py b/scripts/wk/std.py
index 8da412f1..2768fea8 100644
--- a/scripts/wk/std.py
+++ b/scripts/wk/std.py
@@ -650,7 +650,6 @@ def ask(prompt='Kotaero!'):
def beep(repeat=1):
"""Play system bell with optional repeat."""
- # TODO: Verify Windows functionality
while repeat >= 1:
# Print bell char without a newline
print('\a', end='', flush=True)
diff --git a/setup/windows/bin/ConEmu/ConEmu.xml b/setup/windows/bin/ConEmu/ConEmu.xml
index dd6ee9b1..93e91bff 100644
--- a/setup/windows/bin/ConEmu/ConEmu.xml
+++ b/setup/windows/bin/ConEmu/ConEmu.xml
@@ -89,7 +89,7 @@
-
+
From 125907ed3afd1c5eb44d1088c9e7b4d3a6cffffd Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Wed, 21 Apr 2021 05:15:12 -0600
Subject: [PATCH 15/43] Add Auto Repairs menus
---
scripts/auto_repairs.py | 2 +-
scripts/wk/repairs/win.py | 265 +++++++++++++++++++++++++++++---------
scripts/wk/std.py | 85 ++++++------
3 files changed, 250 insertions(+), 102 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index 4409e3ab..f4c471fc 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -11,7 +11,7 @@ import wk
if __name__ == '__main__':
try:
- wk.repairs.win.run_auto_repair()
+ wk.repairs.win.run_auto_repairs()
except SystemExit:
raise
except: #pylint: disable=bare-except
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 6cba5133..324d05a7 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -7,6 +7,8 @@ import os
import platform
import sys
+from subprocess import CalledProcessError
+
from wk.cfg.main import KIT_NAME_FULL
from wk.exe import get_procs, run_program, popen_program, wait_for_procs
from wk.kit.tools import run_tool
@@ -15,9 +17,11 @@ from wk.os.win import reg_delete_value, reg_read_value, reg_set_value
from wk.std import (
GenericError,
GenericWarning,
+ Menu,
TryAndPrint,
- abort,
+ ask,
clear_screen,
+ color_string,
pause,
print_info,
set_title,
@@ -28,117 +32,242 @@ from wk.std import (
# STATIC VARIABLES
LOG = logging.getLogger(__name__)
AUTO_REPAIR_DELAY_IN_SECONDS = 30
-AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\AutoRepair'
+AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\Auto Repairs'
CONEMU = 'ConEmuPID' in os.environ
+MENUS = {
+ 'Options': {
+ 'Backup Settings': (
+ ('Enable System Restore', None),
+ ('Create System Restore', None),
+ ('Backup Browsers', None),
+ ('Backup Power Plans', None),
+ ),
+ 'Windows Repairs': (
+ ('Disable Windows Updates', None),
+ ('Reset Windows Updates', None),
+ ('-Reboot-', None),
+ ('CHKDSK', None),
+ ('DISM RestoreHealth', None),
+ ('SFC Scan', None),
+ ('Fix File Associations', None),
+ ('Clear Proxy Settings', None),
+ ('Disable Pending Renames', None),
+ ('Registry Repairs', None),
+ ('Repair Safe Mode', None),
+ ('Reset Windows Policies', None),
+ ),
+ 'Malware Cleanup': (
+ ('BleachBit', None),
+ ('HitmanPro', None),
+ ('KVRT', None),
+ ('Windows Defender', None),
+ ('-Reboot-', None),
+ ),
+ 'Manual Steps': (
+ ('AdwCleaner', None),
+ ('IO Bit Uninstaller', None),
+ ('Enable Windows Updates', None),
+ ),
+ },
+ 'Toggles': (
+ 'Kill Explorer',
+ 'Run RKill at startup',
+ 'Use Autologon',
+ ),
+ 'Actions': (
+ 'Options',
+ 'Start',
+ 'Quit',
+ ),
+ }
OS_VERSION = float(platform.win32_ver()[0])
TRY_PRINT = TryAndPrint()
-# AutoRepair Functions
+# Auto Repairs Functions
+def build_menus(title):
+ """Build menus, returns dict."""
+ menus = {}
+ menus['Main'] = Menu(title=f'{title}\n{color_string("Main Menu", "GREEN")}')
+
+ # Run groups
+ for group, items in MENUS['Options'].items():
+ menus[group] = Menu(title=f'{title}\n{color_string(group, "GREEN")}')
+ menus[group].disabled_str = color_string('Forced', 'ORANGE')
+ for item in items:
+ menus[group].add_option(item[0], {'Display Name': item[0], 'Selected': True})
+ if '-Reboot-' in menus[group].options:
+ menus[group].options['-Reboot-']['Disabled'] = True
+ menus[group].add_action('Main Menu')
+ for option in MENUS['Options']:
+ menus['Main'].add_option(option, {'Display Name': option, 'Selected': True})
+
+ # Actions
+ for action in MENUS['Actions']:
+ menus['Main'].add_action(action, {'Display Name': action, 'Selected': True})
+ menus['Main'].actions['Start']['Separator'] = True
+
+ # Options
+ menus['Options'] = Menu(title=f'{title}\n{color_string("Options", "GREEN")}')
+ for toggle in MENUS['Toggles']:
+ menus['Options'].add_toggle(toggle, {'Display Name': toggle, 'Selected': True})
+ menus['Options'].add_action('Main Menu')
+
+ # Initialize main menu display names
+ menus['Main'].update()
+
+ # Done
+ return menus
+
+
def end_session():
- """End AutoRepair session."""
+ """End Auto Repairs session."""
print_info('Ending repair session')
- reg_delete_value('HKCU', AUTO_REPAIR_KEY, 'SessionStarted')
+ auto_admin_logon = '0'
+
+ # Delete Auto Repairs session key
+ try:
+ reg_delete_value('HKCU', AUTO_REPAIR_KEY, 'SessionStarted')
+ except FileNotFoundError:
+ LOG.error('Ending repair session but session not started.')
# Remove logon task
cmd = [
'schtasks', '/delete', '/f',
- '/tn', f'{KIT_NAME_FULL}-AutoRepair',
+ '/tn', f'{KIT_NAME_FULL}-AutoRepairs',
]
- run_program(cmd)
+ try:
+ run_program(cmd)
+ except CalledProcessError:
+ LOG.error("Failed to remove scheduled task or it doesn't exist.")
# Disable Autologon
- run_tool('Sysinternals', 'Autologon')
- reg_set_value(
- 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
- 'AutoAdminLogon', '0', 'SZ',
- )
- reg_delete_value(
- 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
- 'DefaultUserName',
- )
- reg_delete_value(
- 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
- 'DefaultDomainName',
- )
+ try:
+ auto_admin_logon = reg_read_value(
+ 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
+ 'AutoAdminLogon',
+ )
+ except FileNotFoundError:
+ # Ignore and assume it's disabled
+ return
+ if auto_admin_logon != '0':
+ run_tool('Sysinternals', 'Autologon')
+ reg_set_value(
+ 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
+ 'AutoAdminLogon', '0', 'SZ',
+ )
+ reg_delete_value(
+ 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
+ 'DefaultUserName',
+ )
+ reg_delete_value(
+ 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
+ 'DefaultDomainName',
+ )
def init():
- """Initialize AutoRepair."""
- session_started = False
- try:
- session_started = reg_read_value('HKCU', AUTO_REPAIR_KEY, 'SessionStarted')
- except FileNotFoundError:
- pass
+ """Initialize Auto Repairs."""
+ session_started = is_session_started()
# Start or resume a repair session
- if not session_started:
- init_run()
- init_session()
- else:
+ if session_started:
print_info('Resuming session, press CTRL+c to cancel')
- try:
- for _x in range(AUTO_REPAIR_DELAY_IN_SECONDS, 0, -1):
- print(f' {_x} second{"" if _x==1 else "s"} remaining... \r', end='')
- sleep(1)
- except KeyboardInterrupt:
- abort()
+ for _x in range(AUTO_REPAIR_DELAY_IN_SECONDS, 0, -1):
+ print(f' {_x} second{"" if _x==1 else "s"} remaining... \r', end='')
+ sleep(1)
print('')
- init_run()
# Done
return session_started
-def init_run():
- """Initialize AutoRepair Run."""
- atexit.register(start_explorer)
+def init_run(options):
+ """Initialize Auto Repairs Run."""
+ if options['Kill Explorer']['Selected']:
+ atexit.register(start_explorer)
+ kill_explorer()
# TODO: Sync Clock
- kill_explorer()
# TODO: RKill
-def init_session():
- """Initialize AutoRepair session."""
+def init_session(options):
+ """Initialize Auto Repairs session."""
print_info('Starting repair session')
reg_set_value('HKCU', AUTO_REPAIR_KEY, 'SessionStarted', 1, 'DWORD')
- # Create logon task for AutoRepair
+ # Create logon task for Auto Repairs
cmd = [
'schtasks', '/create', '/f',
'/sc', 'ONLOGON',
- '/tn', f'{KIT_NAME_FULL}-AutoRepair',
+ '/tn', f'{KIT_NAME_FULL}-AutoRepairs',
'/rl', 'HIGHEST',
'/tr', fr'C:\Windows\System32\cmd.exe "/C {sys.executable} {sys.argv[0]}"',
]
run_program(cmd)
# One-time tasks
- # TODO: Backup Registry
- # TODO: Enable and create restore point
- run_tool('Sysinternals', 'Autologon')
- # TODO: Disable Windows updates
- # TODO: Reset Windows updates
+ if options['Use Autologon']['Selected']:
+ run_tool('Sysinternals', 'Autologon')
reboot()
-def run_auto_repair():
- """Run AutoRepair."""
- update_log_path(dest_name='Auto-Repair Tool', timestamp=True)
- title = f'{KIT_NAME_FULL}: Auto-Repair Tool'
+def is_session_started():
+ """Check if session was started, returns bool."""
+ session_started = False
+ try:
+ session_started = reg_read_value('HKCU', AUTO_REPAIR_KEY, 'SessionStarted')
+ except FileNotFoundError:
+ pass
+
+ # Done
+ return session_started
+
+
+def run_auto_repairs():
+ """Run Auto Repairs."""
+ update_log_path(dest_name='Auto Repairs', timestamp=True)
+ title = f'{KIT_NAME_FULL}: Auto Repairs'
clear_screen()
set_title(title)
print_info(title)
print('')
- # Show Menu on first run
- session_started = init()
- if not session_started:
- # TODO: Show Menu
- pass
+ # Generate menus
+ menus = build_menus(title)
+
+ # Init
+ try:
+ session_started = init()
+ except KeyboardInterrupt:
+ # Assuming session was started and resume countdown was interrupted
+ session_started = None
+
+ # Show Menu
+ if session_started is None or not session_started:
+ while True:
+ update_main_menu(menus)
+ selection = menus['Main'].simple_select(update=False)
+ if selection[0] in MENUS['Options'] or selection[0] == 'Options':
+ menus[selection[0]].advanced_select()
+ elif 'Start' in selection:
+ # TODO: Run repairs
+ pass
+ elif 'Quit' in selection:
+ if ask('End session?'):
+ end_session()
+ raise SystemExit
+
+ # Re-check if a repair session was started
+ if session_started is None:
+ session_started = is_session_started()
# Run repairs
# TODO: Run repairs
+ init_run(menus['Options'])
+ if not session_started:
+ init_session(menus['Options'])
+ atexit.unregister(start_explorer)
end_session()
# Done
@@ -146,6 +275,24 @@ def run_auto_repair():
pause('Press Enter to exit...')
+def update_main_menu(menus):
+ """Update main menu based on current selections."""
+ index = 1
+ skip = '-Reboot-'
+ for name in menus['Main'].options:
+ checkmark = ' '
+ selected = [
+ _v['Selected'] for _k, _v in menus[name].options.items() if _k != skip
+ ]
+ if all(selected):
+ checkmark = '✓'
+ elif any(selected):
+ checkmark = '-'
+ display_name = f' {index}: [{checkmark}] {name}'
+ index += 1
+ menus['Main'].options[name]['Display Name'] = display_name
+
+
# OS Built-in Functions
def kill_explorer():
"""Kill all Explorer processes."""
diff --git a/scripts/wk/std.py b/scripts/wk/std.py
index 2768fea8..f55cd1b3 100644
--- a/scripts/wk/std.py
+++ b/scripts/wk/std.py
@@ -228,44 +228,6 @@ class Menu():
# Done
return resolved_selection
- def _update(self, single_selection=True, settings_mode=False):
- """Update menu items in preparation for printing to screen."""
- index = 0
-
- # Fix selection status for sets
- for set_details in self.sets.values():
- set_selected = True
- set_targets = set_details['Targets']
- for option, option_details in self.options.items():
- if option in set_targets and not option_details['Selected']:
- set_selected = False
- elif option not in set_targets and option_details['Selected']:
- set_selected = False
- set_details['Selected'] = set_selected
-
- # Numbered sections
- for section in (self.sets, self.toggles, self.options):
- for name, details in section.items():
- if details.get('Hidden', False):
- # Skip hidden lines and don't increment index
- continue
- index += 1
- details['Display Name'] = self._get_display_name(
- name,
- details,
- index=index,
- no_checkboxes=single_selection,
- setting_item=settings_mode,
- )
-
- # Actions
- for name, details in self.actions.items():
- details['Display Name'] = self._get_display_name(
- name,
- details,
- no_checkboxes=True,
- )
-
def _update_entry_selection_status(self, entry, toggle=True, status=None):
"""Update entry selection status either directly or by toggling."""
if entry in self.sets:
@@ -340,7 +302,7 @@ class Menu():
NOTE: Menu is displayed until an action entry is selected.
"""
while True:
- self._update(single_selection=False)
+ self.update(single_selection=False)
user_selection = self._user_select(prompt)
selected_entry = self._resolve_selection(user_selection)
if user_selection.isnumeric():
@@ -364,7 +326,7 @@ class Menu():
}
while True:
- self._update(single_selection=True, settings_mode=True)
+ self.update(single_selection=True, settings_mode=True)
user_selection = self._user_select(prompt)
selected_entry = self._resolve_selection(user_selection)
if user_selection.isnumeric():
@@ -381,12 +343,51 @@ class Menu():
# Done
return selected_entry
- def simple_select(self, prompt='Please make a selection: '):
+ def simple_select(self, prompt='Please make a selection: ', update=True):
"""Display menu and make a single selection, returns tuple."""
- self._update()
+ if update:
+ self.update()
user_selection = self._user_select(prompt)
return self._resolve_selection(user_selection)
+ def update(self, single_selection=True, settings_mode=False):
+ """Update menu items in preparation for printing to screen."""
+ index = 0
+
+ # Fix selection status for sets
+ for set_details in self.sets.values():
+ set_selected = True
+ set_targets = set_details['Targets']
+ for option, option_details in self.options.items():
+ if option in set_targets and not option_details['Selected']:
+ set_selected = False
+ elif option not in set_targets and option_details['Selected']:
+ set_selected = False
+ set_details['Selected'] = set_selected
+
+ # Numbered sections
+ for section in (self.sets, self.toggles, self.options):
+ for name, details in section.items():
+ if details.get('Hidden', False):
+ # Skip hidden lines and don't increment index
+ continue
+ index += 1
+ details['Display Name'] = self._get_display_name(
+ name,
+ details,
+ index=index,
+ no_checkboxes=single_selection,
+ setting_item=settings_mode,
+ )
+
+ # Actions
+ for name, details in self.actions.items():
+ details['Display Name'] = self._get_display_name(
+ name,
+ details,
+ no_checkboxes=True,
+ )
+
class TryAndPrint():
# pylint: disable=too-many-instance-attributes
From d200878e0dbea3d60e15c9c04f6b603aa4135c1a Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Wed, 21 Apr 2021 22:27:49 -0600
Subject: [PATCH 16/43] Revert update() to private method and add wrapper
---
scripts/wk/std.py | 84 +++++++++++++++++++++++++----------------------
1 file changed, 44 insertions(+), 40 deletions(-)
diff --git a/scripts/wk/std.py b/scripts/wk/std.py
index f55cd1b3..b8f6910a 100644
--- a/scripts/wk/std.py
+++ b/scripts/wk/std.py
@@ -228,6 +228,44 @@ class Menu():
# Done
return resolved_selection
+ def _update(self, single_selection=True, settings_mode=False):
+ """Update menu items in preparation for printing to screen."""
+ index = 0
+
+ # Fix selection status for sets
+ for set_details in self.sets.values():
+ set_selected = True
+ set_targets = set_details['Targets']
+ for option, option_details in self.options.items():
+ if option in set_targets and not option_details['Selected']:
+ set_selected = False
+ elif option not in set_targets and option_details['Selected']:
+ set_selected = False
+ set_details['Selected'] = set_selected
+
+ # Numbered sections
+ for section in (self.sets, self.toggles, self.options):
+ for name, details in section.items():
+ if details.get('Hidden', False):
+ # Skip hidden lines and don't increment index
+ continue
+ index += 1
+ details['Display Name'] = self._get_display_name(
+ name,
+ details,
+ index=index,
+ no_checkboxes=single_selection,
+ setting_item=settings_mode,
+ )
+
+ # Actions
+ for name, details in self.actions.items():
+ details['Display Name'] = self._get_display_name(
+ name,
+ details,
+ no_checkboxes=True,
+ )
+
def _update_entry_selection_status(self, entry, toggle=True, status=None):
"""Update entry selection status either directly or by toggling."""
if entry in self.sets:
@@ -302,7 +340,7 @@ class Menu():
NOTE: Menu is displayed until an action entry is selected.
"""
while True:
- self.update(single_selection=False)
+ self._update(single_selection=False)
user_selection = self._user_select(prompt)
selected_entry = self._resolve_selection(user_selection)
if user_selection.isnumeric():
@@ -326,7 +364,7 @@ class Menu():
}
while True:
- self.update(single_selection=True, settings_mode=True)
+ self._update(single_selection=True, settings_mode=True)
user_selection = self._user_select(prompt)
selected_entry = self._resolve_selection(user_selection)
if user_selection.isnumeric():
@@ -346,47 +384,13 @@ class Menu():
def simple_select(self, prompt='Please make a selection: ', update=True):
"""Display menu and make a single selection, returns tuple."""
if update:
- self.update()
+ self._update()
user_selection = self._user_select(prompt)
return self._resolve_selection(user_selection)
- def update(self, single_selection=True, settings_mode=False):
- """Update menu items in preparation for printing to screen."""
- index = 0
-
- # Fix selection status for sets
- for set_details in self.sets.values():
- set_selected = True
- set_targets = set_details['Targets']
- for option, option_details in self.options.items():
- if option in set_targets and not option_details['Selected']:
- set_selected = False
- elif option not in set_targets and option_details['Selected']:
- set_selected = False
- set_details['Selected'] = set_selected
-
- # Numbered sections
- for section in (self.sets, self.toggles, self.options):
- for name, details in section.items():
- if details.get('Hidden', False):
- # Skip hidden lines and don't increment index
- continue
- index += 1
- details['Display Name'] = self._get_display_name(
- name,
- details,
- index=index,
- no_checkboxes=single_selection,
- setting_item=settings_mode,
- )
-
- # Actions
- for name, details in self.actions.items():
- details['Display Name'] = self._get_display_name(
- name,
- details,
- no_checkboxes=True,
- )
+ def update(self):
+ """Update menu with default settings."""
+ self._update()
class TryAndPrint():
From c2d3752bc595a4f0be7d156538594078ab8d8d01 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Wed, 21 Apr 2021 23:57:19 -0600
Subject: [PATCH 17/43] Update TryAndPrint()
---
scripts/wk/std.py | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/scripts/wk/std.py b/scripts/wk/std.py
index b8f6910a..c568c499 100644
--- a/scripts/wk/std.py
+++ b/scripts/wk/std.py
@@ -569,13 +569,11 @@ class TryAndPrint():
verbose,
)
f_exception = None
+ catch_all = catch_all if catch_all else self.catch_all
msg_good = msg_good if msg_good else self.msg_good
output = None
result_msg = 'UNKNOWN'
- if catch_all is None:
- catch_all = self.catch_all
- if verbose is None:
- verbose = self.verbose
+ verbose = verbose if verbose else self.verbose
# Build exception tuples
e_exceptions = tuple(self._get_exception(e) for e in self.list_errors)
@@ -619,8 +617,8 @@ class TryAndPrint():
# Done
self._log_result(message, result_msg)
return {
- 'Failed': bool(f_exception),
'Exception': f_exception,
+ 'Failed': bool(f_exception),
'Output': output,
}
From 10b443f0f5331ffc92763f2a2a3f4e75cb77a002 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Wed, 21 Apr 2021 23:58:51 -0600
Subject: [PATCH 18/43] Add framework for running auto repair groups
---
scripts/auto_repairs.py | 95 ++++++++++++++++-
scripts/wk/repairs/win.py | 217 +++++++++++++++++++++++---------------
scripts/wk/std.py | 1 +
3 files changed, 226 insertions(+), 87 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index f4c471fc..8096e5b8 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -3,15 +3,106 @@
import os
import sys
+import random # TODO: Deleteme
+import time
os.chdir(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(os.getcwd())
-import wk
+import wk # pylint: disable=wrong-import-position
+
+
+# Classes
+class MenuEntry():
+ # pylint: disable=too-few-public-methods
+ """Simple class to allow cleaner code below."""
+ def __init__(self, name, function=None, **kwargs):
+ self.name = name
+
+ # Color reboot entries
+ if name == 'Reboot':
+ self.name = wk.std.color_string(
+ ['Reboot', ' ', '(Forced)'],
+ ['YELLOW', None, 'ORANGE'],
+ sep='',
+ )
+
+ # Set details
+ self.details = {
+ 'Selected': True,
+ **kwargs,
+ }
+ if function:
+ self.details['Function'] = function
+
+
+# TODO: Deleteme
+TRY_AND_PRINT = wk.std.TryAndPrint()
+def placeholder_function(group, name):
+ TRY_AND_PRINT.run(f'{name}...', time.sleep, random.randint(1, 3))
+ wk.repairs.win.save_settings(group, name, done=True)
+
+def placeholder_reboot(group, name):
+ print('"Rebooting" shortly...')
+ time.sleep(random.randint(1, 3))
+ wk.repairs.win.save_settings(group, name, done=True)
+ raise SystemExit
+
+
+# STATIC VARIABLES
+BASE_MENUS = {
+ 'Options': {
+ 'Backup Settings': (
+ MenuEntry('Enable RegBack', placeholder_function),
+ MenuEntry('Enable System Restore', placeholder_function),
+ MenuEntry('Create System Restore', placeholder_function),
+ MenuEntry('Backup Browsers', placeholder_function),
+ MenuEntry('Backup Power Plans', placeholder_function),
+ MenuEntry('Backup Registry', placeholder_function),
+ ),
+ 'Windows Repairs': (
+ MenuEntry('Disable Windows Updates', placeholder_function),
+ MenuEntry('Reset Windows Updates', placeholder_function),
+ MenuEntry('Reboot', placeholder_reboot),
+ MenuEntry('CHKDSK', placeholder_function),
+ MenuEntry('DISM RestoreHealth', wk.repairs.win.auto_dism),
+ MenuEntry('SFC Scan', placeholder_function),
+ MenuEntry('Fix File Associations', placeholder_function),
+ MenuEntry('Clear Proxy Settings', placeholder_function),
+ MenuEntry('Disable Pending Renames', placeholder_function),
+ MenuEntry('Registry Repairs', placeholder_function),
+ MenuEntry('Repair Safe Mode', placeholder_function),
+ MenuEntry('Reset UAC', placeholder_function),
+ MenuEntry('Reset Windows Policies', placeholder_function),
+ ),
+ 'Malware Cleanup': (
+ MenuEntry('BleachBit', placeholder_function),
+ MenuEntry('HitmanPro', placeholder_function),
+ MenuEntry('KVRT', placeholder_function),
+ MenuEntry('Windows Defender', placeholder_function),
+ MenuEntry('Reboot', placeholder_reboot),
+ ),
+ 'Manual Steps': (
+ MenuEntry('AdwCleaner', placeholder_function),
+ MenuEntry('IO Bit Uninstaller', placeholder_function),
+ MenuEntry('Enable Windows Updates', placeholder_function),
+ ),
+ },
+ 'Toggles': (
+ MenuEntry('Kill Explorer'),
+ MenuEntry('Run RKill at startup'),
+ MenuEntry('Use Autologon'),
+ ),
+ 'Actions': (
+ MenuEntry('Options'),
+ MenuEntry('Start', Separator=True),
+ MenuEntry('Quit'),
+ ),
+ }
if __name__ == '__main__':
try:
- wk.repairs.win.run_auto_repairs()
+ wk.repairs.win.run_auto_repairs(BASE_MENUS)
except SystemExit:
raise
except: #pylint: disable=bare-except
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 324d05a7..6ae06700 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -13,7 +13,14 @@ from wk.cfg.main import KIT_NAME_FULL
from wk.exe import get_procs, run_program, popen_program, wait_for_procs
from wk.kit.tools import run_tool
from wk.log import format_log_path, update_log_path
-from wk.os.win import reg_delete_value, reg_read_value, reg_set_value
+from wk.os.win import (
+ reg_delete_value,
+ reg_read_value,
+ reg_set_value,
+ disable_service,
+ enable_service,
+ stop_service,
+ )
from wk.std import (
GenericError,
GenericWarning,
@@ -24,8 +31,11 @@ from wk.std import (
color_string,
pause,
print_info,
+ print_standard,
+ print_warning,
set_title,
sleep,
+ strip_colors,
)
@@ -34,85 +44,39 @@ LOG = logging.getLogger(__name__)
AUTO_REPAIR_DELAY_IN_SECONDS = 30
AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\Auto Repairs'
CONEMU = 'ConEmuPID' in os.environ
-MENUS = {
- 'Options': {
- 'Backup Settings': (
- ('Enable System Restore', None),
- ('Create System Restore', None),
- ('Backup Browsers', None),
- ('Backup Power Plans', None),
- ),
- 'Windows Repairs': (
- ('Disable Windows Updates', None),
- ('Reset Windows Updates', None),
- ('-Reboot-', None),
- ('CHKDSK', None),
- ('DISM RestoreHealth', None),
- ('SFC Scan', None),
- ('Fix File Associations', None),
- ('Clear Proxy Settings', None),
- ('Disable Pending Renames', None),
- ('Registry Repairs', None),
- ('Repair Safe Mode', None),
- ('Reset Windows Policies', None),
- ),
- 'Malware Cleanup': (
- ('BleachBit', None),
- ('HitmanPro', None),
- ('KVRT', None),
- ('Windows Defender', None),
- ('-Reboot-', None),
- ),
- 'Manual Steps': (
- ('AdwCleaner', None),
- ('IO Bit Uninstaller', None),
- ('Enable Windows Updates', None),
- ),
- },
- 'Toggles': (
- 'Kill Explorer',
- 'Run RKill at startup',
- 'Use Autologon',
- ),
- 'Actions': (
- 'Options',
- 'Start',
- 'Quit',
- ),
- }
OS_VERSION = float(platform.win32_ver()[0])
TRY_PRINT = TryAndPrint()
+TRY_PRINT.verbose = True
+#for error in ('subprocess.CalledProcessError', 'FileNotFoundError'):
+# TRY_PRINT.add_error(error)
-# Auto Repairs Functions
-def build_menus(title):
+# Auto Repairs
+def build_menus(base_menus, title):
"""Build menus, returns dict."""
menus = {}
menus['Main'] = Menu(title=f'{title}\n{color_string("Main Menu", "GREEN")}')
- # Run groups
- for group, items in MENUS['Options'].items():
- menus[group] = Menu(title=f'{title}\n{color_string(group, "GREEN")}')
- menus[group].disabled_str = color_string('Forced', 'ORANGE')
- for item in items:
- menus[group].add_option(item[0], {'Display Name': item[0], 'Selected': True})
- if '-Reboot-' in menus[group].options:
- menus[group].options['-Reboot-']['Disabled'] = True
- menus[group].add_action('Main Menu')
- for option in MENUS['Options']:
- menus['Main'].add_option(option, {'Display Name': option, 'Selected': True})
-
- # Actions
- for action in MENUS['Actions']:
- menus['Main'].add_action(action, {'Display Name': action, 'Selected': True})
- menus['Main'].actions['Start']['Separator'] = True
+ # Main Menu
+ for entry in base_menus['Actions']:
+ menus['Main'].add_action(entry.name, entry.details)
+ for group in base_menus['Options']:
+ menus['Main'].add_option(group, {'Selected': True})
# Options
menus['Options'] = Menu(title=f'{title}\n{color_string("Options", "GREEN")}')
- for toggle in MENUS['Toggles']:
- menus['Options'].add_toggle(toggle, {'Display Name': toggle, 'Selected': True})
+ for entry in base_menus['Toggles']:
+ menus['Options'].add_toggle(entry.name, entry.details)
menus['Options'].add_action('Main Menu')
+ # Run groups
+ for group, entries in base_menus['Options'].items():
+ menus[group] = Menu(title=f'{title}\n{color_string(group, "GREEN")}')
+ menus[group].disabled_str = 'Done'
+ for entry in entries:
+ menus[group].add_option(entry.name, entry.details)
+ menus[group].add_action('Main Menu')
+
# Initialize main menu display names
menus['Main'].update()
@@ -166,12 +130,28 @@ def end_session():
)
-def init():
+def get_entry_settings(group, name):
+ """Get menu entry settings from the registry, returns dict."""
+ key_path = fr'{AUTO_REPAIR_KEY}\{group}\{strip_colors(name)}'
+ settings = {}
+ for value in ('Done', 'Failed', 'Result'):
+ try:
+ settings[value] = reg_read_value('HKCU', key_path, value)
+ except FileNotFoundError:
+ # Ignore and use current settings
+ pass
+
+ # Done
+ return settings
+
+
+def init(menus):
"""Initialize Auto Repairs."""
session_started = is_session_started()
# Start or resume a repair session
if session_started:
+ load_settings(menus)
print_info('Resuming session, press CTRL+c to cancel')
for _x in range(AUTO_REPAIR_DELAY_IN_SECONDS, 0, -1):
print(f' {_x} second{"" if _x==1 else "s"} remaining... \r', end='')
@@ -184,7 +164,7 @@ def init():
def init_run(options):
"""Initialize Auto Repairs Run."""
- if options['Kill Explorer']['Selected']:
+ if options.toggles['Kill Explorer']['Selected']:
atexit.register(start_explorer)
kill_explorer()
# TODO: Sync Clock
@@ -207,9 +187,9 @@ def init_session(options):
run_program(cmd)
# One-time tasks
- if options['Use Autologon']['Selected']:
+ if options.toggles['Use Autologon']['Selected']:
run_tool('Sysinternals', 'Autologon')
- reboot()
+ # TODO: Re-enable reboot()
def is_session_started():
@@ -224,7 +204,19 @@ def is_session_started():
return session_started
-def run_auto_repairs():
+def load_settings(menus):
+ """Load session settings from the registry."""
+ for group, menu in menus.items():
+ if group == 'Main':
+ continue
+ for name in menu.options:
+ menu.options[name].update(get_entry_settings(group, name))
+ if menu.options[name].get('Done', '0') == '1':
+ menu.options[name]['Selected'] = False
+ menu.options[name]['Disabled'] = True
+
+
+def run_auto_repairs(base_menus):
"""Run Auto Repairs."""
update_log_path(dest_name='Auto Repairs', timestamp=True)
title = f'{KIT_NAME_FULL}: Auto Repairs'
@@ -234,11 +226,12 @@ def run_auto_repairs():
print('')
# Generate menus
- menus = build_menus(title)
+ print_standard('Initializing...')
+ menus = build_menus(base_menus, title)
# Init
try:
- session_started = init()
+ session_started = init(menus)
except KeyboardInterrupt:
# Assuming session was started and resume countdown was interrupted
session_started = None
@@ -248,11 +241,10 @@ def run_auto_repairs():
while True:
update_main_menu(menus)
selection = menus['Main'].simple_select(update=False)
- if selection[0] in MENUS['Options'] or selection[0] == 'Options':
+ if selection[0] in base_menus['Options'] or selection[0] == 'Options':
menus[selection[0]].advanced_select()
elif 'Start' in selection:
- # TODO: Run repairs
- pass
+ break
elif 'Quit' in selection:
if ask('End session?'):
end_session()
@@ -262,23 +254,39 @@ def run_auto_repairs():
if session_started is None:
session_started = is_session_started()
- # Run repairs
- # TODO: Run repairs
+ # Start or resume repairs
init_run(menus['Options'])
if not session_started:
init_session(menus['Options'])
- atexit.unregister(start_explorer)
- end_session()
+
+ # Run repairs
+ clear_screen()
+ for group, menu in menus.items():
+ if group in ('Main', 'Options'):
+ continue
+ for name, details in menu.options.items():
+ if details.get('Done', None) == '1':
+ continue
+ details['Function'](group, name)
# Done
+ end_session()
print_info('Done')
pause('Press Enter to exit...')
+def save_settings(group, name, done=False, failed=False, result='Unknown'):
+ """Load session settings from the registry."""
+ key_path = fr'{AUTO_REPAIR_KEY}\{group}\{strip_colors(name)}'
+ reg_set_value('HKCU', key_path, 'Done', '1' if done else '0', 'SZ')
+ reg_set_value('HKCU', key_path, 'Failed', '1' if failed else '0', 'SZ')
+ reg_set_value('HKCU', key_path, 'Result', result, 'SZ')
+
+
def update_main_menu(menus):
"""Update main menu based on current selections."""
index = 1
- skip = '-Reboot-'
+ skip = 'Reboot'
for name in menus['Main'].options:
checkmark = ' '
selected = [
@@ -293,16 +301,54 @@ def update_main_menu(menus):
menus['Main'].options[name]['Display Name'] = display_name
+# Auto Repairs: Wrapper Functions
+def auto_dism(group, name):
+ """Auto DISM repairs, returns bool."""
+ needs_reboot = False
+ result = TRY_PRINT.run('DISM (RestoreHealth)...', run_dism)
+
+ # Try again if necessary
+ if result['Failed']:
+ TRY_PRINT.run('Enabling Windows Updates...', enable_windows_updates)
+ result = TRY_PRINT.run('DISM (RestoreHealth)...', run_dism)
+ TRY_PRINT.run('Disabling Windows Updates...', disable_windows_updates)
+ needs_reboot = True
+
+ # Save settings
+ save_settings(
+ group, name, done=True,
+ failed=result['Failed'],
+ result=result['Message'],
+ )
+
+ # Done
+ if needs_reboot:
+ reboot()
+
+
# OS Built-in Functions
+def disable_windows_updates():
+ """Disable and stop Windows Updates."""
+ stop_service('wuauserv')
+ disable_service('wuauserv')
+
+
+def enable_windows_updates():
+ """Enable Windows Updates."""
+ enable_service('wuauserv', 'demand')
+
+
def kill_explorer():
"""Kill all Explorer processes."""
cmd = ['taskkill', '/im', 'explorer.exe', '/f']
run_program(cmd, check=False)
-def reboot():
+def reboot(timeout=10):
"""Reboot the system."""
atexit.unregister(start_explorer)
+ print_warning(f'Rebooting the system in {timeout} seconds...')
+ sleep(timeout)
cmd = ['shutdown', '-r', '-t', '0']
run_program(cmd, check=False)
raise SystemExit
@@ -327,7 +373,7 @@ def run_chkdsk_online():
if OS_VERSION >= 8:
cmd.extend(['/scan', '/perf'])
if CONEMU:
- cmd.extend(['-new_console:n', '-new_console:s33V'])
+ cmd.extend(['-new_console:nb', '-new_console:s33V'])
retried = False
# Run scan
@@ -360,7 +406,7 @@ def run_chkdsk_online():
def run_dism(repair=True):
"""Run DISM to either scan or repair component store health."""
- conemu_args = ['-new_console:n', '-new_console:s33V'] if CONEMU else []
+ conemu_args = ['-new_console:nb', '-new_console:s33V'] if CONEMU else []
# Bail early
if OS_VERSION < 8:
@@ -370,6 +416,7 @@ def run_dism(repair=True):
log_path = format_log_path(
log_name=f'DISM_{"Restore" if repair else "Scan"}Health', tool=True,
)
+ log_path.parent.mkdir(parents=True, exist_ok=True)
cmd = [
'DISM', '/Online', '/Cleanup-Image',
'/RestoreHealth' if repair else '/ScanHealth',
diff --git a/scripts/wk/std.py b/scripts/wk/std.py
index c568c499..e601befc 100644
--- a/scripts/wk/std.py
+++ b/scripts/wk/std.py
@@ -619,6 +619,7 @@ class TryAndPrint():
return {
'Exception': f_exception,
'Failed': bool(f_exception),
+ 'Message': result_msg,
'Output': output,
}
From 9a77a5cb9b103198e5d532444583bc17007bcab7 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Fri, 23 Apr 2021 23:39:11 -0600
Subject: [PATCH 19/43] Update Auto Repair sections
* Expanded saving/loading settings from registry
* Keep previous selections by default but allow changes
* Print previous session(s) to have the whole session info present
* Changed variable names for clarity (why am I so bad at this?)
---
scripts/auto_repairs.py | 26 ++---
scripts/wk/repairs/win.py | 193 ++++++++++++++++++++++++++++++--------
scripts/wk/std.py | 8 +-
3 files changed, 174 insertions(+), 53 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index 8096e5b8..2ddfc344 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -15,7 +15,7 @@ import wk # pylint: disable=wrong-import-position
class MenuEntry():
# pylint: disable=too-few-public-methods
"""Simple class to allow cleaner code below."""
- def __init__(self, name, function=None, **kwargs):
+ def __init__(self, name, function, **kwargs):
self.name = name
# Color reboot entries
@@ -28,29 +28,29 @@ class MenuEntry():
# Set details
self.details = {
+ 'Function': function,
'Selected': True,
**kwargs,
}
- if function:
- self.details['Function'] = function
# TODO: Deleteme
TRY_AND_PRINT = wk.std.TryAndPrint()
+TRY_AND_PRINT.width = 50
def placeholder_function(group, name):
TRY_AND_PRINT.run(f'{name}...', time.sleep, random.randint(1, 3))
- wk.repairs.win.save_settings(group, name, done=True)
+ wk.repairs.win.save_settings(group, name, done=True, result='SUCCESS')
def placeholder_reboot(group, name):
print('"Rebooting" shortly...')
time.sleep(random.randint(1, 3))
- wk.repairs.win.save_settings(group, name, done=True)
+ wk.repairs.win.save_settings(group, name, done=True, result='DONE')
raise SystemExit
# STATIC VARIABLES
BASE_MENUS = {
- 'Options': {
+ 'Groups': {
'Backup Settings': (
MenuEntry('Enable RegBack', placeholder_function),
MenuEntry('Enable System Restore', placeholder_function),
@@ -87,15 +87,15 @@ BASE_MENUS = {
MenuEntry('Enable Windows Updates', placeholder_function),
),
},
- 'Toggles': (
- MenuEntry('Kill Explorer'),
- MenuEntry('Run RKill at startup'),
- MenuEntry('Use Autologon'),
+ 'Options': (
+ MenuEntry('Kill Explorer', placeholder_function),
+ MenuEntry('Run RKill at startup', placeholder_function),
+ MenuEntry('Use Autologon', placeholder_function),
),
'Actions': (
- MenuEntry('Options'),
- MenuEntry('Start', Separator=True),
- MenuEntry('Quit'),
+ MenuEntry('Options', placeholder_function),
+ MenuEntry('Start', placeholder_function, Separator=True),
+ MenuEntry('Quit', placeholder_function),
),
}
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 6ae06700..97223d64 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -34,6 +34,7 @@ from wk.std import (
print_standard,
print_warning,
set_title,
+ show_data,
sleep,
strip_colors,
)
@@ -45,7 +46,9 @@ AUTO_REPAIR_DELAY_IN_SECONDS = 30
AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\Auto Repairs'
CONEMU = 'ConEmuPID' in os.environ
OS_VERSION = float(platform.win32_ver()[0])
+WIDTH = 50
TRY_PRINT = TryAndPrint()
+TRY_PRINT.width = WIDTH
TRY_PRINT.verbose = True
#for error in ('subprocess.CalledProcessError', 'FileNotFoundError'):
# TRY_PRINT.add_error(error)
@@ -60,22 +63,30 @@ def build_menus(base_menus, title):
# Main Menu
for entry in base_menus['Actions']:
menus['Main'].add_action(entry.name, entry.details)
- for group in base_menus['Options']:
+ for group in base_menus['Groups']:
menus['Main'].add_option(group, {'Selected': True})
# Options
menus['Options'] = Menu(title=f'{title}\n{color_string("Options", "GREEN")}')
- for entry in base_menus['Toggles']:
- menus['Options'].add_toggle(entry.name, entry.details)
- menus['Options'].add_action('Main Menu')
+ for entry in base_menus['Options']:
+ menus['Options'].add_option(entry.name, entry.details)
+ menus['Options'].add_action('All')
+ menus['Options'].add_action('None')
+ menus['Options'].add_action('Main Menu', {'Separator': True})
+ menus['Options'].add_action('Quit')
# Run groups
- for group, entries in base_menus['Options'].items():
+ for group, entries in base_menus['Groups'].items():
menus[group] = Menu(title=f'{title}\n{color_string(group, "GREEN")}')
- menus[group].disabled_str = 'Done'
+ menus[group].disabled_str = 'Locked'
for entry in entries:
menus[group].add_option(entry.name, entry.details)
- menus[group].add_action('Main Menu')
+ menus[group].add_action('All')
+ menus[group].add_action('None')
+ menus[group].add_action('Select Skipped Entries', {'Separator': True})
+ menus[group].add_action('Unlock All Entries')
+ menus[group].add_action('Main Menu', {'Separator': True})
+ menus[group].add_action('Quit')
# Initialize main menu display names
menus['Main'].update()
@@ -89,11 +100,16 @@ def end_session():
print_info('Ending repair session')
auto_admin_logon = '0'
- # Delete Auto Repairs session key
+ # Delete Auto Repairs keys
try:
reg_delete_value('HKCU', AUTO_REPAIR_KEY, 'SessionStarted')
except FileNotFoundError:
LOG.error('Ending repair session but session not started.')
+ try:
+ cmd = ['reg', 'delete', fr'HKCU\{AUTO_REPAIR_KEY}', '/f']
+ run_program(cmd)
+ except CalledProcessError:
+ LOG.error('Failed to remote Auto Repairs session settings')
# Remove logon task
cmd = [
@@ -134,13 +150,17 @@ def get_entry_settings(group, name):
"""Get menu entry settings from the registry, returns dict."""
key_path = fr'{AUTO_REPAIR_KEY}\{group}\{strip_colors(name)}'
settings = {}
- for value in ('Done', 'Failed', 'Result'):
+ for value in ('done', 'failed', 'result', 'selected', 'skipped', 'warning'):
try:
- settings[value] = reg_read_value('HKCU', key_path, value)
+ settings[value.title()] = reg_read_value('HKCU', key_path, value)
except FileNotFoundError:
# Ignore and use current settings
pass
+ # Disable previously run or skipped entries
+ if settings.get('Done', False) or settings.get('Skipped', False):
+ settings['Disabled'] = True
+
# Done
return settings
@@ -164,7 +184,7 @@ def init(menus):
def init_run(options):
"""Initialize Auto Repairs Run."""
- if options.toggles['Kill Explorer']['Selected']:
+ if options['Kill Explorer']['Selected']:
atexit.register(start_explorer)
kill_explorer()
# TODO: Sync Clock
@@ -187,8 +207,9 @@ def init_session(options):
run_program(cmd)
# One-time tasks
- if options.toggles['Use Autologon']['Selected']:
+ if options['Use Autologon']['Selected']:
run_tool('Sysinternals', 'Autologon')
+ # TODO: TDSSKiller?
# TODO: Re-enable reboot()
@@ -211,9 +232,6 @@ def load_settings(menus):
continue
for name in menu.options:
menu.options[name].update(get_entry_settings(group, name))
- if menu.options[name].get('Done', '0') == '1':
- menu.options[name]['Selected'] = False
- menu.options[name]['Disabled'] = True
def run_auto_repairs(base_menus):
@@ -238,36 +256,29 @@ def run_auto_repairs(base_menus):
# Show Menu
if session_started is None or not session_started:
- while True:
- update_main_menu(menus)
- selection = menus['Main'].simple_select(update=False)
- if selection[0] in base_menus['Options'] or selection[0] == 'Options':
- menus[selection[0]].advanced_select()
- elif 'Start' in selection:
- break
- elif 'Quit' in selection:
- if ask('End session?'):
- end_session()
- raise SystemExit
+ try:
+ show_main_menu(base_menus, menus)
+ except SystemExit:
+ if ask('End session?'):
+ end_session()
+ raise
# Re-check if a repair session was started
if session_started is None:
session_started = is_session_started()
# Start or resume repairs
- init_run(menus['Options'])
+ save_selection_settings(menus)
+ init_run(menus['Options'].options)
if not session_started:
- init_session(menus['Options'])
+ init_session(menus['Options'].options)
# Run repairs
clear_screen()
for group, menu in menus.items():
if group in ('Main', 'Options'):
continue
- for name, details in menu.options.items():
- if details.get('Done', None) == '1':
- continue
- details['Function'](group, name)
+ run_group(group, menu)
# Done
end_session()
@@ -275,12 +286,119 @@ def run_auto_repairs(base_menus):
pause('Press Enter to exit...')
-def save_settings(group, name, done=False, failed=False, result='Unknown'):
- """Load session settings from the registry."""
+def run_group(group, menu):
+ """Run entries in group if appropriate."""
+ print_info(f' {group}')
+ for name, details in menu.options.items():
+ name_str = strip_colors(name)
+ skipped = details.get('Skipped', False)
+ done = details.get('Done', False)
+ disabled = details.get('Disabled', False)
+ selected = details.get('Selected', False)
+
+ # Selection changed
+ if (skipped or done) and not disabled and selected:
+ save_settings(group, name, done=False, skipped=False)
+ details['Function'](group, name)
+ continue
+
+ # Previously skipped
+ if skipped:
+ show_data(f'{name_str}...', 'Skipped', 'YELLOW', width=WIDTH)
+ continue
+
+ # Previously ran
+ if done:
+ color = 'GREEN'
+ if details.get('Failed', False):
+ color = 'RED'
+ elif details.get('Warning', False):
+ color = 'YELLOW'
+ show_data(
+ f'{name_str}...', details.get('Result', 'Unknown'), color, width=WIDTH,
+ )
+ continue
+
+ # Not selected
+ if not selected:
+ show_data(f'{name_str}...', 'Skipped', 'YELLOW', width=WIDTH)
+ save_settings(group, name, skipped=True)
+ continue
+
+ # Selected
+ details['Function'](group, name)
+
+
+def save_selection_settings(menus):
+ """Save selections in the registry."""
+ for group, menu in menus.items():
+ if group == 'Main':
+ continue
+ for name, details in menu.options.items():
+ save_settings(
+ group, name,
+ disabled=details.get('Disabled', False),
+ selected=details.get('Selected', False),
+ )
+
+
+def save_settings(group, name, **kwargs):
+ """Save entry settings in the registry."""
key_path = fr'{AUTO_REPAIR_KEY}\{group}\{strip_colors(name)}'
- reg_set_value('HKCU', key_path, 'Done', '1' if done else '0', 'SZ')
- reg_set_value('HKCU', key_path, 'Failed', '1' if failed else '0', 'SZ')
- reg_set_value('HKCU', key_path, 'Result', result, 'SZ')
+ for value_name, data in kwargs.items():
+ if isinstance(data, bool):
+ data = 1 if data else 0
+ if isinstance(data, int):
+ data_type = 'DWORD'
+ elif isinstance(data, str):
+ data_type = 'SZ'
+ else:
+ raise TypeError(f'Invalid data: "{data}" ({type(data)})')
+ reg_set_value('HKCU', key_path, value_name, data, data_type)
+
+
+def show_main_menu(base_menus, menus):
+ """Show main menu and handle actions."""
+ while True:
+ update_main_menu(menus)
+ selection = menus['Main'].simple_select(update=False)
+ if selection[0] in base_menus['Groups'] or selection[0] == 'Options':
+ show_sub_menu(menus[selection[0]])
+ elif 'Start' in selection:
+ break
+ elif 'Quit' in selection:
+ raise SystemExit
+
+
+def show_sub_menu(menu):
+ """Show sub-menu and handle sub-menu actions."""
+ while True:
+ selection = menu.advanced_select()
+ if 'Main Menu' in selection:
+ break
+ if 'Quit' in selection:
+ raise SystemExit
+
+ # Modify entries
+ key = 'Selected'
+ unlock_all = False
+ unlock_skipped = False
+ if 'Select Skipped Entries' in selection:
+ key = 'Disabled'
+ unlock_skipped = True
+ value = False
+ if 'Unlock All Entries' in selection:
+ key = 'Disabled'
+ unlock_all = True
+ value = False
+ else:
+ value = 'All' in selection
+ for name in menu.options:
+ if (unlock_all
+ or (unlock_skipped and not menu.options[name].get('Selected', False))
+ or not menu.options[name].get('Disabled', False)
+ ):
+ menu.options[name][key] = value
def update_main_menu(menus):
@@ -318,6 +436,7 @@ def auto_dism(group, name):
save_settings(
group, name, done=True,
failed=result['Failed'],
+ warning=not result['Failed'] and needs_reboot,
result=result['Message'],
)
diff --git a/scripts/wk/std.py b/scripts/wk/std.py
index e601befc..9f235a1f 100644
--- a/scripts/wk/std.py
+++ b/scripts/wk/std.py
@@ -985,11 +985,13 @@ def set_title(title):
print_error('Setting the title is only supported under Windows.')
-def show_data(message, data, color=None):
- """Display info using standard WIDTH and INDENT."""
+def show_data(message, data, color=None, indent=None, width=None):
+ """Display info using default or provided indent and width."""
colors = (None, color if color else None)
+ indent = INDENT if indent is None else indent
+ width = WIDTH if width is None else width
print_colored(
- (f'{" "*INDENT}{message:<{WIDTH}}', data),
+ (f'{" "*indent}{message:<{width}}', data),
colors,
log=True,
sep='',
From 005d4d1ea698c06b2dbaae75226fd81c144fc44f Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 24 Apr 2021 16:55:17 -0600
Subject: [PATCH 20/43] Search all modules for a matching exception
---
scripts/wk/std.py | 36 +++++++++++++++++++++++++++---------
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/scripts/wk/std.py b/scripts/wk/std.py
index 9f235a1f..fe31e50c 100644
--- a/scripts/wk/std.py
+++ b/scripts/wk/std.py
@@ -18,6 +18,7 @@ import time
import traceback
from collections import OrderedDict
+from functools import cache
import requests
@@ -498,24 +499,41 @@ class TryAndPrint():
# Done
return result_msg
+ @cache
def _get_exception(self, name):
# pylint: disable=no-self-use
"""Get exception by name, returns exception object.
[Doctest]
- >>> self._get_exception('AttributeError')
+ >>> t = TryAndPrint()
+ >>> t._get_exception('AttributeError')
- >>> self._get_exception('CalledProcessError')
+ >>> t._get_exception('CalledProcessError')
- >>> self._get_exception('GenericError')
-
+ >>> t._get_exception('GenericError')
+
"""
LOG.debug('Getting exception: %s', name)
- try:
- obj = getattr(sys.modules[__name__], name)
- except AttributeError:
- # Try builtin classes
- obj = getattr(sys.modules['builtins'], name)
+ obj = getattr(sys.modules[__name__], name, None)
+ if obj:
+ return obj
+
+ # Try builtin classes
+ obj = getattr(sys.modules['builtins'], name, None)
+ if obj:
+ return obj
+
+ # Try all modules
+ for _mod in sys.modules:
+ obj = getattr(sys.modules[_mod], name, None)
+ if obj:
+ break
+
+ # Check if not found
+ if not obj:
+ raise AttributeError(f'Failed to find exception: {name}')
+
+ # Done
return obj
def _log_result(self, message, result_msg):
From b2c94113d9ebf63e2da48d0ad08639d2fc97bf60 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 24 Apr 2021 19:03:41 -0600
Subject: [PATCH 21/43] Remove date from DEFAULT_LOG_DIR
---
scripts/wk/log.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/scripts/wk/log.py b/scripts/wk/log.py
index cf7b373a..2cabd520 100644
--- a/scripts/wk/log.py
+++ b/scripts/wk/log.py
@@ -17,8 +17,7 @@ if os.name == 'nt':
# Example: "C:\WK\1955-11-05\WizardKit"
DEFAULT_LOG_DIR = (
f'{os.environ.get("SYSTEMDRIVE", "C:")}/'
- f'{cfg.main.KIT_NAME_SHORT}/'
- f'{time.strftime("%Y-%m-%d")}'
+ f'{cfg.main.KIT_NAME_SHORT}/Logs/'
)
else:
# Example: "/home/tech/Logs"
From 1dbad4bafec756b4211fbb2a0db84959926fe178 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 24 Apr 2021 20:18:45 -0600
Subject: [PATCH 22/43] Split result variables to result and message
result is for the return variable from TryAndPrint calls.
message is the text to display or save to the registry.
---
scripts/auto_repairs.py | 6 +++---
scripts/wk/repairs/win.py | 25 ++++++++++++++++++-------
2 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index 2ddfc344..00cea939 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -38,13 +38,13 @@ class MenuEntry():
TRY_AND_PRINT = wk.std.TryAndPrint()
TRY_AND_PRINT.width = 50
def placeholder_function(group, name):
- TRY_AND_PRINT.run(f'{name}...', time.sleep, random.randint(1, 3))
- wk.repairs.win.save_settings(group, name, done=True, result='SUCCESS')
+ result = TRY_AND_PRINT.run(f'{name}...', time.sleep, random.randint(1, 3))
+ wk.repairs.win.save_settings(group, name, result=result)
def placeholder_reboot(group, name):
print('"Rebooting" shortly...')
time.sleep(random.randint(1, 3))
- wk.repairs.win.save_settings(group, name, done=True, result='DONE')
+ wk.repairs.win.save_settings(group, name, done=True, message='DONE')
raise SystemExit
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 97223d64..150229cd 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -150,7 +150,7 @@ def get_entry_settings(group, name):
"""Get menu entry settings from the registry, returns dict."""
key_path = fr'{AUTO_REPAIR_KEY}\{group}\{strip_colors(name)}'
settings = {}
- for value in ('done', 'failed', 'result', 'selected', 'skipped', 'warning'):
+ for value in ('done', 'failed', 'message', 'selected', 'skipped', 'warning'):
try:
settings[value.title()] = reg_read_value('HKCU', key_path, value)
except FileNotFoundError:
@@ -310,12 +310,13 @@ def run_group(group, menu):
# Previously ran
if done:
color = 'GREEN'
- if details.get('Failed', False):
- color = 'RED'
- elif details.get('Warning', False):
+ if details.get('Warning', False):
color = 'YELLOW'
+ elif details.get('Failed', False):
+ color = 'RED'
show_data(
- f'{name_str}...', details.get('Result', 'Unknown'), color, width=WIDTH,
+ f'{name_str}...',
+ details.get('Message', 'Unknown'), color, width=WIDTH,
)
continue
@@ -342,9 +343,19 @@ def save_selection_settings(menus):
)
-def save_settings(group, name, **kwargs):
+def save_settings(group, name, result=None, **kwargs):
"""Save entry settings in the registry."""
key_path = fr'{AUTO_REPAIR_KEY}\{group}\{strip_colors(name)}'
+
+ # Get values from TryAndPrint result
+ if result:
+ kwargs.update({
+ 'done': True,
+ 'failed': result['Failed'],
+ 'message': result['Message'],
+ })
+
+ # Write values to registry
for value_name, data in kwargs.items():
if isinstance(data, bool):
data = 1 if data else 0
@@ -437,7 +448,7 @@ def auto_dism(group, name):
group, name, done=True,
failed=result['Failed'],
warning=not result['Failed'] and needs_reboot,
- result=result['Message'],
+ message=result['Message'],
)
# Done
From a7db972ba5883649964cc2c45291490facdb1742 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 24 Apr 2021 20:23:08 -0600
Subject: [PATCH 23/43] Get repair functions by name instead of full path
---
scripts/auto_repairs.py | 16 ++++++++--------
scripts/wk/repairs/win.py | 11 +++++++++++
2 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index 00cea939..1e47cfeb 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -15,7 +15,7 @@ import wk # pylint: disable=wrong-import-position
class MenuEntry():
# pylint: disable=too-few-public-methods
"""Simple class to allow cleaner code below."""
- def __init__(self, name, function, **kwargs):
+ def __init__(self, name, function=None, **kwargs):
self.name = name
# Color reboot entries
@@ -64,7 +64,7 @@ BASE_MENUS = {
MenuEntry('Reset Windows Updates', placeholder_function),
MenuEntry('Reboot', placeholder_reboot),
MenuEntry('CHKDSK', placeholder_function),
- MenuEntry('DISM RestoreHealth', wk.repairs.win.auto_dism),
+ MenuEntry('DISM RestoreHealth', 'auto_dism'),
MenuEntry('SFC Scan', placeholder_function),
MenuEntry('Fix File Associations', placeholder_function),
MenuEntry('Clear Proxy Settings', placeholder_function),
@@ -88,14 +88,14 @@ BASE_MENUS = {
),
},
'Options': (
- MenuEntry('Kill Explorer', placeholder_function),
- MenuEntry('Run RKill at startup', placeholder_function),
- MenuEntry('Use Autologon', placeholder_function),
+ MenuEntry('Kill Explorer'),
+ MenuEntry('Run RKill at startup'),
+ MenuEntry('Use Autologon'),
),
'Actions': (
- MenuEntry('Options', placeholder_function),
- MenuEntry('Start', placeholder_function, Separator=True),
- MenuEntry('Quit', placeholder_function),
+ MenuEntry('Options'),
+ MenuEntry('Start', Separator=True),
+ MenuEntry('Quit'),
),
}
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 150229cd..b3f6353d 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -91,6 +91,17 @@ def build_menus(base_menus, title):
# Initialize main menu display names
menus['Main'].update()
+ # Fix Function references
+ for group, menu in menus.items():
+ if group not in base_menus['Groups']:
+ continue
+ for name in menu.options:
+ _function = menu.options[name]['Function']
+ if isinstance(_function, str):
+ menu.options[name]['Function'] = getattr(
+ sys.modules[__name__], _function,
+ )
+
# Done
return menus
From 078859838af79f67c62855d78e6f5b257127c7d1 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 24 Apr 2021 20:24:39 -0600
Subject: [PATCH 24/43] Add 'Backup Settings' group functions
---
scripts/auto_repairs.py | 55 ++++++++---------
scripts/wk/repairs/win.py | 123 ++++++++++++++++++++++++++++++++++++--
2 files changed, 147 insertions(+), 31 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index 1e47cfeb..55aff945 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -52,39 +52,40 @@ def placeholder_reboot(group, name):
BASE_MENUS = {
'Groups': {
'Backup Settings': (
- MenuEntry('Enable RegBack', placeholder_function),
- MenuEntry('Enable System Restore', placeholder_function),
- MenuEntry('Create System Restore', placeholder_function),
- MenuEntry('Backup Browsers', placeholder_function),
- MenuEntry('Backup Power Plans', placeholder_function),
- MenuEntry('Backup Registry', placeholder_function),
+ MenuEntry('Enable RegBack', 'auto_enable_regback'),
+ MenuEntry('Enable System Restore', 'auto_system_restore_enable'),
+ MenuEntry('Set System Restore Size', 'auto_system_restore_set_size'),
+ MenuEntry('Create System Restore', 'auto_system_restore_create'),
+ MenuEntry('Backup Browsers', placeholder_function),
+ MenuEntry('Backup Power Plans', 'auto_backup_power_plans'),
+ MenuEntry('Backup Registry', 'auto_backup_registry'),
),
'Windows Repairs': (
- MenuEntry('Disable Windows Updates', placeholder_function),
- MenuEntry('Reset Windows Updates', placeholder_function),
- MenuEntry('Reboot', placeholder_reboot),
- MenuEntry('CHKDSK', placeholder_function),
- MenuEntry('DISM RestoreHealth', 'auto_dism'),
- MenuEntry('SFC Scan', placeholder_function),
- MenuEntry('Fix File Associations', placeholder_function),
- MenuEntry('Clear Proxy Settings', placeholder_function),
- MenuEntry('Disable Pending Renames', placeholder_function),
- MenuEntry('Registry Repairs', placeholder_function),
- MenuEntry('Repair Safe Mode', placeholder_function),
- MenuEntry('Reset UAC', placeholder_function),
- MenuEntry('Reset Windows Policies', placeholder_function),
+ MenuEntry('Disable Windows Updates', placeholder_function),
+ MenuEntry('Reset Windows Updates', placeholder_function),
+ MenuEntry('Reboot', placeholder_reboot),
+ MenuEntry('CHKDSK', placeholder_function),
+ MenuEntry('DISM RestoreHealth', 'auto_dism'),
+ MenuEntry('SFC Scan', placeholder_function),
+ MenuEntry('Fix File Associations', placeholder_function),
+ MenuEntry('Clear Proxy Settings', placeholder_function),
+ MenuEntry('Disable Pending Renames', placeholder_function),
+ MenuEntry('Registry Repairs', placeholder_function),
+ MenuEntry('Repair Safe Mode', placeholder_function),
+ MenuEntry('Reset UAC', placeholder_function),
+ MenuEntry('Reset Windows Policies', placeholder_function),
),
'Malware Cleanup': (
- MenuEntry('BleachBit', placeholder_function),
- MenuEntry('HitmanPro', placeholder_function),
- MenuEntry('KVRT', placeholder_function),
- MenuEntry('Windows Defender', placeholder_function),
- MenuEntry('Reboot', placeholder_reboot),
+ MenuEntry('BleachBit', placeholder_function),
+ MenuEntry('HitmanPro', placeholder_function),
+ MenuEntry('KVRT', placeholder_function),
+ MenuEntry('Windows Defender', placeholder_function),
+ MenuEntry('Reboot', placeholder_reboot),
),
'Manual Steps': (
- MenuEntry('AdwCleaner', placeholder_function),
- MenuEntry('IO Bit Uninstaller', placeholder_function),
- MenuEntry('Enable Windows Updates', placeholder_function),
+ MenuEntry('AdwCleaner', placeholder_function),
+ MenuEntry('IO Bit Uninstaller', placeholder_function),
+ MenuEntry('Enable Windows Updates', placeholder_function),
),
},
'Options': (
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index b3f6353d..eb080d95 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -5,9 +5,11 @@ import atexit
import logging
import os
import platform
+import re
import sys
+import time
-from subprocess import CalledProcessError
+from subprocess import CalledProcessError, DEVNULL
from wk.cfg.main import KIT_NAME_FULL
from wk.exe import get_procs, run_program, popen_program, wait_for_procs
@@ -47,11 +49,12 @@ AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\Auto Repairs'
CONEMU = 'ConEmuPID' in os.environ
OS_VERSION = float(platform.win32_ver()[0])
WIDTH = 50
+SYSTEMDRIVE = os.environ.get('SYSTEMDRIVE')
TRY_PRINT = TryAndPrint()
TRY_PRINT.width = WIDTH
TRY_PRINT.verbose = True
-#for error in ('subprocess.CalledProcessError', 'FileNotFoundError'):
-# TRY_PRINT.add_error(error)
+for error in ('CalledProcessError', 'FileNotFoundError'):
+ TRY_PRINT.add_error(error)
# Auto Repairs
@@ -442,8 +445,20 @@ def update_main_menu(menus):
# Auto Repairs: Wrapper Functions
+def auto_backup_power_plans(group, name):
+ """Backup power plans."""
+ result = TRY_PRINT.run('Backup Power Plans...', export_power_plans)
+ save_settings(group, name, result=result)
+
+
+def auto_backup_registry(group, name):
+ """Backup registry."""
+ result = TRY_PRINT.run('Backup Registry...', backup_registry)
+ save_settings(group, name, result=result)
+
+
def auto_dism(group, name):
- """Auto DISM repairs, returns bool."""
+ """Run DISM repairs."""
needs_reboot = False
result = TRY_PRINT.run('DISM (RestoreHealth)...', run_dism)
@@ -467,7 +482,73 @@ def auto_dism(group, name):
reboot()
+def auto_enable_regback(group, name):
+ """Enable RegBack."""
+ result = TRY_PRINT.run(
+ 'Enable RegBack...', reg_set_value, 'HKLM',
+ r'System\CurrentControlSet\Control\Session Manager\Configuration Manager',
+ 'EnablePeriodicBackup', 1, 'DWORD',
+ )
+ save_settings(group, name, result=result)
+
+
+def auto_system_restore_create(group, name):
+ """Create a System Restore point."""
+ result = TRY_PRINT.run(
+ 'Create System Restore...', create_system_restore_point,
+ )
+ save_settings(group, name, result=result)
+
+
+def auto_system_restore_enable(group, name):
+ """Enable System Restore."""
+ cmd = [
+ 'powershell', '-Command', 'Enable-ComputerRestore',
+ '-Drive', SYSTEMDRIVE,
+ ]
+ result = TRY_PRINT.run('Enable System Restore...', run_program, cmd=cmd)
+ save_settings(group, name, result=result)
+
+
+def auto_system_restore_set_size(group, name):
+ """Set System Restore size."""
+ result = TRY_PRINT.run('Set System Restore Size...', set_system_restore_size)
+ save_settings(group, name, result=result)
+
+
+# Misc Functions
+def set_backup_path(name, date=False):
+ """Set backup path, returns pathlib.Path."""
+ backup_path = format_log_path(log_name=f'../Backups/{name}').with_suffix('')
+ if date:
+ backup_path = backup_path.joinpath(time.strftime('%Y-%m-%d'))
+ return backup_path.resolve()
+
+
+# Tool Functions
+def backup_registry():
+ """Backup Registry."""
+ backup_path = set_backup_path('Registry', date=True)
+ backup_path.parent.mkdir(parents=True, exist_ok=True)
+ run_tool('Erunt', 'ERUNT', backup_path, 'sysreg', 'curuser', 'otherusers')
+
+
# OS Built-in Functions
+def create_system_restore_point():
+ """Create System Restore point."""
+ cmd = [
+ 'powershell', '-Command', 'Checkpoint-Computer',
+ '-Description', f'{KIT_NAME_FULL}-AutoRepairs',
+ ]
+ too_recent = (
+ 'WARNING: A new system restore point cannot be created'
+ 'because one has already been created within the past'
+ )
+ proc = run_program(cmd)
+ if too_recent in proc.stdout:
+ raise GenericWarning('Skipped, a restore point was created too recently')
+
+
def disable_windows_updates():
"""Disable and stop Windows Updates."""
stop_service('wuauserv')
@@ -479,6 +560,31 @@ def enable_windows_updates():
enable_service('wuauserv', 'demand')
+def export_power_plans():
+ """Export existing power plans."""
+ backup_path = set_backup_path('Power Plans', date=True)
+ backup_path.mkdir(parents=True, exist_ok=True)
+ cmd = ['powercfg', '/L']
+ proc = run_program(cmd)
+ plans = {}
+
+ # Get plans
+ for line in proc.stdout.splitlines():
+ line = line.strip()
+ match = re.match(r'^Power Scheme GUID: (.{36})\s+\((.*)\)\s*(\*?)', line)
+ if match:
+ name = match.group(2)
+ if match.group(3):
+ name += ' (Default)'
+ plans[name] = match.group(1)
+
+ # Backup plans to disk
+ for name, guid in plans.items():
+ out_path = backup_path.joinpath(f'{name}.pow')
+ cmd = ['powercfg', '-export', out_path, guid]
+ run_program(cmd)
+
+
def kill_explorer():
"""Kill all Explorer processes."""
cmd = ['taskkill', '/im', 'explorer.exe', '/f']
@@ -608,6 +714,15 @@ def run_sfc_scan():
raise OSError
+def set_system_restore_size(size=8):
+ """Set System Restore size."""
+ cmd = [
+ 'vssadmin', 'Resize', 'ShadowStorage',
+ f'/On={SYSTEMDRIVE}', f'/For={SYSTEMDRIVE}', f'/MaxSize={size}%',
+ ]
+ run_program(cmd, pipe=False, stderr=DEVNULL, stdout=DEVNULL)
+
+
def start_explorer():
"""Start Explorer."""
popen_program(['explorer.exe'])
From 973dad32403227fc9c023daf170faf7ff7b817cc Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Thu, 29 Apr 2021 19:37:09 -0600
Subject: [PATCH 25/43] Add "auto_" windows updates functions
---
scripts/auto_repairs.py | 8 ++++----
scripts/wk/io.py | 6 ++++++
scripts/wk/repairs/win.py | 37 ++++++++++++++++++++++++++++++++++++-
3 files changed, 46 insertions(+), 5 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index 55aff945..aabb9ba6 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -56,13 +56,13 @@ BASE_MENUS = {
MenuEntry('Enable System Restore', 'auto_system_restore_enable'),
MenuEntry('Set System Restore Size', 'auto_system_restore_set_size'),
MenuEntry('Create System Restore', 'auto_system_restore_create'),
- MenuEntry('Backup Browsers', placeholder_function),
+ #MenuEntry('Backup Browsers', #TODO),
MenuEntry('Backup Power Plans', 'auto_backup_power_plans'),
MenuEntry('Backup Registry', 'auto_backup_registry'),
),
'Windows Repairs': (
- MenuEntry('Disable Windows Updates', placeholder_function),
- MenuEntry('Reset Windows Updates', placeholder_function),
+ MenuEntry('Disable Windows Updates', 'auto_windows_updates_disable'),
+ MenuEntry('Reset Windows Updates', 'auto_windows_updates_reset'),
MenuEntry('Reboot', placeholder_reboot),
MenuEntry('CHKDSK', placeholder_function),
MenuEntry('DISM RestoreHealth', 'auto_dism'),
@@ -85,7 +85,7 @@ BASE_MENUS = {
'Manual Steps': (
MenuEntry('AdwCleaner', placeholder_function),
MenuEntry('IO Bit Uninstaller', placeholder_function),
- MenuEntry('Enable Windows Updates', placeholder_function),
+ MenuEntry('Enable Windows Updates', 'auto_windows_updates_enable'),
),
},
'Options': (
diff --git a/scripts/wk/io.py b/scripts/wk/io.py
index f81d9a26..2cbbef46 100644
--- a/scripts/wk/io.py
+++ b/scripts/wk/io.py
@@ -192,5 +192,11 @@ def recursive_copy(source, dest, overwrite=False):
raise FileExistsError(f'Refusing to delete file: {dest}')
+def rename_item(path, new_path):
+ """Rename item, returns pathlib.Path."""
+ path = pathlib.Path(path)
+ return path.rename(new_path)
+
+
if __name__ == '__main__':
print("This file is not meant to be called directly.")
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index eb080d95..206683ea 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -13,6 +13,7 @@ from subprocess import CalledProcessError, DEVNULL
from wk.cfg.main import KIT_NAME_FULL
from wk.exe import get_procs, run_program, popen_program, wait_for_procs
+from wk.io import delete_folder, rename_item
from wk.kit.tools import run_tool
from wk.log import format_log_path, update_log_path
from wk.os.win import (
@@ -516,6 +517,30 @@ def auto_system_restore_set_size(group, name):
save_settings(group, name, result=result)
+def auto_windows_updates_disable(group, name):
+ """Disable Windows Updates."""
+ result = TRY_PRINT.run('Disable Windows Updates...', disable_windows_updates)
+ if result['Failed']:
+ # Reboot and try again?
+ reboot()
+ save_settings(group, name, result=result)
+
+
+def auto_windows_updates_enable(group, name):
+ """Enable Windows Updates."""
+ result = TRY_PRINT.run('Enable Windows Updates...', enable_windows_updates)
+ save_settings(group, name, result=result)
+
+
+def auto_windows_updates_reset(group, name):
+ """Reset Windows Updates."""
+ result = TRY_PRINT.run('Reset Windows Updates...', reset_windows_updates)
+ if result['Failed']:
+ # Reboot and try again?
+ reboot()
+ save_settings(group, name, result=result)
+
+
# Misc Functions
def set_backup_path(name, date=False):
"""Set backup path, returns pathlib.Path."""
@@ -551,8 +576,8 @@ def create_system_restore_point():
def disable_windows_updates():
"""Disable and stop Windows Updates."""
- stop_service('wuauserv')
disable_service('wuauserv')
+ stop_service('wuauserv')
def enable_windows_updates():
@@ -601,6 +626,16 @@ def reboot(timeout=10):
raise SystemExit
+def reset_windows_updates():
+ """Reset Windows Updates."""
+ system_root = os.environ.get('SYSTEMROOT', 'C:/Windows')
+ rename_item(
+ f'{system_root}/SoftwareDistribution',
+ f'{system_root}/SoftwareDistribution.old',
+ )
+ delete_folder(f'{system_root}/SoftwareDistribution.old', force=True)
+
+
def run_chkdsk_offline():
"""Set filesystem 'dirty bit' to force a CHKDSK during startup."""
cmd = ['fsutil', 'dirty', 'set', os.environ.get('SYSTEMDRIVE', 'C:')]
From e17c96d4657a9c23519d1cb4568527d292a997fa Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Thu, 29 Apr 2021 21:08:50 -0600
Subject: [PATCH 26/43] Add auto_chkdsk()
---
scripts/auto_repairs.py | 4 ++--
scripts/wk/repairs/win.py | 27 +++++++++++++++++++++++++++
2 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index aabb9ba6..1cfb8da5 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -29,7 +29,7 @@ class MenuEntry():
# Set details
self.details = {
'Function': function,
- 'Selected': True,
+ 'Selected': False,
**kwargs,
}
@@ -64,7 +64,7 @@ BASE_MENUS = {
MenuEntry('Disable Windows Updates', 'auto_windows_updates_disable'),
MenuEntry('Reset Windows Updates', 'auto_windows_updates_reset'),
MenuEntry('Reboot', placeholder_reboot),
- MenuEntry('CHKDSK', placeholder_function),
+ MenuEntry('CHKDSK', 'auto_chkdsk'),
MenuEntry('DISM RestoreHealth', 'auto_dism'),
MenuEntry('SFC Scan', placeholder_function),
MenuEntry('Fix File Associations', placeholder_function),
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 206683ea..31a661fe 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -369,6 +369,8 @@ def save_settings(group, name, result=None, **kwargs):
'failed': result['Failed'],
'message': result['Message'],
})
+ if isinstance(result['Exception'], GenericWarning):
+ kwargs['warning'] = True
# Write values to registry
for value_name, data in kwargs.items():
@@ -458,6 +460,31 @@ def auto_backup_registry(group, name):
save_settings(group, name, result=result)
+def auto_chkdsk(group, name):
+ """Run CHKDSK repairs."""
+ needs_reboot = False
+ system_disk = os.environ.get('SYSTEMDRIVE', 'C:')
+ result = TRY_PRINT.run(f'CHKDSK ({system_disk})...', run_chkdsk_online)
+
+ # Run offline CHKDSK if required
+ if result['Failed'] and 'Repaired' not in result['Message']:
+ needs_reboot = True
+ result = TRY_PRINT.run(
+ f'Scheduling offline CHKDSK ({system_disk})...',
+ run_chkdsk_offline,
+ )
+ if not result['Failed']:
+ # Successfully set dirty bit to force offline check
+ # Set result['Failed'] to True because we failed to repair online
+ result['Failed'] = True
+ result['Message'] = 'Scheduled offline repairs'
+
+ # Done
+ save_settings(group, name, result=result)
+ if needs_reboot:
+ reboot()
+
+
def auto_dism(group, name):
"""Run DISM repairs."""
needs_reboot = False
From 7f40a52444097248feb3fe869ffacfd1ce5495e0 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Fri, 30 Apr 2021 00:06:22 -0600
Subject: [PATCH 27/43] Add remaining Windows Repairs functions
---
scripts/auto_repairs.py | 18 +++---
scripts/wk/cfg/tools.py | 1 +
scripts/wk/repairs/win.py | 120 ++++++++++++++++++++++++++++++++++++--
3 files changed, 123 insertions(+), 16 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index 1cfb8da5..65cce403 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -63,24 +63,22 @@ BASE_MENUS = {
'Windows Repairs': (
MenuEntry('Disable Windows Updates', 'auto_windows_updates_disable'),
MenuEntry('Reset Windows Updates', 'auto_windows_updates_reset'),
- MenuEntry('Reboot', placeholder_reboot),
+ MenuEntry('Reboot', 'auto_reboot'),
MenuEntry('CHKDSK', 'auto_chkdsk'),
MenuEntry('DISM RestoreHealth', 'auto_dism'),
- MenuEntry('SFC Scan', placeholder_function),
- MenuEntry('Fix File Associations', placeholder_function),
- MenuEntry('Clear Proxy Settings', placeholder_function),
- MenuEntry('Disable Pending Renames', placeholder_function),
- MenuEntry('Registry Repairs', placeholder_function),
- MenuEntry('Repair Safe Mode', placeholder_function),
- MenuEntry('Reset UAC', placeholder_function),
- MenuEntry('Reset Windows Policies', placeholder_function),
+ MenuEntry('SFC Scan', 'auto_sfc'),
+ MenuEntry('Clear Proxy Settings', 'auto_reset_proxy'),
+ MenuEntry('Disable Pending Renames', 'auto_disable_pending_renames'),
+ MenuEntry('Registry Repairs', 'auto_repair_registry'),
+ MenuEntry('Reset UAC', 'auto_restore_uac_defaults'),
+ MenuEntry('Reset Windows Policies', 'auto_reset_windows_policies'),
),
'Malware Cleanup': (
MenuEntry('BleachBit', placeholder_function),
MenuEntry('HitmanPro', placeholder_function),
MenuEntry('KVRT', placeholder_function),
MenuEntry('Windows Defender', placeholder_function),
- MenuEntry('Reboot', placeholder_reboot),
+ MenuEntry('Reboot', 'auto_reboot'),
),
'Manual Steps': (
MenuEntry('AdwCleaner', placeholder_function),
diff --git a/scripts/wk/cfg/tools.py b/scripts/wk/cfg/tools.py
index e32d3c2e..d7eaa837 100644
--- a/scripts/wk/cfg/tools.py
+++ b/scripts/wk/cfg/tools.py
@@ -46,6 +46,7 @@ SOURCES = {
'ProduKey32': 'http://www.nirsoft.net/utils/produkey.zip',
'ProduKey64': 'http://www.nirsoft.net/utils/produkey-x64.zip',
'PuTTY': 'https://the.earth.li/~sgtatham/putty/latest/w32/putty.zip',
+ 'RegDelNull': 'https://download.sysinternals.com/files/Regdelnull.zip',
'RKill': 'https://download.bleepingcomputer.com/grinler/rkill.exe',
'Samsung Magician': 'https://s3.ap-northeast-2.amazonaws.com/global.semi.static/SAMSUNG_SSD_v5_3_0_181121/CD0C7CC1BE00525FAC4675B9E502899B41D5C3909ECE3AA2FB6B74A766B2A1EA/Samsung_Magician_Installer.zip',
'SDIO Themes': 'http://snappy-driver-installer.org/downloads/SDIO_Themes.zip',
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 31a661fe..14e9b5c9 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -20,6 +20,7 @@ from wk.os.win import (
reg_delete_value,
reg_read_value,
reg_set_value,
+ reg_write_settings,
disable_service,
enable_service,
stop_service,
@@ -48,9 +49,22 @@ LOG = logging.getLogger(__name__)
AUTO_REPAIR_DELAY_IN_SECONDS = 30
AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\Auto Repairs'
CONEMU = 'ConEmuPID' in os.environ
+GPUPDATE_SUCCESS_STRINGS = (
+ 'Computer Policy update has completed successfully.',
+ 'User Policy update has completed successfully.',
+ )
OS_VERSION = float(platform.win32_ver()[0])
-WIDTH = 50
+REG_UAC_DEFAULT_SETTINGS = {
+ 'HKLM': {
+ r'Software\Microsoft\Windows\CurrentVersion\Policies\System': (
+ ('ConsentPromptBehaviorAdmin', 5, 'DWORD'),
+ ('ConsentPromptBehaviorUser', 3, 'DWORD'),
+ ('EnableLUA', 1, 'DWORD'),
+ ),
+ },
+ }
SYSTEMDRIVE = os.environ.get('SYSTEMDRIVE')
+WIDTH = 50
TRY_PRINT = TryAndPrint()
TRY_PRINT.width = WIDTH
TRY_PRINT.verbose = True
@@ -290,6 +304,9 @@ def run_auto_repairs(base_menus):
# Run repairs
clear_screen()
+ print_standard(title)
+ print('')
+ print_info('Running repairs')
for group, menu in menus.items():
if group in ('Main', 'Options'):
continue
@@ -485,6 +502,14 @@ def auto_chkdsk(group, name):
reboot()
+def auto_disable_pending_renames(group, name):
+ """Disable pending renames."""
+ result = TRY_PRINT.run(
+ 'Disabling pending renames...', disable_pending_renames,
+ )
+ save_settings(group, name, result=result)
+
+
def auto_dism(group, name):
"""Run DISM repairs."""
needs_reboot = False
@@ -520,6 +545,47 @@ def auto_enable_regback(group, name):
save_settings(group, name, result=result)
+def auto_reboot(group, name):
+ """Reboot the system."""
+ save_settings(group, name, done=True, failed=False, message='DONE')
+ print('')
+ reboot(30)
+
+
+def auto_repair_registry(group, name):
+ """Delete registry keys with embedded null characters."""
+ result = TRY_PRINT.run(
+ 'Running Registry repairs...', delete_registry_null_keys,
+ )
+ save_settings(group, name, result=result)
+
+
+def auto_reset_proxy(group, name):
+ """Reset proxy settings."""
+ result = TRY_PRINT.run('Clearing proxy settings...', reset_proxy)
+ save_settings(group, name, result=result)
+
+
+def auto_reset_windows_policies(group, name):
+ """Reset Windows policies to defaults."""
+ result = TRY_PRINT.run(
+ 'Resetting Windows policies...', reset_windows_policies,
+ )
+ save_settings(group, name, result=result)
+
+
+def auto_restore_uac_defaults(group, name):
+ """Restore UAC default settings."""
+ result = TRY_PRINT.run('Restoring UAC defaults...', restore_uac_defaults)
+ save_settings(group, name, result=result)
+
+
+def auto_sfc(group, name):
+ """Run SFC repairs."""
+ result = TRY_PRINT.run('SFC Scan...', run_sfc_scan)
+ save_settings(group, name, result=result)
+
+
def auto_system_restore_create(group, name):
"""Create a System Restore point."""
result = TRY_PRINT.run(
@@ -585,6 +651,11 @@ def backup_registry():
run_tool('Erunt', 'ERUNT', backup_path, 'sysreg', 'curuser', 'otherusers')
+def delete_registry_null_keys():
+ """Delete registry keys with embedded null characters."""
+ run_tool('RegDelNull', 'RegDelNull', '-s', '-y', cbin=True)
+
+
# OS Built-in Functions
def create_system_restore_point():
"""Create System Restore point."""
@@ -601,6 +672,14 @@ def create_system_restore_point():
raise GenericWarning('Skipped, a restore point was created too recently')
+def disable_pending_renames():
+ """Disable pending renames."""
+ reg_set_value(
+ 'HKLM', r'SYSTEM\CurrentControlSet\Control\Session Manager',
+ 'PendingFileRenameOperations', [], 'MULTI_SZ',
+ )
+
+
def disable_windows_updates():
"""Disable and stop Windows Updates."""
disable_service('wuauserv')
@@ -653,14 +732,43 @@ def reboot(timeout=10):
raise SystemExit
+def reset_proxy():
+ """Reset WinHTTP proxy settings."""
+ cmd = ['netsh', 'winhttp', 'reset', 'proxy']
+ proc = run_program(cmd, check=False)
+
+ # Check result
+ if 'Direct access (no proxy server)' not in proc.stdout:
+ raise GenericError('Failed to reset proxy settings.')
+
+
+def reset_windows_policies():
+ """Reset Windows policies to defaults."""
+ cmd = ['gpupdate', '/force']
+ proc = run_program(cmd, check=False)
+
+ # Check result
+ if not all(_s in proc.stdout for _s in GPUPDATE_SUCCESS_STRINGS):
+ raise GenericError('Failed to reset one or more policies.')
+
+
def reset_windows_updates():
"""Reset Windows Updates."""
system_root = os.environ.get('SYSTEMROOT', 'C:/Windows')
- rename_item(
- f'{system_root}/SoftwareDistribution',
- f'{system_root}/SoftwareDistribution.old',
- )
- delete_folder(f'{system_root}/SoftwareDistribution.old', force=True)
+ try:
+ rename_item(
+ f'{system_root}/SoftwareDistribution',
+ f'{system_root}/SoftwareDistribution.old',
+ )
+ delete_folder(f'{system_root}/SoftwareDistribution.old', force=True)
+ except FileNotFoundError:
+ # Ignore
+ pass
+
+
+def restore_uac_defaults():
+ """Restore UAC default settings."""
+ reg_write_settings(REG_UAC_DEFAULT_SETTINGS)
def run_chkdsk_offline():
From 447e93ca3a214c1f7adcf414bf0fdc653e72d2aa Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Fri, 30 Apr 2021 00:19:33 -0600
Subject: [PATCH 28/43] Resume in ConEmu if started in ConEmu
---
scripts/wk/repairs/win.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 14e9b5c9..110b516d 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -14,7 +14,7 @@ from subprocess import CalledProcessError, DEVNULL
from wk.cfg.main import KIT_NAME_FULL
from wk.exe import get_procs, run_program, popen_program, wait_for_procs
from wk.io import delete_folder, rename_item
-from wk.kit.tools import run_tool
+from wk.kit.tools import get_tool_path, run_tool
from wk.log import format_log_path, update_log_path
from wk.os.win import (
reg_delete_value,
@@ -233,6 +233,9 @@ def init_session(options):
'/rl', 'HIGHEST',
'/tr', fr'C:\Windows\System32\cmd.exe "/C {sys.executable} {sys.argv[0]}"',
]
+ if CONEMU:
+ exe_path = get_tool_path('ConEmu', 'ConEmu')
+ cmd[-1] = f'{exe_path} -run {sys.executable} {sys.argv[0]}'
run_program(cmd)
# One-time tasks
From e488b2c89c5af4d3aef4f005f07a26448c5e43bd Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Fri, 30 Apr 2021 02:08:50 -0600
Subject: [PATCH 29/43] Avoid crash when downloading tools
---
scripts/wk/kit/tools.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/scripts/wk/kit/tools.py b/scripts/wk/kit/tools.py
index 4f23b9a9..93311098 100644
--- a/scripts/wk/kit/tools.py
+++ b/scripts/wk/kit/tools.py
@@ -27,6 +27,8 @@ CACHED_DIRS = {}
def download_file(out_path, source_url, as_new=False, overwrite=False):
"""Download a file using requests, returns pathlib.Path."""
out_path = pathlib.Path(out_path).resolve()
+ if as_new:
+ out_path = out_path.with_suffix(f'{out_path.suffix}.new')
cursor_left = '\u001B[14D'
print(f'Downloading...{cursor_left}', end='', flush=True)
@@ -36,8 +38,6 @@ def download_file(out_path, source_url, as_new=False, overwrite=False):
# Create destination directory
out_path.parent.mkdir(parents=True, exist_ok=True)
- if as_new:
- out_path = out_path.with_suffix(f'{out_path.suffix}.new')
# Request download
response = requests.get(source_url, stream=True)
From 06b0ff81e1cfb2eae05329bedea5015488d38c7f Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Fri, 30 Apr 2021 02:09:43 -0600
Subject: [PATCH 30/43] Add Auto Repairs init functions
---
scripts/auto_repairs.py | 3 +-
scripts/wk/kit/tools.py | 4 +-
scripts/wk/repairs/win.py | 118 +++++++++++++++++++++++++++++---------
3 files changed, 96 insertions(+), 29 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index 65cce403..d9979573 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -88,7 +88,8 @@ BASE_MENUS = {
},
'Options': (
MenuEntry('Kill Explorer'),
- MenuEntry('Run RKill at startup'),
+ MenuEntry('Run RKill'),
+ MenuEntry('Run TDSSKiller (once)'),
MenuEntry('Use Autologon'),
),
'Actions': (
diff --git a/scripts/wk/kit/tools.py b/scripts/wk/kit/tools.py
index 93311098..cb145de1 100644
--- a/scripts/wk/kit/tools.py
+++ b/scripts/wk/kit/tools.py
@@ -100,7 +100,7 @@ def find_kit_dir(name=None):
return cur_path
-def get_tool_path(folder, name):
+def get_tool_path(folder, name, check=True):
"""Get tool path, returns pathlib.Path"""
bin_dir = find_kit_dir('.bin')
@@ -110,7 +110,7 @@ def get_tool_path(folder, name):
tool_path = tool_path.with_stem(name)
# Missing?
- if not tool_path.exists():
+ if check and not tool_path.exists():
raise FileNotFoundError(f'Failed to find tool, {folder=}, {name=}')
# Done
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 110b516d..b6f17df5 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -48,11 +48,17 @@ from wk.std import (
LOG = logging.getLogger(__name__)
AUTO_REPAIR_DELAY_IN_SECONDS = 30
AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\Auto Repairs'
-CONEMU = 'ConEmuPID' in os.environ
+CONEMU_EXE = get_tool_path('ConEmu', 'ConEmu', check=False)
GPUPDATE_SUCCESS_STRINGS = (
'Computer Policy update has completed successfully.',
'User Policy update has completed successfully.',
)
+IN_CONEMU = 'ConEmuPID' in os.environ
+PROGRAMFILES_32 = os.environ.get(
+ 'PROGRAMFILES(X86)', os.environ.get(
+ 'PROGRAMFILES', r'C:\Program Files (x86)',
+ ),
+ )
OS_VERSION = float(platform.win32_ver()[0])
REG_UAC_DEFAULT_SETTINGS = {
'HKLM': {
@@ -63,6 +69,16 @@ REG_UAC_DEFAULT_SETTINGS = {
),
},
}
+RKILL_WHITELIST = (
+ CONEMU_EXE,
+ fr'{PROGRAMFILES_32}\TeamViewer\TeamViewer.exe',
+ fr'{PROGRAMFILES_32}\TeamViewer\TeamViewer_Desktop.exe',
+ fr'{PROGRAMFILES_32}\TeamViewer\TeamViewer_Note.exe',
+ fr'{PROGRAMFILES_32}\TeamViewer\TeamViewer_Service.exe',
+ fr'{PROGRAMFILES_32}\TeamViewer\tv_w32.exe',
+ fr'{PROGRAMFILES_32}\TeamViewer\tv_x64.exe',
+ sys.executable,
+ )
SYSTEMDRIVE = os.environ.get('SYSTEMDRIVE')
WIDTH = 50
TRY_PRINT = TryAndPrint()
@@ -126,7 +142,6 @@ def build_menus(base_menus, title):
def end_session():
"""End Auto Repairs session."""
- print_info('Ending repair session')
auto_admin_logon = '0'
# Delete Auto Repairs keys
@@ -215,14 +230,17 @@ def init_run(options):
"""Initialize Auto Repairs Run."""
if options['Kill Explorer']['Selected']:
atexit.register(start_explorer)
- kill_explorer()
- # TODO: Sync Clock
- # TODO: RKill
+ TRY_PRINT.run('Killing Explorer...', kill_explorer, msg_good='DONE')
+ TRY_PRINT.run(
+ 'Syncing Clock...', run_tool, 'Neutron', 'Neutron',
+ cbin=True, msg_good='DONE',
+ )
+ if options['Run RKill']['Selected']:
+ TRY_PRINT.run('Running RKill...', run_rkill, msg_good='DONE')
def init_session(options):
"""Initialize Auto Repairs session."""
- print_info('Starting repair session')
reg_set_value('HKCU', AUTO_REPAIR_KEY, 'SessionStarted', 1, 'DWORD')
# Create logon task for Auto Repairs
@@ -233,16 +251,21 @@ def init_session(options):
'/rl', 'HIGHEST',
'/tr', fr'C:\Windows\System32\cmd.exe "/C {sys.executable} {sys.argv[0]}"',
]
- if CONEMU:
- exe_path = get_tool_path('ConEmu', 'ConEmu')
- cmd[-1] = f'{exe_path} -run {sys.executable} {sys.argv[0]}'
+ if IN_CONEMU:
+ cmd[-1] = f'{CONEMU_EXE} -run {sys.executable} {sys.argv[0]}'
run_program(cmd)
# One-time tasks
if options['Use Autologon']['Selected']:
- run_tool('Sysinternals', 'Autologon')
- # TODO: TDSSKiller?
- # TODO: Re-enable reboot()
+ TRY_PRINT.run(
+ 'Running Autologon...', run_tool,
+ 'Autologon', 'Autologon',
+ cbin=True, msg_good='DONE',
+ )
+ if options['Run TDSSKiller (once)']['Selected']:
+ TRY_PRINT.run('Running TDSSKiller...', run_tdsskiller, msg_good='DONE')
+ print('')
+ reboot(30)
def is_session_started():
@@ -300,16 +323,17 @@ def run_auto_repairs(base_menus):
session_started = is_session_started()
# Start or resume repairs
- save_selection_settings(menus)
- init_run(menus['Options'].options)
- if not session_started:
- init_session(menus['Options'].options)
-
- # Run repairs
clear_screen()
print_standard(title)
print('')
+ save_selection_settings(menus)
+ print_info('Initializing...')
+ init_run(menus['Options'].options)
+ if not session_started:
+ init_session(menus['Options'].options)
print_info('Running repairs')
+
+ # Run repairs
for group, menu in menus.items():
if group in ('Main', 'Options'):
continue
@@ -640,10 +664,15 @@ def auto_windows_updates_reset(group, name):
# Misc Functions
def set_backup_path(name, date=False):
"""Set backup path, returns pathlib.Path."""
- backup_path = format_log_path(log_name=f'../Backups/{name}').with_suffix('')
+ return get_local_storage_path('Backups', name, date)
+
+
+def get_local_storage_path(folder, name, date=False):
+ """Get path for local storage, returns pathlib.Path."""
+ local_path = format_log_path(log_name=f'../{folder}/{name}').with_suffix('')
if date:
- backup_path = backup_path.joinpath(time.strftime('%Y-%m-%d'))
- return backup_path.resolve()
+ local_path = local_path.joinpath(time.strftime('%Y-%m-%d'))
+ return local_path.resolve()
# Tool Functions
@@ -659,6 +688,40 @@ def delete_registry_null_keys():
run_tool('RegDelNull', 'RegDelNull', '-s', '-y', cbin=True)
+def run_rkill():
+ """Run RKill scan."""
+ log_path = format_log_path(log_name='RKill', timestamp=True, tool=True)
+ log_path.parent.mkdir(parents=True, exist_ok=True)
+ whitelist_path = log_path.with_suffix('.wl')
+ whitelist_path.write_text('\n'.join(map(str, RKILL_WHITELIST)))
+ cmd_args = (
+ '-l', log_path,
+ '-w', whitelist_path,
+ '-s',
+ )
+ run_tool('RKill', 'RKill', *cmd_args, download=True)
+
+
+def run_tdsskiller():
+ """Run TDSSKiller scan."""
+ log_path = format_log_path(log_name='TDSSKiller', timestamp=True, tool=True)
+ log_path.parent.mkdir(parents=True, exist_ok=True)
+ quarantine_path = get_local_storage_path(
+ 'Quarantine', 'TDSSKiller', date=True,
+ )
+ quarantine_path.mkdir(parents=True, exist_ok=True)
+ cmd_args = (
+ '-accepteula',
+ '-accepteulaksn',
+ '-l', log_path,
+ '-qpath', quarantine_path,
+ '-qsus',
+ '-dcexact',
+ '-silent',
+ )
+ run_tool('TDSSKiller', 'TDSSKiller', *cmd_args, download=True)
+
+
# OS Built-in Functions
def create_system_restore_point():
"""Create System Restore point."""
@@ -792,7 +855,7 @@ def run_chkdsk_online():
cmd = ['CHKDSK', os.environ.get('SYSTEMDRIVE', 'C:')]
if OS_VERSION >= 8:
cmd.extend(['/scan', '/perf'])
- if CONEMU:
+ if IN_CONEMU:
cmd.extend(['-new_console:nb', '-new_console:s33V'])
retried = False
@@ -826,7 +889,7 @@ def run_chkdsk_online():
def run_dism(repair=True):
"""Run DISM to either scan or repair component store health."""
- conemu_args = ['-new_console:nb', '-new_console:s33V'] if CONEMU else []
+ conemu_args = ['-new_console:nb', '-new_console:s33V'] if IN_CONEMU else []
# Bail early
if OS_VERSION < 8:
@@ -834,7 +897,8 @@ def run_dism(repair=True):
# Run (repair) scan
log_path = format_log_path(
- log_name=f'DISM_{"Restore" if repair else "Scan"}Health', tool=True,
+ log_name=f'DISM_{"Restore" if repair else "Scan"}Health',
+ timestamp=True, tool=True,
)
log_path.parent.mkdir(parents=True, exist_ok=True)
cmd = [
@@ -847,7 +911,9 @@ def run_dism(repair=True):
wait_for_procs('dism.exe')
# Run check health
- log_path = format_log_path(log_name='DISM_CheckHealth.log', tool=True)
+ log_path = format_log_path(
+ log_name='DISM_CheckHealth.log', timestamp=True, tool=True,
+ )
cmd = [
'DISM', '/Online', '/Cleanup-Image',
'/CheckHealth',
@@ -863,7 +929,7 @@ def run_dism(repair=True):
def run_sfc_scan():
"""Run SFC and save results."""
cmd = ['sfc', '/scannow']
- log_path = format_log_path(log_name='SFC', tool=True)
+ log_path = format_log_path(log_name='SFC', timestamp=True, tool=True)
err_path = log_path.with_suffix('.err')
# Run SFC
From 77920db5b5f51e323c809050fbbb78ff30b40419 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Fri, 30 Apr 2021 02:58:02 -0600
Subject: [PATCH 31/43] Add missing default UAC setting
---
scripts/wk/repairs/win.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index b6f17df5..48e66f06 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -66,6 +66,7 @@ REG_UAC_DEFAULT_SETTINGS = {
('ConsentPromptBehaviorAdmin', 5, 'DWORD'),
('ConsentPromptBehaviorUser', 3, 'DWORD'),
('EnableLUA', 1, 'DWORD'),
+ ('PromptOnSecureDesktop', 1, 'DWORD'),
),
},
}
From bdbed4622e7430c2e777b0097c506b5174f7ca78 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Fri, 30 Apr 2021 02:59:19 -0600
Subject: [PATCH 32/43] Add BleachBit sections
---
scripts/auto_repairs.py | 2 +-
scripts/wk/repairs/win.py | 64 +++++++++++++++++++++++++++++++++++++--
2 files changed, 62 insertions(+), 4 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index d9979573..d1b13be6 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -74,7 +74,7 @@ BASE_MENUS = {
MenuEntry('Reset Windows Policies', 'auto_reset_windows_policies'),
),
'Malware Cleanup': (
- MenuEntry('BleachBit', placeholder_function),
+ MenuEntry('BleachBit', 'auto_bleachbit'),
MenuEntry('HitmanPro', placeholder_function),
MenuEntry('KVRT', placeholder_function),
MenuEntry('Windows Defender', placeholder_function),
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 48e66f06..b9dac293 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -48,6 +48,42 @@ from wk.std import (
LOG = logging.getLogger(__name__)
AUTO_REPAIR_DELAY_IN_SECONDS = 30
AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\Auto Repairs'
+BLEACH_BIT_CLEANERS = (
+ # Applications
+ 'adobe_reader.cache',
+ 'adobe_reader.tmp',
+ 'flash.cache',
+ 'gimp.tmp',
+ 'hippo_opensim_viewer.cache',
+ 'java.cache',
+ 'miro.cache',
+ 'openofficeorg.cache',
+ 'pidgin.cache',
+ 'secondlife_viewer.Cache',
+ 'thunderbird.cache',
+ 'vuze.cache',
+ 'yahoo_messenger.cache',
+ # Browsers
+ 'chromium.cache',
+ 'chromium.session',
+ 'firefox.cache',
+ 'firefox.session_restore',
+ 'google_chrome.cache',
+ 'google_chrome.session',
+ 'google_earth.temporary_files',
+ 'opera.cache',
+ 'opera.session',
+ 'safari.cache',
+ 'seamonkey.cache',
+ # System
+ 'system.clipboard',
+ 'system.tmp',
+ 'winapp2_windows.jump_lists',
+ 'winapp2_windows.ms_search',
+ 'windows_explorer.run',
+ 'windows_explorer.search_history',
+ 'windows_explorer.thumbnails',
+ )
CONEMU_EXE = get_tool_path('ConEmu', 'ConEmu', check=False)
GPUPDATE_SUCCESS_STRINGS = (
'Computer Policy update has completed successfully.',
@@ -505,6 +541,14 @@ def auto_backup_registry(group, name):
save_settings(group, name, result=result)
+def auto_bleachbit(group, name):
+ """Run BleachBit to clean files."""
+ result = TRY_PRINT.run(
+ 'BleachBit...', run_bleachbit, BLEACH_BIT_CLEANERS, msg_good='DONE',
+ )
+ save_settings(group, name, result=result)
+
+
def auto_chkdsk(group, name):
"""Run CHKDSK repairs."""
needs_reboot = False
@@ -665,10 +709,10 @@ def auto_windows_updates_reset(group, name):
# Misc Functions
def set_backup_path(name, date=False):
"""Set backup path, returns pathlib.Path."""
- return get_local_storage_path('Backups', name, date)
+ return set_local_storage_path('Backups', name, date)
-def get_local_storage_path(folder, name, date=False):
+def set_local_storage_path(folder, name, date=False):
"""Get path for local storage, returns pathlib.Path."""
local_path = format_log_path(log_name=f'../{folder}/{name}').with_suffix('')
if date:
@@ -689,6 +733,20 @@ def delete_registry_null_keys():
run_tool('RegDelNull', 'RegDelNull', '-s', '-y', cbin=True)
+def run_bleachbit(cleaners, preview=True):
+ """Run BleachBit to either clean or preview files."""
+ cmd_args = (
+ '--preview' if preview else '--clean',
+ *cleaners,
+ )
+ log_path = format_log_path(log_name='BleachBit', timestamp=True, tool=True)
+ proc = run_tool('BleachBit', 'bleachbit_console', *cmd_args, cbin=True)
+
+ # Save logs
+ log_path.write_text(proc.stdout)
+ log_path.with_suffix('.err').write_text(proc.stderr)
+
+
def run_rkill():
"""Run RKill scan."""
log_path = format_log_path(log_name='RKill', timestamp=True, tool=True)
@@ -707,7 +765,7 @@ def run_tdsskiller():
"""Run TDSSKiller scan."""
log_path = format_log_path(log_name='TDSSKiller', timestamp=True, tool=True)
log_path.parent.mkdir(parents=True, exist_ok=True)
- quarantine_path = get_local_storage_path(
+ quarantine_path = set_local_storage_path(
'Quarantine', 'TDSSKiller', date=True,
)
quarantine_path.mkdir(parents=True, exist_ok=True)
From f706a48f60ec62e98a97968015f8e6be64a3d1ec Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Fri, 30 Apr 2021 03:26:45 -0600
Subject: [PATCH 33/43] Add HMP sections
---
scripts/auto_repairs.py | 2 +-
scripts/wk/cfg/tools.py | 2 +-
scripts/wk/repairs/win.py | 22 +++++++++++++++++++++-
3 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index d1b13be6..ad229c3a 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -75,7 +75,7 @@ BASE_MENUS = {
),
'Malware Cleanup': (
MenuEntry('BleachBit', 'auto_bleachbit'),
- MenuEntry('HitmanPro', placeholder_function),
+ MenuEntry('HitmanPro', 'auto_hitmanpro'),
MenuEntry('KVRT', placeholder_function),
MenuEntry('Windows Defender', placeholder_function),
MenuEntry('Reboot', 'auto_reboot'),
diff --git a/scripts/wk/cfg/tools.py b/scripts/wk/cfg/tools.py
index d7eaa837..27e56796 100644
--- a/scripts/wk/cfg/tools.py
+++ b/scripts/wk/cfg/tools.py
@@ -30,7 +30,7 @@ SOURCES = {
'FastCopy': 'https://ftp.vector.co.jp/73/10/2323/FastCopy392_installer.exe',
'FurMark': 'https://geeks3d.com/dl/get/569',
'Firefox uBO': 'https://addons.mozilla.org/firefox/downloads/file/3740966/ublock_origin-1.34.0-an+fx.xpi',
- 'HitmanPro32': 'https://dl.surfright.nl/HitmanPro.exe',
+ 'HitmanPro': 'https://dl.surfright.nl/HitmanPro.exe',
'HitmanPro64': 'https://dl.surfright.nl/HitmanPro_x64.exe',
'HWiNFO': 'https://files1.majorgeeks.com/c8a055180587599139f8f454712dcc618cd1740e/systeminfo/hwi_702.zip',
'Intel SSD Toolbox': r'https://downloadmirror.intel.com/28593/eng/Intel%20SSD%20Toolbox%20-%20v3.5.9.exe',
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index b9dac293..64422845 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -1,4 +1,5 @@
"""WizardKit: Repairs - Windows"""
+# pylint: disable=too-many-lines
# vim: sts=2 sw=2 ts=2
import atexit
@@ -14,7 +15,7 @@ from subprocess import CalledProcessError, DEVNULL
from wk.cfg.main import KIT_NAME_FULL
from wk.exe import get_procs, run_program, popen_program, wait_for_procs
from wk.io import delete_folder, rename_item
-from wk.kit.tools import get_tool_path, run_tool
+from wk.kit.tools import ARCH, get_tool_path, run_tool
from wk.log import format_log_path, update_log_path
from wk.os.win import (
reg_delete_value,
@@ -617,6 +618,12 @@ def auto_enable_regback(group, name):
save_settings(group, name, result=result)
+def auto_hitmanpro(group, name):
+ """Run HitmanPro scan."""
+ result = TRY_PRINT.run('HitmanPro...', run_hitmanpro, msg_good='DONE')
+ save_settings(group, name, result=result)
+
+
def auto_reboot(group, name):
"""Reboot the system."""
save_settings(group, name, done=True, failed=False, message='DONE')
@@ -740,6 +747,7 @@ def run_bleachbit(cleaners, preview=True):
*cleaners,
)
log_path = format_log_path(log_name='BleachBit', timestamp=True, tool=True)
+ log_path.parent.mkdir(parents=True, exist_ok=True)
proc = run_tool('BleachBit', 'bleachbit_console', *cmd_args, cbin=True)
# Save logs
@@ -747,6 +755,18 @@ def run_bleachbit(cleaners, preview=True):
log_path.with_suffix('.err').write_text(proc.stderr)
+def run_hitmanpro():
+ """Run HitmanPro scan."""
+ log_path = format_log_path(log_name='HitmanPro', timestamp=True, tool=True)
+ log_path = log_path.with_suffix('.xml')
+ log_path.parent.mkdir(parents=True, exist_ok=True)
+ cmd_args = ['/scanonly', f'/log={log_path}']
+ run_tool(
+ 'HitmanPro', f'HitmanPro{"64" if ARCH=="64" else ""}',
+ *cmd_args, download=True,
+ )
+
+
def run_rkill():
"""Run RKill scan."""
log_path = format_log_path(log_name='RKill', timestamp=True, tool=True)
From cf8b600dd5d0c1c51610850cabe99fe1b10af5cb Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 1 May 2021 17:00:48 -0600
Subject: [PATCH 34/43] Drop extraneous SYSTEMDRIVE lookups
---
scripts/wk/repairs/win.py | 32 ++++++++++++++++++--------------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 64422845..1d217430 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -10,14 +10,19 @@ import re
import sys
import time
-from subprocess import CalledProcessError, DEVNULL
+from subprocess import CalledProcessError, DEVNULL
-from wk.cfg.main import KIT_NAME_FULL
-from wk.exe import get_procs, run_program, popen_program, wait_for_procs
-from wk.io import delete_folder, rename_item
-from wk.kit.tools import ARCH, get_tool_path, run_tool
-from wk.log import format_log_path, update_log_path
-from wk.os.win import (
+from wk.cfg.main import KIT_NAME_FULL
+from wk.exe import (
+ get_procs,
+ run_program,
+ popen_program,
+ wait_for_procs,
+ )
+from wk.io import delete_folder, rename_item
+from wk.kit.tools import ARCH, get_tool_path, run_tool
+from wk.log import format_log_path, update_log_path
+from wk.os.win import (
reg_delete_value,
reg_read_value,
reg_set_value,
@@ -26,7 +31,7 @@ from wk.os.win import (
enable_service,
stop_service,
)
-from wk.std import (
+from wk.std import (
GenericError,
GenericWarning,
Menu,
@@ -117,7 +122,7 @@ RKILL_WHITELIST = (
fr'{PROGRAMFILES_32}\TeamViewer\tv_x64.exe',
sys.executable,
)
-SYSTEMDRIVE = os.environ.get('SYSTEMDRIVE')
+SYSTEMDRIVE = os.environ.get('SYSTEMDRIVE', 'C:')
WIDTH = 50
TRY_PRINT = TryAndPrint()
TRY_PRINT.width = WIDTH
@@ -553,14 +558,13 @@ def auto_bleachbit(group, name):
def auto_chkdsk(group, name):
"""Run CHKDSK repairs."""
needs_reboot = False
- system_disk = os.environ.get('SYSTEMDRIVE', 'C:')
- result = TRY_PRINT.run(f'CHKDSK ({system_disk})...', run_chkdsk_online)
+ result = TRY_PRINT.run(f'CHKDSK ({SYSTEMDRIVE})...', run_chkdsk_online)
# Run offline CHKDSK if required
if result['Failed'] and 'Repaired' not in result['Message']:
needs_reboot = True
result = TRY_PRINT.run(
- f'Scheduling offline CHKDSK ({system_disk})...',
+ f'Scheduling offline CHKDSK ({SYSTEMDRIVE})...',
run_chkdsk_offline,
)
if not result['Failed']:
@@ -918,7 +922,7 @@ def restore_uac_defaults():
def run_chkdsk_offline():
"""Set filesystem 'dirty bit' to force a CHKDSK during startup."""
- cmd = ['fsutil', 'dirty', 'set', os.environ.get('SYSTEMDRIVE', 'C:')]
+ cmd = ['fsutil', 'dirty', 'set', SYSTEMDRIVE]
proc = run_program(cmd, check=False)
# Check result
@@ -931,7 +935,7 @@ def run_chkdsk_online():
NOTE: If run on Windows 8+ online repairs are attempted.
"""
- cmd = ['CHKDSK', os.environ.get('SYSTEMDRIVE', 'C:')]
+ cmd = ['CHKDSK', SYSTEMDRIVE]
if OS_VERSION >= 8:
cmd.extend(['/scan', '/perf'])
if IN_CONEMU:
From 03000662fe6b683b0d1271107bbc47782464278c Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 1 May 2021 17:12:11 -0600
Subject: [PATCH 35/43] Add KVRT sections
---
scripts/auto_repairs.py | 2 +-
scripts/wk/repairs/win.py | 26 ++++++++++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index ad229c3a..d199d918 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -76,7 +76,7 @@ BASE_MENUS = {
'Malware Cleanup': (
MenuEntry('BleachBit', 'auto_bleachbit'),
MenuEntry('HitmanPro', 'auto_hitmanpro'),
- MenuEntry('KVRT', placeholder_function),
+ MenuEntry('KVRT', 'auto_kvrt'),
MenuEntry('Windows Defender', placeholder_function),
MenuEntry('Reboot', 'auto_reboot'),
),
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 1d217430..d77d19d1 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -628,6 +628,12 @@ def auto_hitmanpro(group, name):
save_settings(group, name, result=result)
+def auto_kvrt(group, name):
+ """Run KVRT scan."""
+ result = TRY_PRINT.run('KVRT...', run_kvrt, msg_good='DONE')
+ save_settings(group, name, result=result)
+
+
def auto_reboot(group, name):
"""Reboot the system."""
save_settings(group, name, done=True, failed=False, message='DONE')
@@ -771,6 +777,26 @@ def run_hitmanpro():
)
+def run_kvrt():
+ """Run KVRT scan."""
+ log_path = format_log_path(log_name='KVRT', timestamp=True, tool=True)
+ log_path.parent.mkdir(parents=True, exist_ok=True)
+ quarantine_path = set_local_storage_path(
+ 'Quarantine', 'KVRT', date=True,
+ )
+ quarantine_path.mkdir(parents=True, exist_ok=True)
+ cmd_args = (
+ '-accepteula',
+ '-d', quarantine_path,
+ '-dontencrypt', '-fixednames',
+ '-processlevel', '1',
+ '-custom', SYSTEMDRIVE,
+ '-silent', '-adinsilent',
+ )
+ proc = run_tool('KVRT', 'KVRT', *cmd_args, download=True)
+ log_path.write_text(proc.stdout)
+
+
def run_rkill():
"""Run RKill scan."""
log_path = format_log_path(log_name='RKill', timestamp=True, tool=True)
From 04b2c1c9d9839e9a32742a89ac048d06849877fc Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 1 May 2021 19:14:32 -0600
Subject: [PATCH 36/43] Add Microsoft Defender sections
---
scripts/auto_repairs.py | 2 +-
scripts/wk/repairs/win.py | 49 +++++++++++++++++++++++++++++++++++++++
2 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index d199d918..381ea729 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -77,7 +77,7 @@ BASE_MENUS = {
MenuEntry('BleachBit', 'auto_bleachbit'),
MenuEntry('HitmanPro', 'auto_hitmanpro'),
MenuEntry('KVRT', 'auto_kvrt'),
- MenuEntry('Windows Defender', placeholder_function),
+ MenuEntry('Windows Defender', 'auto_microsoft_defender'),
MenuEntry('Reboot', 'auto_reboot'),
),
'Manual Steps': (
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index d77d19d1..b054eb85 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -634,6 +634,14 @@ def auto_kvrt(group, name):
save_settings(group, name, result=result)
+def auto_microsoft_defender(group, name):
+ """Run Microsoft Defender scan."""
+ result = TRY_PRINT.run(
+ 'Microsoft Defender...', run_microsoft_defender, msg_good='DONE',
+ )
+ save_settings(group, name, result=result)
+
+
def auto_reboot(group, name):
"""Reboot the system."""
save_settings(group, name, done=True, failed=False, message='DONE')
@@ -797,6 +805,47 @@ def run_kvrt():
log_path.write_text(proc.stdout)
+def run_microsoft_defender(full=True):
+ """Run Microsoft Defender scan."""
+ reg_key = r'Software\Microsoft\Windows Defender'
+
+ def _get_defender_path():
+ install_path = reg_read_value('HKLM', reg_key, 'InstallLocation')
+ return fr'{install_path}\MpCmdRun.exe'
+
+ log_path = format_log_path(
+ log_name='Microsoft Defender', timestamp=True, tool=True,
+ )
+ log_path.parent.mkdir(parents=True, exist_ok=True)
+
+ # Get MS Defender status
+ ## NOTE: disabled may be set to an int instead of bool
+ ## This is fine because we're just checking if it's enabled.
+ disabled = bool(reg_read_value('HKLM', reg_key, 'DisableAntiSpyware'))
+ disabled = disabled or reg_read_value('HKLM', reg_key, 'DisableAntiVirus')
+ passive_mode = reg_read_value('HKLM', reg_key, 'PassiveMode') == 2
+ if disabled and not passive_mode:
+ raise GenericError('Defender is disabled.')
+
+ # Update signatures
+ defender_path = _get_defender_path()
+ cmd = (defender_path, '-SignatureUpdate')
+ proc = run_program(cmd, check=False)
+ sleep(2)
+ if proc.returncode > 0:
+ LOG.warning('Failed to update Defender signatures')
+
+ # Update defender path in case it changed after the update
+ defender_path = _get_defender_path()
+
+ # Run scan
+ cmd = (defender_path, '-Scan', '-ScanType', '2' if full else '1')
+ proc = run_program(cmd, check=False)
+ log_path.write_text(proc.stdout)
+ if proc.returncode > 0:
+ raise GenericError('Failed to run scan or clean items.')
+
+
def run_rkill():
"""Run RKill scan."""
log_path = format_log_path(log_name='RKill', timestamp=True, tool=True)
From 080e440d235681c67d4010ccc6374818c6a46e21 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 1 May 2021 19:28:07 -0600
Subject: [PATCH 37/43] Add AdwCleaner sections
---
scripts/auto_repairs.py | 2 +-
scripts/wk/repairs/win.py | 17 +++++++++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index 381ea729..d5ce4297 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -81,7 +81,7 @@ BASE_MENUS = {
MenuEntry('Reboot', 'auto_reboot'),
),
'Manual Steps': (
- MenuEntry('AdwCleaner', placeholder_function),
+ MenuEntry('AdwCleaner', 'auto_adwcleaner'),
MenuEntry('IO Bit Uninstaller', placeholder_function),
MenuEntry('Enable Windows Updates', 'auto_windows_updates_enable'),
),
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index b054eb85..c5a1930b 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -535,6 +535,18 @@ def update_main_menu(menus):
# Auto Repairs: Wrapper Functions
+def auto_adwcleaner(group, name):
+ """Run AdwCleaner scan.
+
+ save_settings() is called first since AdwCleaner may kill this script.
+ """
+ save_settings(group, name, done=True, failed=False, message='DONE')
+ result = TRY_PRINT.run('AdwCleaner...', run_adwcleaner, msg_good='DONE')
+
+ # Update with actual results (assuming this script wasn't killed)
+ save_settings(group, name, result=result)
+
+
def auto_backup_power_plans(group, name):
"""Backup power plans."""
result = TRY_PRINT.run('Backup Power Plans...', export_power_plans)
@@ -758,6 +770,11 @@ def delete_registry_null_keys():
run_tool('RegDelNull', 'RegDelNull', '-s', '-y', cbin=True)
+def run_adwcleaner():
+ """Run AdwCleaner."""
+ run_tool('AdwCleaner', 'AdwCleaner', download=True)
+
+
def run_bleachbit(cleaners, preview=True):
"""Run BleachBit to either clean or preview files."""
cmd_args = (
From ff43bc79b8798bd6875aba88bb6482128969828c Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 1 May 2021 20:00:46 -0600
Subject: [PATCH 38/43] Add uninstaller sections
---
scripts/auto_repairs.py | 2 +-
scripts/wk/repairs/win.py | 13 +++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index d5ce4297..06c4ba92 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -82,7 +82,7 @@ BASE_MENUS = {
),
'Manual Steps': (
MenuEntry('AdwCleaner', 'auto_adwcleaner'),
- MenuEntry('IO Bit Uninstaller', placeholder_function),
+ MenuEntry('IO Bit Uninstaller', 'auto_iobit_uninstaller'),
MenuEntry('Enable Windows Updates', 'auto_windows_updates_enable'),
),
},
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index c5a1930b..2945b554 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -640,6 +640,14 @@ def auto_hitmanpro(group, name):
save_settings(group, name, result=result)
+def auto_iobit_uninstaller(group, name):
+ """Run IO Bit Uninstaller scan."""
+ result = TRY_PRINT.run(
+ 'IO Bit Uninstaller...', run_iobit_uninstaller, msg_good='DONE',
+ )
+ save_settings(group, name, result=result)
+
+
def auto_kvrt(group, name):
"""Run KVRT scan."""
result = TRY_PRINT.run('KVRT...', run_kvrt, msg_good='DONE')
@@ -802,6 +810,11 @@ def run_hitmanpro():
)
+def run_iobit_uninstaller():
+ """Run IO Bit Uninstaller."""
+ run_tool('IObitUninstallerPortable', 'IObitUninstallerPortable', cbin=True)
+
+
def run_kvrt():
"""Run KVRT scan."""
log_path = format_log_path(log_name='KVRT', timestamp=True, tool=True)
From 9b6bfa27600561e442188213b14a5fc765cdfdb4 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 1 May 2021 20:01:01 -0600
Subject: [PATCH 39/43] Make Sync Clock optional
---
scripts/auto_repairs.py | 1 +
scripts/wk/repairs/win.py | 9 +++++----
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index 06c4ba92..29846f01 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -90,6 +90,7 @@ BASE_MENUS = {
MenuEntry('Kill Explorer'),
MenuEntry('Run RKill'),
MenuEntry('Run TDSSKiller (once)'),
+ MenuEntry('Sync Clock'),
MenuEntry('Use Autologon'),
),
'Actions': (
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 2945b554..79424f5b 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -274,10 +274,11 @@ def init_run(options):
if options['Kill Explorer']['Selected']:
atexit.register(start_explorer)
TRY_PRINT.run('Killing Explorer...', kill_explorer, msg_good='DONE')
- TRY_PRINT.run(
- 'Syncing Clock...', run_tool, 'Neutron', 'Neutron',
- cbin=True, msg_good='DONE',
- )
+ if options['Sync Clock']['Selected']:
+ TRY_PRINT.run(
+ 'Syncing Clock...', run_tool, 'Neutron', 'Neutron',
+ cbin=True, msg_good='DONE',
+ )
if options['Run RKill']['Selected']:
TRY_PRINT.run('Running RKill...', run_rkill, msg_good='DONE')
From 6b35d4165d6b8f495495dd842a3aaff70c0bc00d Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 1 May 2021 20:04:50 -0600
Subject: [PATCH 40/43] Add download_tool() function
---
scripts/wk/kit/tools.py | 54 ++++++++++++++++++++++-------------------
1 file changed, 29 insertions(+), 25 deletions(-)
diff --git a/scripts/wk/kit/tools.py b/scripts/wk/kit/tools.py
index cb145de1..ca44e61e 100644
--- a/scripts/wk/kit/tools.py
+++ b/scripts/wk/kit/tools.py
@@ -61,6 +61,34 @@ def download_file(out_path, source_url, as_new=False, overwrite=False):
return out_path
+def download_tool(folder, name):
+ """Download tool."""
+ out_path = find_kit_dir('.bin').joinpath(f'{folder}/{name}.exe')
+ up_to_date = False
+
+ # Check if tool is up to date
+ try:
+ ctime = datetime.fromtimestamp(out_path.stat().st_ctime)
+ up_to_date = datetime.now() - ctime < timedelta(days=DOWNLOAD_FREQUENCY)
+ except FileNotFoundError:
+ # Ignore - we'll download it below
+ pass
+ if out_path.exists() and up_to_date:
+ LOG.info('Skip downloading up-to-date tool: %s', name)
+ return
+
+ # Download
+ LOG.info('Downloading tool: %s', name)
+ source_url = SOURCES[name]
+ try:
+ new_file = download_file(out_path, source_url, as_new=True)
+ new_file.replace(out_path)
+ except GenericError:
+ # Ignore as long as there's still a version present
+ if not out_path.exists():
+ raise
+
+
def extract_archive(archive, out_path, *args, mode='x', silent=True):
"""Extract an archive to out_path."""
out_path = pathlib.Path(out_path).resolve()
@@ -127,18 +155,6 @@ def run_tool(
proc will be either subprocess.CompletedProcess or subprocess.Popen."""
proc = None
- def _is_outdated(file_path):
- """Check if the ctime is older than the threshold, returns bool."""
- outdated = False
- try:
- ctime = datetime.fromtimestamp(file_path.stat().st_ctime)
- outdated = datetime.now() - ctime > timedelta(days=DOWNLOAD_FREQUENCY)
- except FileNotFoundError:
- LOG.error("This shouldn't happen right?")
-
- # Done
- return outdated
-
# Extract from .cbin
if cbin:
extract_archive(
@@ -149,19 +165,7 @@ def run_tool(
# Download tool
if download:
- out_path = find_kit_dir('.bin').joinpath(f'{folder}/{name}.exe')
- if not out_path.exists() or _is_outdated(out_path):
- LOG.info('Downloading tool: %s', name)
- source_url = SOURCES[name]
- try:
- new_file = download_file(out_path, source_url, as_new=True)
- new_file.replace(out_path)
- except GenericError:
- # Ignore as long as there's still a version present
- if not out_path.exists():
- raise
- else:
- LOG.info('Skip downloading tool: %s', name)
+ download_tool(folder, name)
# Run
tool_path = get_tool_path(folder, name)
From 2d4ae6518830ee49776af134c22a2f071efcecab Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 1 May 2021 20:41:24 -0600
Subject: [PATCH 41/43] Run KVRT in new pane under ConEmu
---
scripts/wk/repairs/win.py | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 79424f5b..165b15dd 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -20,7 +20,7 @@ from wk.exe import (
wait_for_procs,
)
from wk.io import delete_folder, rename_item
-from wk.kit.tools import ARCH, get_tool_path, run_tool
+from wk.kit.tools import ARCH, download_tool, get_tool_path, run_tool
from wk.log import format_log_path, update_log_path
from wk.os.win import (
reg_delete_value,
@@ -826,12 +826,28 @@ def run_kvrt():
quarantine_path.mkdir(parents=True, exist_ok=True)
cmd_args = (
'-accepteula',
- '-d', quarantine_path,
+ '-d', str(quarantine_path),
'-dontencrypt', '-fixednames',
'-processlevel', '1',
'-custom', SYSTEMDRIVE,
'-silent', '-adinsilent',
)
+
+ # Run in new pane
+ if IN_CONEMU:
+ download_tool('KVRT', 'KVRT')
+ kvrt_path = get_tool_path('KVRT', 'KVRT')
+ tmp_file = fr'{os.environ.get("TMP")}\run_kvrt.cmd'
+ with open(tmp_file, 'w') as _f:
+ _f.write('@echo off\n')
+ _f.write(f'"{kvrt_path}" {" ".join(cmd_args)}\n')
+ cmd = ('cmd', '/c', tmp_file, '-new_console:nb', '-new_console:s33V')
+ run_program(cmd, check=False)
+ sleep(1)
+ wait_for_procs('KVRT.exe')
+ return
+
+ # Run in background
proc = run_tool('KVRT', 'KVRT', *cmd_args, download=True)
log_path.write_text(proc.stdout)
From b1acb6a07606919da770286c03e6cb3a9f049810 Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 1 May 2021 20:49:28 -0600
Subject: [PATCH 42/43] Remove placeholder functions
---
scripts/auto_repairs.py | 18 +-----------------
1 file changed, 1 insertion(+), 17 deletions(-)
diff --git a/scripts/auto_repairs.py b/scripts/auto_repairs.py
index 29846f01..7e0fd1f5 100644
--- a/scripts/auto_repairs.py
+++ b/scripts/auto_repairs.py
@@ -3,8 +3,6 @@
import os
import sys
-import random # TODO: Deleteme
-import time
os.chdir(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(os.getcwd())
@@ -29,25 +27,11 @@ class MenuEntry():
# Set details
self.details = {
'Function': function,
- 'Selected': False,
+ 'Selected': True,
**kwargs,
}
-# TODO: Deleteme
-TRY_AND_PRINT = wk.std.TryAndPrint()
-TRY_AND_PRINT.width = 50
-def placeholder_function(group, name):
- result = TRY_AND_PRINT.run(f'{name}...', time.sleep, random.randint(1, 3))
- wk.repairs.win.save_settings(group, name, result=result)
-
-def placeholder_reboot(group, name):
- print('"Rebooting" shortly...')
- time.sleep(random.randint(1, 3))
- wk.repairs.win.save_settings(group, name, done=True, message='DONE')
- raise SystemExit
-
-
# STATIC VARIABLES
BASE_MENUS = {
'Groups': {
From e9db4238ff900ebf5fb8120b8001a880f1d843ac Mon Sep 17 00:00:00 2001
From: 2Shirt <2xShirt@gmail.com>
Date: Sat, 1 May 2021 21:17:42 -0600
Subject: [PATCH 43/43] Run Autologon before any scans
---
scripts/wk/repairs/win.py | 46 ++++++++++++++++++++++++---------------
1 file changed, 29 insertions(+), 17 deletions(-)
diff --git a/scripts/wk/repairs/win.py b/scripts/wk/repairs/win.py
index 165b15dd..c21c4c3f 100644
--- a/scripts/wk/repairs/win.py
+++ b/scripts/wk/repairs/win.py
@@ -185,8 +185,6 @@ def build_menus(base_menus, title):
def end_session():
"""End Auto Repairs session."""
- auto_admin_logon = '0'
-
# Delete Auto Repairs keys
try:
reg_delete_value('HKCU', AUTO_REPAIR_KEY, 'SessionStarted')
@@ -209,15 +207,7 @@ def end_session():
LOG.error("Failed to remove scheduled task or it doesn't exist.")
# Disable Autologon
- try:
- auto_admin_logon = reg_read_value(
- 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
- 'AutoAdminLogon',
- )
- except FileNotFoundError:
- # Ignore and assume it's disabled
- return
- if auto_admin_logon != '0':
+ if is_autologon_enabled():
run_tool('Sysinternals', 'Autologon')
reg_set_value(
'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
@@ -274,6 +264,12 @@ def init_run(options):
if options['Kill Explorer']['Selected']:
atexit.register(start_explorer)
TRY_PRINT.run('Killing Explorer...', kill_explorer, msg_good='DONE')
+ if options['Use Autologon']['Selected'] and not is_autologon_enabled():
+ TRY_PRINT.run(
+ 'Running Autologon...', run_tool,
+ 'Autologon', 'Autologon',
+ cbin=True, msg_good='DONE',
+ )
if options['Sync Clock']['Selected']:
TRY_PRINT.run(
'Syncing Clock...', run_tool, 'Neutron', 'Neutron',
@@ -300,18 +296,30 @@ def init_session(options):
run_program(cmd)
# One-time tasks
- if options['Use Autologon']['Selected']:
- TRY_PRINT.run(
- 'Running Autologon...', run_tool,
- 'Autologon', 'Autologon',
- cbin=True, msg_good='DONE',
- )
if options['Run TDSSKiller (once)']['Selected']:
TRY_PRINT.run('Running TDSSKiller...', run_tdsskiller, msg_good='DONE')
print('')
reboot(30)
+def is_autologon_enabled():
+ """Check if Autologon is enabled, returns bool."""
+ auto_admin_logon = False
+ try:
+ auto_admin_logon = reg_read_value(
+ 'HKLM', r'Software\Microsoft\Windows NT\CurrentVersion\Winlogon',
+ 'AutoAdminLogon',
+ )
+ except FileNotFoundError:
+ # Ignore and assume it's disabled
+ pass
+ else:
+ auto_admin_logon = auto_admin_logon != '0'
+
+ # Done
+ return auto_admin_logon
+
+
def is_session_started():
"""Check if session was started, returns bool."""
session_started = False
@@ -373,6 +381,10 @@ def run_auto_repairs(base_menus):
save_selection_settings(menus)
print_info('Initializing...')
init_run(menus['Options'].options)
+ if not is_autologon_enabled():
+ # Either it wasn't selected or a password wasn't entered
+ menus['Options'].options['Use Autologon']['Selected'] = False
+ save_selection_settings(menus)
if not session_started:
init_session(menus['Options'].options)
print_info('Running repairs')