WizardKit/.bin/Scripts/functions.py
Alan Mason 41d6cfc209 2017-08: Retroactive Updates
* Bugfixes
  * Windows 10 v1703 / Redstone 2 / Creator's Update now recognized (attempt #2)
2017-11-17 00:57:07 -07:00

2811 lines
119 KiB
Python

# Wizard Kit: Init
import os
import partition_uids
import psutil
import re
import shutil
import subprocess
import sys
import time
import traceback
import winreg
from operator import itemgetter
from subprocess import CalledProcessError
# STATIC VARIABLES
ARCHIVE_PASSWORD='Abracadabra'
COLORS = {
'CLEAR': '\033[0m',
'RED': '\033[31m',
'GREEN': '\033[32m',
'YELLOW': '\033[33m',
'BLUE': '\033[34m'
}
BACKUP_SERVERS = [
{ 'IP': '10.0.0.10',
'Name': 'ServerOne',
'Mounted': False,
'Share': 'Backups',
'User': 'restore',
'Pass': 'Abracadabra',
},
{ 'IP': '10.0.0.11',
'Name': 'ServerTwo',
'Mounted': False,
'Share': 'Backups',
'User': 'restore',
'Pass': 'Abracadabra',
},
]
CLIENT_INFO_SERVER = {
'IP': '10.0.0.10',
'RegEntry': r'0x10001,0xcc674aebbd889f5fd553564adcf3cab550791eca12542033d52134db893c95aabb6b318a4621d8116f6838d873edfe9db4509e1dfc9177ee7484808a62cbc42b913387f694fd67e81950f85198acf721c5767b54db7b864d69cce65e12c78c87d0fb4fc54996609c9b9274b1de7bae2f95000c9ca8d7e3f9b3f2cdb21cd578adf9ba98d10400a8203bb1a879a4cd2fad99baeb12738b9b4b99fec821f881acb62598a43c059f74af287bc8dceeb4821317aa44e2e0ee66d346927a654c702854a71a2eaed6a53f6be9360c7049974a2597a548361da42ac982ae55f993700a8b1fc9f3b4458314fbd41f239de0a29716cdcefbbb2c8d02b4c2effa4163cfeac9',
'Share': '/srv/ClientInfo',
'User': 'wkdiag',
}
OFFICE_SERVER = {
'IP': '10.0.0.10',
'Name': 'ServerOne',
'Mounted': False,
'Share': 'Office',
'User': 'restore', # Using these credentials in case the backup shares are also mounted.
'Pass': 'Abracadabra', # This is because Windows only allows one set of credentials to be used per server at a time.
}
WINDOWS_SERVER = {
'IP': '10.0.0.10',
'Name': 'ServerOne',
'Mounted': False,
'Share': 'Windows',
'User': 'restore', # Using these credentials in case the backup shares are also mounted.
'Pass': 'Abracadabra', # This is because Windows only allows one set of credentials to be used per server at a time.
}
SHELL_FOLDERS = {
#GUIDs from: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx
'Desktop': ('{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}'),
'Documents': ('Personal', '{FDD39AD0-238F-46AF-ADB4-6C85480369C7}'),
'Downloads': ('{374DE290-123F-4565-9164-39C4925E467B}'),
'Favorites': ('{1777F761-68AD-4D8A-87BD-30B759FA33DD}'),
'Music': ('My Music', '{4BD8D571-6D19-48D3-BE97-422220080E43}'),
'Pictures': ('My Pictures', '{33E28130-4E1E-4676-835A-98395C3BC3BB}'),
'Videos': ('My Video', '{18989B1D-99B5-455B-841C-AB7C74E4DDFC}'),
}
EXTRA_FOLDERS = [
'Dropbox',
'Google Drive',
'OneDrive',
'SkyDrive',
]
TOOLS = {
# NOTE: The brace variables will be expanded at runtime
'AIDA64': {
'32': '{BinDir}\\AIDA64\\aida64.exe'},
'AutoRuns': {
'32': '{BinDir}\\SysinternalsSuite\\autoruns.exe',
'64': '{BinDir}\\SysinternalsSuite\\autoruns64.exe'},
'BleachBit': {
'32': '{BinDir}\\BleachBit\\bleachbit_console.exe'},
'Caffeine': {
'32': '{BinDir}\\caffeine\\caffeine.exe'},
'curl': {
'32': '{BinDir}\\curl\\curl.exe'},
'Du': {
'32': '{BinDir}\\SysinternalsSuite\\du.exe',
'64': '{BinDir}\\SysinternalsSuite\\du64.exe'},
'ERUNT': {
'32': '{BinDir}\\erunt\\ERUNT.EXE'},
'Everything': {
'32': '{BinDir}\\Everything\\Everything.exe',
'64': '{BinDir}\\Everything\\Everything64.exe'},
'FastCopy': {
'32': '{BinDir}\\FastCopy\\FastCopy.exe',
'64': '{BinDir}\\FastCopy\\FastCopy64.exe'},
'HitmanPro': {
'32': '{BinDir}\\HitmanPro\\HitmanPro.exe',
'64': '{BinDir}\\HitmanPro\\HitmanPro64.exe'},
'HWiNFO': {
'32': '{BinDir}\\HWiNFO\\HWiNFO.exe',
'64': '{BinDir}\\HWiNFO\\HWiNFO64.exe'},
'KVRT': {
'32': '{BinDir}\\KVRT\\KVRT.exe'},
'NotepadPlusPlus': {
'32': '{BinDir}\\notepadplusplus\\notepadplusplus.exe'},
'ProduKey': {
'32': '{BinDir}\\ProduKey\\ProduKey.exe',
'64': '{BinDir}\\ProduKey\\ProduKey64.exe'},
'PuTTY-PSFTP': {
'32': '{BinDir}\\PuTTY\\PSFTP.EXE'},
'RKill': {
'32': '{BinDir}\\RKill\\RKill.exe'},
'SevenZip': {
'32': '{BinDir}\\7-Zip\\7za.exe',
'64': '{BinDir}\\7-Zip\\7za64.exe'},
'TDSSKiller': {
'32': '{BinDir}\\TDSSKiller\\TDSSKiller.exe'},
'wimlib-imagex': {
'32': '{BinDir}\\wimlib\\x32\\wimlib-imagex.exe',
'64': '{BinDir}\\wimlib\\x64\\wimlib-imagex.exe'},
'XMPlay': {
'32': '{BinDir}\\XMPlay\\xmplay.exe'},
}
# Browsers
DEFAULT_HOMEPAGE = 'https://www.google.com/'
REGEX_CHROMIUM_ITEMS = re.compile(r'^(Bookmarks|Cookies|Favicons|Google Profile|History|Login Data|Top Sites|TransportSecurity|Visited Links|Web Data).*', re.IGNORECASE)
REGEX_FIREFOX = re.compile(r'^(bookmarkbackups|(cookies|formhistory|places).sqlite|key3.db|logins.json|persdict.dat)$', re.IGNORECASE)
REGEX_OFFICE = re.compile(r'(Microsoft (Office\s+(365|Enterprise|Home|Pro(\s|fessional)|Single|Small|Standard|Starter|Ultimate|system)|Works[-\s\d]+\d)|(Libre|Open|Star)\s*Office|WordPerfect|Gnumeric|Abiword)', re.IGNORECASE)
# Registry
REGEX_REGISTRY_DIRS = re.compile(r'^(config$|RegBack$|System32$|Transfer|Win)', re.IGNORECASE)
REGEX_SOFTWARE_HIVE = re.compile(r'^Software$', re.IGNORECASE)
# Data 1
REGEX_EXCL_ITEMS = re.compile(r'^(\.(AppleDB|AppleDesktop|AppleDouble|com\.apple\.timemachine\.supported|dbfseventsd|DocumentRevisions-V100.*|DS_Store|fseventsd|PKInstallSandboxManager|Spotlight.*|SymAV.*|symSchedScanLockxz|TemporaryItems|Trash.*|vol|VolumeIcon\.icns)|desktop\.(ini|.*DB|.*DF)|(hiberfil|pagefile)\.sys|lost\+found|Network\.*Trash\.*Folder|Recycle[dr]|System\.*Volume\.*Information|Temporary\.*Items|Thumbs\.db)$', flags=re.IGNORECASE)
REGEX_EXCL_ROOT_ITEMS = re.compile(r'^\\?(boot(mgr|nxt)$|Config.msi|(eula|globdata|install|vc_?red)|.*.sys$|System Volume Information|RECYCLER?|\$Recycle\.bin|\$?Win(dows(.old.*|\.~BT|)$|RE_)|\$GetCurrent|PerfLogs|Program Files|.*\.(esd|swm|wim|dd|map|dmg|image)$|SYSTEM.SAV|Windows10Upgrade)', flags=re.IGNORECASE)
REGEX_INCL_ROOT_ITEMS = re.compile(r'^\\?(AdwCleaner|(My\s*|)(Doc(uments?( and Settings|)|s?)|Downloads|WK(-?Info|-?Transfer|)|Media|Music|Pic(ture|)s?|Vid(eo|)s?)|(ProgramData|Recovery|Temp.*|Users)$|.*\.(log|txt|rtf|qb\w*|avi|m4a|m4v|mp4|mkv|jpg|png|tiff?)$)', flags=re.IGNORECASE)
REGEX_WIM_FILE = re.compile(r'\.wim$', flags=re.IGNORECASE)
REGEX_WINDOWS_OLD = re.compile(r'^\\Win(dows|)\.old', flags=re.IGNORECASE)
FAST_COPY_ARGS = [
'/cmd=noexist_only',
'/utf8',
'/skip_empty_dir',
'/linkdest',
'/no_ui',
'/auto_close',
r'/exclude=\*.esd;\*.swm;\*.wim;\*.dd;\*.dd.tgz;\*.dd.txz;\*.map;\*.dmg;\*.image;$RECYCLE.BIN;$Recycle.Bin;.AppleDB;.AppleDesktop;.AppleDouble;.com.apple.timemachine.supported;.dbfseventsd;.DocumentRevisions-V100*;.DS_Store;.fseventsd;.PKInstallSandboxManager;.Spotlight*;.SymAV*;.symSchedScanLockxz;.TemporaryItems;.Trash*;.vol;.VolumeIcon.icns;desktop.ini;Desktop?DB;Desktop?DF;hiberfil.sys;lost+found;Network?Trash?Folder;pagefile.sys;Recycled;RECYCLER;System?Volume?Information;Temporary?Items;Thumbs.db']
VCR_REDISTS = [
{'Name': 'Visual C++ 2005 SP1 x32...', 'Cmd': ['msiexec', '/i', '2005sp1\\x86\\vcredist.msi', '/qb!', '/norestart']},
{'Name': 'Visual C++ 2005 SP1 x64...', 'Cmd': ['msiexec', '/i', '2005sp1\\x64\\vcredist.msi', '/qb!', '/norestart']},
{'Name': 'Visual C++ 2008 SP1 x32...', 'Cmd': ['2008sp1\\vcredist_x86.exe', '/qb! /norestart']},
{'Name': 'Visual C++ 2008 SP1 x64...', 'Cmd': ['2008sp1\\vcredist_x64.exe', '/qb! /norestart']},
{'Name': 'Visual C++ 2010 x32...', 'Cmd': ['2010\\vcredist_x86.exe', '/passive', '/norestart']},
{'Name': 'Visual C++ 2010 x64...', 'Cmd': ['2010\\vcredist_x64.exe', '/passive', '/norestart']},
{'Name': 'Visual C++ 2012 Update 4 x32...', 'Cmd': ['2012u4\\vcredist_x86.exe', '/passive', '/norestart']},
{'Name': 'Visual C++ 2012 Update 4 x64...', 'Cmd': ['2012u4\\vcredist_x64.exe', '/passive', '/norestart']},
{'Name': 'Visual C++ 2013 x32...', 'Cmd': ['2013\\vcredist_x86.exe', '/install', '/passive', '/norestart']},
{'Name': 'Visual C++ 2013 x64...', 'Cmd': ['2013\\vcredist_x64.exe', '/install', '/passive', '/norestart']},
{'Name': 'Visual C++ 2015 Update 3 x32...', 'Cmd': ['2015u3\\vc_redist.x86.exe', '/install', '/passive', '/norestart']},
{'Name': 'Visual C++ 2015 Update 3 x64...', 'Cmd': ['2015u3\\vc_redist.x64.exe', '/install', '/passive', '/norestart']}]
global_vars = {}
# Error Classes
class BIOSKeyNotFoundError(Exception):
pass
class BinNotFoundError(Exception):
pass
class GenericError(Exception):
pass
class GenericRepair(Exception):
pass
class NotInstalledError(Exception):
pass
class NoProfilesError(Exception):
pass
class UnsupportedOSError(Exception):
pass
# General functions
def ask(prompt='Kotaero!'):
"""Prompt the user with a Y/N question, log answer, and return a bool."""
answer = None
prompt = prompt + ' [Y/N]: '
while answer is None:
tmp = input(prompt)
if re.search(r'^y(es|)$', tmp, re.IGNORECASE):
answer = True
elif re.search(r'^n(o|ope|)$', tmp, re.IGNORECASE):
answer = False
_message = '{prompt}{answer_text}'.format(
prompt = prompt,
answer_text = 'Yes' if answer else 'No')
print_log(message=_message)
return answer
def convert_to_bytes(size):
"""Convert human-readable size str to bytes and return an int."""
size = str(size)
tmp = re.search(r'(\d+)\s+([KMGT]B)', size.upper())
if tmp:
size = int(tmp.group(1))
units = tmp.group(2)
if units == 'TB':
size *= 1099511627776
elif units == 'GB':
size *= 1073741824
elif units == 'MB':
size *= 1048576
elif units == 'KB':
size *= 1024
else:
return -1
return size
def exit_script():
# Remove dirs (if empty)
for dir in ['BackupDir', 'LogDir', 'TmpDir']:
try:
dir = global_vars[dir]
os.rmdir(dir)
except:
pass
# Open Log (if it exists)
_log = global_vars.get('LogFile', '')
if os.path.exists(_log):
try:
extract_item('NotepadPlusPlus', silent=True)
popen_program([global_vars['Tools']['NotepadPlusPlus'], global_vars['LogFile']])
except:
print_error('ERROR: Failed to extract Notepad++ and open log.')
# Kill Caffeine if still running
kill_process('caffeine.exe')
# Exit
sys.exit(0)
def extract_item(item=None, filter='', silent=False):
"""Extract item from .cbin into .bin."""
if item is None:
raise Exception
_cmd = [
global_vars['Tools']['SevenZip'], 'x', '-aos', '-bso0', '-bse0',
'-p{ArchivePassword}'.format(**global_vars),
'-o"{BinDir}\\{item}"'.format(item=item, **global_vars),
'"{CBinDir}\\{item}.7z"'.format(item=item, **global_vars),
filter]
if not silent:
print_standard('Extracting "{item}"...'.format(item=item))
try:
run_program(_cmd, shell=True)
except subprocess.CalledProcessError:
if not silent:
print_warning('WARNING: Errors encountered while exctracting data')
def get_ticket_number():
"""Get TicketNumber from user and save it in the info folder."""
global_vars['TicketNumber'] = None
while global_vars['TicketNumber'] is None:
_ticket = input('Enter ticket number: ')
if re.match(r'^([0-9]+([-_]?\w+|))$', _ticket):
global_vars['TicketNumber'] = _ticket
with open('{LogDir}\\TicketNumber'.format(**global_vars), 'w') as f:
f.write(_ticket)
def human_readable_size(size, decimals=0):
"""Convert size in bytes to a human-readable format and return a str."""
# Prep string formatting
width = 3+decimals
if decimals > 0:
width += 1
human_format = '>{width}.{decimals}f'.format(width=width, decimals=decimals)
tmp = ''
# Convert size to int
try:
size = int(size)
except ValueError:
size = convert_to_bytes(size)
# Verify we have a valid size
if size <= 0:
return '{size:>{width}} b'.format(size='???', width=width)
# Format string
if size >= 1099511627776:
size /= 1099511627776
tmp = '{size:{human_format}} Tb'.format(size=size, human_format=human_format)
elif size >= 1073741824:
size /= 1073741824
tmp = '{size:{human_format}} Gb'.format(size=size, human_format=human_format)
elif size >= 1048576:
size /= 1048576
tmp = '{size:{human_format}} Mb'.format(size=size, human_format=human_format)
elif size >= 1024:
size /= 1024
tmp = '{size:{human_format}} Kb'.format(size=size, human_format=human_format)
else:
tmp = '{size:{human_format}} b'.format(size=size, human_format=human_format)
# Return
return tmp
def kill_process(name=None):
"""Kill any running caffeine.exe processes."""
if name is None:
raise Exception
for proc in psutil.process_iter():
if proc.name() == name:
proc.kill()
def major_exception():
"""Display traceback and exit"""
print_error('Major exception')
print_warning(' Please let The Wizard know and he\'ll look into it (Please include the details below).')
print(traceback.format_exc())
print_log(traceback.format_exc())
sleep(30)
pause("Press Enter to exit...")
sys.exit(1)
def menu_select(title='~ Untitled Menu ~', main_entries=[], action_entries=[],
prompt='Please make a selection', secret_exit=False, disabled_label='DISABLED'):
"""Display options in a menu for user and return selected option as a str."""
# Bail early
if (len(main_entries) + len(action_entries) == 0):
raise Exception("MenuError: No items given")
# Build menu
menu_splash = '{title}\n\n'.format(title=title)
width = len(str(len(main_entries)))
valid_answers = []
if (secret_exit):
valid_answers.append('Q')
# Add main entries
if (len(main_entries) > 0):
for i in range(len(main_entries)):
entry = main_entries[i]
# Add Spacer
if ('CRLF' in entry):
menu_splash += '\n'
if entry.get('Disabled', False):
menu_splash += '{YELLOW}{number:>{width}}: {name} ({disabled}){CLEAR}\n'.format(
number = i+1,
disabled = disabled_label,
width = width,
name = entry.get('Display Name', entry['Name']),
**COLORS)
else:
valid_answers.append(str(i+1))
menu_splash += '{number:>{width}}: {name}\n'.format(
number = i+1,
width = width,
name = entry.get('Display Name', entry['Name']))
menu_splash += '\n'
# Add action entries
if (len(action_entries) > 0):
for entry in action_entries:
# Add Spacer
if ('CRLF' in entry):
menu_splash += '\n'
valid_answers.append(entry['Letter'])
menu_splash += '{letter:>{width}}: {name}\n'.format(
letter = entry['Letter'].upper(),
width = len(str(len(action_entries))),
name = entry['Name'])
menu_splash += '\n'
answer = ''
while (answer.upper() not in valid_answers):
os.system('cls')
print(menu_splash)
answer = input('{prompt}: '.format(prompt=prompt))
return answer.upper()
def non_clobber_rename(full_path):
"""Append suffix to path, if necessary, to avoid clobbering path"""
new_path = full_path
_i = 1;
while os.path.exists(new_path):
new_path = '{path}_{i}'.format(i=_i, path=full_path)
_i += 1
return new_path
def pause(prompt='Press Enter to continue... '):
"""Simple pause implementation."""
input(prompt)
def popen_program(cmd=None, pipe=False, minimized=False, shell=False):
"""Run program and return a subprocess.Popen object."""
if cmd is None:
raise Exception('No program passed.')
startupinfo=None
if minimized:
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = 6
if pipe:
popen_obj = subprocess.Popen(cmd, shell=shell, startupinfo=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
else:
popen_obj = subprocess.Popen(cmd, shell=shell, startupinfo=None)
return popen_obj
def print_error(message='Generic error', end='\n', timestamp=True, **kwargs):
"""Prints message to screen in RED."""
print('{RED}{message}{CLEAR}'.format(message=message, **COLORS), end=end, **kwargs)
print_log(message, end, timestamp)
def print_info(message='Generic info', end='\n', timestamp=True, **kwargs):
"""Prints message to screen in BLUE."""
print('{BLUE}{message}{CLEAR}'.format(message=message, **COLORS), end=end, **kwargs)
print_log(message, end, timestamp)
def print_warning(message='Generic warning', end='\n', timestamp=True, **kwargs):
"""Prints message to screen in YELLOW."""
print('{YELLOW}{message}{CLEAR}'.format(message=message, **COLORS), end=end, **kwargs)
print_log(message, end, timestamp)
def print_standard(message='Generic info', end='\n', timestamp=True, **kwargs):
"""Prints message to screen."""
print('{message}'.format(message=message, **COLORS), end=end, **kwargs)
print_log(message, end, timestamp)
def print_success(message='Generic success', end='\n', timestamp=True, **kwargs):
"""Prints message to screen in GREEN."""
print('{GREEN}{message}{CLEAR}'.format(message=message, **COLORS), end=end, **kwargs)
print_log(message, end, timestamp)
def print_log(message='', end='\n', timestamp=True):
if 'LogFile' in global_vars and global_vars['LogFile'] is not None:
with open(global_vars['LogFile'], 'a') as f:
for line in message.splitlines():
f.write('{timestamp}{line}{end}'.format(
timestamp = time.strftime("%Y-%m-%d %H%M%z: ") if timestamp else '',
line = line,
end = end))
def run_program(cmd=None, args=[], check=True, pipe=True, shell=False):
"""Run program and return a subprocess.CompletedProcess object."""
if cmd is None:
raise Exception('No program passed.')
_cmd = cmd
if len(args) > 0:
# BAD Need to refactor all calls to run_program to only use cmd=X
## That way the cmd var is set the same as the real run() and Popen() calls
_cmd = [cmd] + args
if shell:
_cmd = ' '.join(_cmd)
if pipe:
process_return = subprocess.run(_cmd, check=check, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
else:
process_return = subprocess.run(_cmd, check=check, shell=shell)
return process_return
def show_info(message='~Some message~', info='~Some info~', indent=8, width=32):
print_standard('{indent}{message:<{width}}{info}'.format(indent=' '*indent, width=width, message=message, info=info))
def sleep(seconds=2):
time.sleep(seconds)
def stay_awake():
"""Prevent the system from sleeping or hibernating."""
# Bail if caffeine is already running
for proc in psutil.process_iter():
if proc.name() == 'caffeine.exe':
return
# Extract and run
extract_item('caffeine', silent=True)
try:
popen_program(global_vars['Tools']['Caffeine'])
except:
print_error('ERROR: No caffeine available; please set the power setting to High Performace.')
def try_and_print(function=None, message='Trying...', cs='CS', ns='NS', other_results={},
catch_all=True, indent=8, width=32, print_return=False, silent_function=True, *args, **kwargs):
"""Run function, print if successful or not, and return dict.
other_results is in the form of {'Warning': {'ExceptionClassName': 'Result Message'}, 'Error': {'ExceptionClassName': 'Result Message'}}
The the ExceptionClassNames will be additionally excepted conditions and the result string will be printed in the correct color.
catch_all=False will result in unspecified exceptions being re-raised."""
if function is None:
raise Exception
err = ''
wrn_exceptions = other_results.get('Warning', {}).keys()
wrn_exceptions = tuple(getattr(sys.modules[__name__], e) for e in wrn_exceptions)
err_exceptions = other_results.get('Error', {}).keys()
err_exceptions = tuple(getattr(sys.modules[__name__], e) for e in err_exceptions)
wrn_results = other_results.get('Warning', {})
err_results = other_results.get('Error', {})
# Run function and catch errors
print_standard('{indent}{message:<{width}}'.format(indent=' '*indent, message=message, width=width), end='', flush=True)
try:
out = function(*args, **kwargs)
if print_return:
print_standard(out[0], timestamp=False)
for item in out[1:]:
print_standard('{indent}{item}'.format(indent=' '*(indent+width), item=item))
elif silent_function:
print_success(cs, timestamp=False)
except wrn_exceptions as e:
_result = wrn_results.get(e.__class__.__name__, 'Warning')
print_warning(_result, timestamp=False)
err = e
except err_exceptions as e:
_result = err_results.get(e.__class__.__name__, 'Error')
print_error(_result, timestamp=False)
err = e
except:
print_error(ns, timestamp=False)
err = traceback.format_exc()
# Return or raise?
if bool(err) and not catch_all:
raise
else:
return {'CS': not bool(err), 'Error': err}
def upload_data(path=None, file=None):
"""Add CLIENT_INFO_SERVER to authorized connections and upload file."""
extract_item('PuTTY', filter='WK.ppk psftp.exe', silent=True)
# Authorize connection to the server
winreg.CreateKey(winreg.HKEY_CURRENT_USER, r'Software\SimonTatham\PuTTY\SshHostKeys')
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\SimonTatham\PuTTY\SshHostKeys', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'rsa2@22:{IP}'.format(**CLIENT_INFO_SERVER), 0, winreg.REG_SZ, CLIENT_INFO_SERVER['RegEntry'])
# Write batch file
with open('{TmpDir}\\psftp.batch'.format(**global_vars), 'w', encoding='ascii') as f:
f.write('lcd "{path}"\n'.format(path=path))
f.write('cd "{Share}"\n'.format(**CLIENT_INFO_SERVER))
f.write('mkdir {TicketNumber}\n'.format(**global_vars))
f.write('cd {TicketNumber}\n'.format(**global_vars))
f.write('put "{file}"\n'.format(file=file))
# Upload Info
_cmd = [
global_vars['Tools']['PuTTY-PSFTP'],
'-noagent',
'-i', '{BinDir}\\PuTTY\\WK.ppk'.format(**global_vars),
'{User}@{IP}'.format(**CLIENT_INFO_SERVER),
'-b', '{TmpDir}\\psftp.batch'.format(**global_vars)]
run_program(_cmd)
def wait_for_process(name=None):
"""Wait for process by name."""
if name is None:
raise Exception
_still_running = True
while _still_running:
sleep(1)
_still_running = False
for proc in psutil.process_iter():
if re.search(r'^{name}'.format(name=name), proc.name(), re.IGNORECASE):
_still_running = True
sleep(1)
# global_vars functions
def init_global_vars():
"""Sets global variables based on system info."""
print_info('Initializing')
try:
try_and_print(message='Checking .bin...', function=find_bin, catch_all=False, cs='Done', ns='Error')
try_and_print(message='Checking environment...', function=set_common_vars, catch_all=False, cs='Done', ns='Error')
try_and_print(message='Checking OS...', function=check_os, catch_all=False, cs='Done', ns='Error')
try_and_print(message='Checking tools...', function=check_tools, catch_all=False, cs='Done', ns='Error')
try_and_print(message='Creating folders...', function=make_tmp_dirs, catch_all=False, cs='Done', ns='Error')
try_and_print(message='Clearing collisions...', function=clean_env_vars, catch_all=False, cs='Done', ns='Error')
except:
major_exception()
def check_os():
_vars_os = {}
# Query registry
_reg_path = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion')
for key in ['CSDVersion', 'CurrentBuild', 'CurrentBuildNumber', 'CurrentVersion', 'ProductName']:
try:
_vars_os[key] = winreg.QueryValueEx(_reg_path, key)[0]
if key in ['CurrentBuild', 'CurrentBuildNumber']:
_vars_os[key] = int(_vars_os[key])
except ValueError:
# Couldn't convert Build to int so this should return interesting results...
_vars_os[key] = 0
except:
_vars_os[key] = 'Unknown'
# Determine OS bit depth
_vars_os['Arch'] = 32
if 'PROGRAMFILES(X86)' in global_vars['Env']:
_vars_os['Arch'] = 64
# Determine OS Name
_vars_os['Name'] = '{ProductName} {CSDVersion}'.format(**_vars_os)
if _vars_os['CurrentBuild'] == 9600:
_vars_os['Name'] += ' Update'
if _vars_os['CurrentBuild'] == 10240:
_vars_os['Name'] += ' Release 1507 "Threshold 1"'
if _vars_os['CurrentBuild'] == 10586:
_vars_os['Name'] += ' Release 1511 "Threshold 2"'
if _vars_os['CurrentBuild'] == 14393:
_vars_os['Name'] += ' Release 1607 "Redstone 1" / "Anniversary Update"'
if _vars_os['CurrentBuild'] == 15063:
_vars_os['Name'] += ' Release 1703 "Redstone 1" / "Creators Update"'
_vars_os['Name'] = _vars_os['Name'].replace('Service Pack ', 'SP')
_vars_os['Name'] = _vars_os['Name'].replace('Unknown Release', 'Release')
_vars_os['Name'] = re.sub(r'\s+', ' ', _vars_os['Name'])
# == vista ==
# 6.0.6000
# 6.0.6001
# 6.0.6002
# ==== 7 ====
# 6.1.7600
# 6.1.7601
# 6.1.7602
# ==== 8 ====
# 6.2.9200
# === 8.1 ===
# 6.3.9200
# === 8.1u ==
# 6.3.9600
# === 10 v1507 "Threshold 1" ==
# 6.3.10240
# === 10 v1511 "Threshold 2" ==
# 6.3.10586
# === 10 v1607 "Redstone 1" "Anniversary Update" ==
# 6.3.14393
# === 10 v1703 "Redstone 2" "Creators Update" ==
# 6.3.15063
# === 10 v???? "Redstone 3" "Fall Creators Update" ==
# 6.3.?????
# Determine OS version
_os_name = '{Name} x{Arch}'.format(**_vars_os)
if _vars_os['CurrentVersion'] == '6.0':
_vars_os['Version'] = 'Vista'
_os_name = _os_name + ' (very outdated)'
elif _vars_os['CurrentVersion'] == '6.1':
_vars_os['Version'] = '7'
if _vars_os['CSDVersion'] == 'Service Pack 1':
_os_name = _os_name + ' (outdated)'
else:
_os_name = _os_name + ' (very outdated)'
elif _vars_os['CurrentVersion'] == '6.2':
_vars_os['Version'] = '8'
_os_name = _os_name + ' (very outdated)'
elif _vars_os['CurrentVersion'] == '6.3':
if int(_vars_os['CurrentBuildNumber']) <= 9600:
_vars_os['Version'] = '8'
elif int(_vars_os['CurrentBuildNumber']) >= 10240:
_vars_os['Version'] = '10'
if _vars_os['CurrentBuild'] == 9200:
_os_name = _os_name + ' (very outdated)'
elif _vars_os['CurrentBuild'] == 9600:
_os_name = _os_name + ' (outdated)'
elif _vars_os['CurrentBuild'] == 10240:
_os_name = _os_name + ' (very outdated)'
elif _vars_os['CurrentBuild'] == 10586:
_os_name = _os_name + ' (outdated)'
elif _vars_os['CurrentBuild'] == 14393:
_os_name = _os_name + ' (outdated)'
elif _vars_os['CurrentBuild'] == 15063:
pass # Current Win10
else:
_os_name = _os_name + ' (unrecognized)'
_vars_os['DisplayName'] = _os_name
# Determine bootup type
_vars_os['SafeMode'] = False
if 'SAFEBOOT_OPTION' in global_vars['Env']:
_vars_os['SafeMode'] = True
# Ignore activation status if in Safe Mode
if _vars_os['SafeMode']:
_vars_os['Activation'] = 'Activation status unavailable in safe mode'
else:
_vars_os['Activation'] = 'Unknown'
global_vars['OS'] = _vars_os
# Update activation status
update_windows_activation_status()
def check_tools():
# Add tools to dict
if global_vars['OS'].get('Arch', 32) == 64:
global_vars['Tools'] = {k: v.get('64', v.get('32')) for (k, v) in TOOLS.items()}
else:
global_vars['Tools'] = {k: v.get('32') for (k, v) in TOOLS.items()}
# Fix paths
global_vars['Tools'] = {k: v.format(**global_vars) for (k, v) in global_vars['Tools'].items()}
def clean_env_vars():
# This fixes an issue where both global_vars and global_vars['Env'] are expanded at the same time
for key in global_vars.keys():
global_vars['Env'].pop(key, None)
def find_bin():
_wd = os.getcwd()
_base = None
while _base is None:
if os.path.exists('.bin'):
_base = os.getcwd()
break
if re.fullmatch(r'\w:\\', os.getcwd()):
break
os.chdir('..')
os.chdir(_wd)
if _base is None:
raise BinNotFoundError
global_vars['BaseDir'] = _base
def make_tmp_dirs():
os.makedirs(global_vars['BackupDir'], exist_ok=True)
os.makedirs(global_vars['LogDir'], exist_ok=True)
os.makedirs(global_vars['TmpDir'], exist_ok=True)
def set_common_vars():
global_vars['Date'] = time.strftime("%Y-%m-%d")
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
global_vars['Env'] = os.environ.copy()
global_vars['ArchivePassword'] = ARCHIVE_PASSWORD
global_vars['BinDir'] = '{BaseDir}\\.bin'.format(**global_vars)
global_vars['CBinDir'] = '{BaseDir}\\.cbin'.format(**global_vars)
global_vars['ClientDir'] = '{SYSTEMDRIVE}\\WK'.format(**global_vars['Env'])
global_vars['BackupDir'] = '{ClientDir}\\Backups\\{Date}'.format(**global_vars)
global_vars['LogDir'] = '{ClientDir}\\Info\\{Date}'.format(**global_vars)
global_vars['QuarantineDir'] = '{ClientDir}\\Quarantine'.format(**global_vars)
global_vars['TmpDir'] = '{BinDir}\\tmp'.format(**global_vars)
# Browsers
def backup_browsers():
functions = [
backup_chromium,
backup_chrome,
backup_chrome_canary,
backup_firefox,
backup_internet_explorer,
backup_opera,
backup_opera_beta,
backup_opera_dev]
errors = False
for function in functions:
try:
function()
except NoProfilesError:
pass
except:
errors = True
if errors:
raise GenericError
def backup_chromium():
"""Create backup of Chromium in the BackupDir."""
if os.path.exists('{LOCALAPPDATA}\\Chromium\\User Data'.format(**global_vars['Env'])):
cmd = [
global_vars['Tools']['SevenZip'], 'a', '-aoa', '-bso0', '-bse0', '-mx=1',
'{BackupDir}\\Browsers ({USERNAME})\\Chromium.7z'.format(**global_vars, **global_vars['Env']),
'{LOCALAPPDATA}\\Chromium\\User Data'.format(**global_vars['Env'])]
run_program(cmd, check=False)
else:
raise NoProfilesError
def backup_chrome():
"""Create backup of Google Chrome in the BackupDir."""
if os.path.exists('{LOCALAPPDATA}\\Google\\Chrome\\User Data'.format(**global_vars['Env'])):
cmd = [
global_vars['Tools']['SevenZip'], 'a', '-aoa', '-bso0', '-bse0', '-mx=1',
'{BackupDir}\\Browsers ({USERNAME})\\Google Chrome.7z'.format(**global_vars, **global_vars['Env']),
'{LOCALAPPDATA}\\Google\\Chrome\\User Data'.format(**global_vars['Env'])]
run_program(cmd, check=False)
else:
raise NoProfilesError
def backup_chrome_canary():
"""Create backup of Google Chrome Canary in the BackupDir."""
if os.path.exists('{LOCALAPPDATA}\\Google\\Chrome SxS\\User Data'.format(**global_vars['Env'])):
cmd = [
global_vars['Tools']['SevenZip'], 'a', '-aoa', '-bso0', '-bse0', '-mx=1',
'{BackupDir}\\Browsers ({USERNAME})\\Google Chrome Canary.7z'.format(**global_vars, **global_vars['Env']),
'{LOCALAPPDATA}\\Google\\Chrome SxS\\User Data'.format(**global_vars['Env'])]
run_program(cmd, check=False)
else:
raise NoProfilesError
def backup_internet_explorer():
"""Create backup of Internet Explorer in the BackupDir."""
run_program('reg export "hkcu\\Software\\Microsoft\\Internet Explorer" "{TmpDir}\\Internet Explorer (HKCU).reg" /y'.format(**global_vars), check=False)
run_program('reg export "hklm\\Software\\Microsoft\\Internet Explorer" "{TmpDir}\\Internet Explorer (HKLM).reg" /y'.format(**global_vars), check=False)
cmd = [
global_vars['Tools']['SevenZip'], 'a', '-aoa', '-bso0', '-bse0', '-mx=1',
'{BackupDir}\\Browsers ({USERNAME})\\Internet Explorer.7z'.format(**global_vars, **global_vars['Env']),
'{TmpDir}\\Internet*.reg'.format(**global_vars),
'{USERPROFILE}\\Favorites'.format(**global_vars['Env'])]
run_program(cmd, check=False)
def backup_firefox():
"""Create backup of Mozilla Firefox in the BackupDir."""
if os.path.exists('{APPDATA}\\Mozilla\\Firefox'.format(**global_vars['Env'])):
cmd = [
global_vars['Tools']['SevenZip'], 'a', '-aoa', '-bso0', '-bse0', '-mx=1',
'{BackupDir}\\Browsers ({USERNAME})\\Mozilla Firefox.7z'.format(**global_vars, **global_vars['Env']),
'{APPDATA}\\Mozilla\\Firefox\\Profile*'.format(**global_vars['Env'])]
run_program(cmd, check=False)
else:
raise NoProfilesError
def backup_opera():
"""Create backup of Opera Chromium in the BackupDir."""
if os.path.exists('{APPDATA}\\Opera Software\\Opera Stable'.format(**global_vars['Env'])):
cmd = [
global_vars['Tools']['SevenZip'], 'a', '-aoa', '-bso0', '-bse0', '-mx=1',
'{BackupDir}\\Browsers ({USERNAME})\\Opera.7z'.format(**global_vars, **global_vars['Env']),
'{APPDATA}\\Opera Software\\Opera Stable'.format(**global_vars['Env'])]
run_program(cmd, check=False)
else:
raise NoProfilesError
def backup_opera_beta():
"""Create backup of Opera Chromium Beta in the BackupDir."""
if os.path.exists('{APPDATA}\\Opera Software\\Opera Next'.format(**global_vars['Env'])):
cmd = [
global_vars['Tools']['SevenZip'], 'a', '-aoa', '-bso0', '-bse0', '-mx=1',
'{BackupDir}\\Browsers ({USERNAME})\\Opera Beta.7z'.format(**global_vars, **global_vars['Env']),
'{APPDATA}\\Opera Software\\Opera Next'.format(**global_vars['Env'])]
run_program(cmd, check=False)
else:
raise NoProfilesError
def backup_opera_dev():
"""Create backup of Opera Chromium Developer in the BackupDir."""
if os.path.exists('{APPDATA}\\Opera Software\\Opera Developer'.format(**global_vars['Env'])):
cmd = [
global_vars['Tools']['SevenZip'], 'a', '-aoa', '-bso0', '-bse0', '-mx=1',
'{BackupDir}\\Browsers ({USERNAME})\\Opera Dev.7z'.format(**global_vars, **global_vars['Env']),
'{APPDATA}\\Opera Software\\Opera Developer'.format(**global_vars['Env'])]
run_program(cmd, check=False)
else:
raise NoProfilesError
def clean_chromium_profile(profile):
"""Renames profile folder as backup and then recreates the folder with only the essential files."""
if profile is None:
raise Exception
# print_info(' Resetting profile: {name}'.format(name=profile.name))
backup_path = '{path}_{Date}.bak'.format(path=profile.path, **global_vars)
backup_path = non_clobber_rename(backup_path)
shutil.move(profile.path, backup_path)
os.makedirs(profile.path, exist_ok=True)
# Restore essential files from backup_path
for entry in os.scandir(backup_path):
if REGEX_CHROMIUM_ITEMS.search(entry.name):
shutil.copy(entry.path, '{path}\\{name}'.format(path=profile.path, name=entry.name))
def clean_internet_explorer():
"""Uses the built-in function to reset IE and sets the homepage."""
kill_process('iexplore.exe')
run_program('rundll32.exe', ['inetcpl.cpl,ResetIEtoDefaults'], check=False)
key = r'Software\Microsoft\Internet Explorer\Main'
# Set homepage
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key, access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'Start Page', 0, winreg.REG_SZ, DEFAULT_HOMEPAGE)
try:
winreg.DeleteValue(_key, 'Secondary Start Pages')
except FileNotFoundError:
pass
def clean_firefox_profile(profile):
"""Renames profile folder as backup and then recreates the folder with only the essential files."""
if profile is None:
raise Exception
# print_info(' Resetting profile: {name}'.format(name=profile.name))
backup_path = '{path}_{Date}.bak'.format(path=profile.path, **global_vars)
backup_path = non_clobber_rename(backup_path)
shutil.move(profile.path, backup_path)
homepages = []
os.makedirs(profile.path, exist_ok=True)
# Restore essential files from backup_path
for entry in os.scandir(backup_path):
if REGEX_FIREFOX.search(entry.name):
if entry.is_dir():
shutil.copytree(entry.path, '{path}\\{name}'.format(path=profile.path, name=entry.name))
else:
shutil.copy(entry.path, '{path}\\{name}'.format(path=profile.path, name=entry.name))
# Set profile defaults
with open('{path}\\prefs.js'.format(path=profile.path), 'a', encoding='ascii') as f:
f.write('user_pref("browser.search.geoSpecificDefaults", false);\n')
# Set search to Google
f.write('user_pref("browser.search.defaultenginename", "Google");\n')
f.write('user_pref("browser.search.defaultenginename.US", "Google");\n')
# Set homepage
f.write('user_pref("browser.startup.homepage", "{homepage}");\n'.format(homepage=DEFAULT_HOMEPAGE))
def config_internet_explorer():
ie_exe = get_iexplorer_exe()
run_program(ie_exe, ['https://www.microsoft.com/en-us/iegallery'], check=False)
def config_google_chrome():
chrome_exe = get_chrome_exe()
if chrome_exe is None:
raise NotInstalledError
# Check for system exensions
_ublock_origin = None
_ublock_extra = None
try:
_ublock_origin = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE, r'Software\Wow6432Node\Google\Chrome\Extensions\cjpalhdlnbpafiamejdnhcphjbkeiagm')
except FileNotFoundError:
pass
try:
_ublock_extra = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE, r'Software\Wow6432Node\Google\Chrome\Extensions\pgdnlhfefecpicbbihgmbmffkjpaplco')
except FileNotFoundError:
pass
# Open Chrome
_args = [
'https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?hl=en',
'https://chrome.google.com/webstore/detail/ublock-origin-extra/pgdnlhfefecpicbbihgmbmffkjpaplco?hl=en']
if _ublock_origin is None and _ublock_extra is None:
run_program(chrome_exe, _args, check=False)
elif _ublock_origin is None:
run_program(chrome_exe, [_args[0]], check=False)
elif _ublock_extra is None:
run_program(chrome_exe, [_args[1]], check=False)
else:
run_program(chrome_exe, ['chrome://extensions'], check=False)
def config_google_chrome_canary():
chrome_canary_exe = get_chrome_canary_exe()
if chrome_canary_exe is None:
raise NotInstalledError
_args = [
'https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?hl=en',
'https://chrome.google.com/webstore/detail/ublock-origin-extra/pgdnlhfefecpicbbihgmbmffkjpaplco?hl=en']
run_program(chrome_canary_exe, _args, check=False)
def config_mozilla_firefox():
firefox_exe = get_firefox_exe()
if firefox_exe is None:
raise NotInstalledError
# Check for system extension(s)
_ublock_origin = '{PROGRAMFILES}\\Mozilla Firefox\\distribution\\extensions\\uBlock0@raymondhill.net'.format(**global_vars['Env'])
if 'PROGRAMFILES(X86)' in global_vars['Env']:
_ublock_origin = '{PROGRAMFILES(X86)}\\Mozilla Firefox\\distribution\\extensions\\uBlock0@raymondhill.net'.format(**global_vars['Env'])
# Open Firefox
if os.path.exists(_ublock_origin):
run_program(firefox_exe, ['about:addons'], check=False)
else:
run_program(firefox_exe, ['https://addons.mozilla.org/en-us/firefox/addon/ublock-origin/'], check=False)
def config_mozilla_firefox_dev():
firefox_dev_exe = get_firefox_dev_exe()
if firefox_dev_exe is None:
raise NotInstalledError
run_program(firefox_dev_exe, ['https://addons.mozilla.org/en-us/firefox/addon/ublock-origin/'], check=False)
def config_opera():
opera_exe = get_opera_exe()
if opera_exe is None:
raise NotInstalledError
run_program(opera_exe, ['https://addons.opera.com/en/extensions/details/ublock/?display=en'], check=False)
def config_opera_beta():
opera_beta_exe = get_opera_beta_exe()
if opera_beta_exe is None:
raise NotInstalledError
run_program(opera_beta_exe, ['https://addons.opera.com/en/extensions/details/ublock/?display=en'], check=False)
def config_opera_dev():
opera_dev_exe = get_opera_dev_exe()
if opera_dev_exe is None:
raise NotInstalledError
run_program(opera_dev_exe, ['https://addons.opera.com/en/extensions/details/ublock/?display=en'], check=False)
def create_firefox_default_profiles():
"""Create new default profile for Mozilla Firefox for both stable and dev releases."""
firefox_exe = get_firefox_exe()
firefox_dev_exe = get_firefox_dev_exe()
profiles_ini_path = '{APPDATA}\\Mozilla\\Firefox\\profiles.ini'.format(**global_vars['Env'])
# Rename profiles.ini
if os.path.exists(profiles_ini_path):
backup_path = '{path}_{Date}.bak'.format(path=profiles_ini_path, **global_vars)
backup_path = non_clobber_rename(backup_path)
shutil.move(profiles_ini_path, backup_path)
# Create profile(s)
if firefox_exe is not None:
run_program(firefox_exe, ['-createprofile', 'default'], check=False)
if firefox_dev_exe is not None:
run_program(firefox_dev_exe, ['-createprofile'], check=False)
def get_chrome_exe():
"""Check for conflicting Chrome installations and return chrome.exe path as str."""
install_multi = '{PROGRAMFILES}\\Google\\Chrome\\Application\\chrome.exe'.format(**global_vars['Env'])
if 'PROGRAMFILES(X86)' in global_vars['Env']:
install_multi = '{PROGRAMFILES(X86)}\\Google\\Chrome\\Application\\chrome.exe'.format(**global_vars['Env'])
install_single = '{LOCALAPPDATA}\\Google\\Chrome\\Application\\chrome.exe'.format(**global_vars['Env'])
if os.path.exists(install_multi):
if os.path.exists(install_single):
print_log(' WARNING: Single-user and multi-user installations present.')
print_log(' It is recommended to move to only having the multi-user installation.')
return install_multi
elif os.path.exists(install_single):
return install_single
else:
return None
def get_chrome_profiles():
"""Find any existing Chrome profiles and return as a list of os.DirEntry objects."""
profiles = []
try:
for entry in os.scandir('{LOCALAPPDATA}\\Google\\Chrome\\User Data'.format(**global_vars['Env'])):
if entry.is_dir() and re.search(r'^(Default|Profile)', entry.name, re.IGNORECASE):
profiles.append(entry)
profiles = [p for p in profiles if not re.search(r'\.(wk|)bak.*', p.name, re.IGNORECASE)]
except:
pass
return profiles
def get_chrome_canary_exe():
"""Check for Google Chrome Canary installation and return chrome.exe path as str."""
prog_exe = '{LOCALAPPDATA}\\Google\\Chrome SxS\\Application\\chrome.exe'.format(**global_vars['Env'])
if os.path.exists(prog_exe):
return prog_exe
else:
return None
def get_chrome_canary_profiles():
"""Find any existing Chrome profiles and return as a list of os.DirEntry objects."""
profiles = []
try:
for entry in os.scandir('{LOCALAPPDATA}\\Google\\Chrome SxS\\User Data'.format(**global_vars['Env'])):
if entry.is_dir() and re.search(r'^(Default|Profile)', entry.name, re.IGNORECASE):
profiles.append(entry)
profiles = [p for p in profiles if not re.search(r'\.(wk|)bak.*', p.name, re.IGNORECASE)]
except:
pass
return profiles
def get_iexplorer_exe():
"""Find and return iexplorer.exe path as str."""
ie_exe = '{PROGRAMFILES}\\Internet Explorer\\iexplore.exe'.format(**global_vars['Env'])
if 'PROGRAMFILES(X86)' in global_vars['Env']:
ie_exe = '{PROGRAMFILES(X86)}\\Internet Explorer\\iexplore.exe'.format(**global_vars['Env'])
return ie_exe
def get_firefox_exe():
"""Check for Mozilla Firefox installation and return firefox.exe path as str."""
prog_exe = '{PROGRAMFILES}\\Mozilla Firefox\\firefox.exe'.format(**global_vars['Env'])
if 'PROGRAMFILES(X86)' in global_vars['Env']:
prog_exe = '{PROGRAMFILES(X86)}\\Mozilla Firefox\\firefox.exe'.format(**global_vars['Env'])
if os.path.exists(prog_exe):
return prog_exe
else:
return None
def get_firefox_dev_exe():
"""Check for Mozilla Firefox Developer Edition installation and return firefox.exe path as str."""
prog_exe = '{PROGRAMFILES}\\Firefox Developer Edition\\firefox.exe'.format(**global_vars['Env'])
if 'PROGRAMFILES(X86)' in global_vars['Env']:
prog_exe = '{PROGRAMFILES(X86)}\\Firefox Developer Edition\\firefox.exe'.format(**global_vars['Env'])
if os.path.exists(prog_exe):
return prog_exe
else:
return None
def get_firefox_profiles():
"""Find any existing Chrome profiles and return as a list of os.DirEntry objects."""
profiles = []
try:
for entry in os.scandir('{APPDATA}\\Mozilla\\Firefox\\Profiles'.format(**global_vars['Env'])):
if entry.is_dir():
profiles.append(entry)
profiles = [p for p in profiles if not re.search(r'\.(wk|)bak.*', p.name, re.IGNORECASE)]
except:
pass
return profiles
def get_opera_exe():
"""Check for Opera installation and return launcher.exe path as str."""
prog_exe = '{PROGRAMFILES}\\Opera\\launcher.exe'.format(**global_vars['Env'])
if 'PROGRAMFILES(X86)' in global_vars['Env']:
prog_exe = '{PROGRAMFILES(X86)}\\Opera\\launcher.exe'.format(**global_vars['Env'])
if os.path.exists(prog_exe):
return prog_exe
else:
return None
def get_opera_beta_exe():
"""Check for Opera Beta installation and return launcher.exe path as str."""
prog_exe = '{PROGRAMFILES}\\Opera beta\\launcher.exe'.format(**global_vars['Env'])
# Installs as 64-bit on a 64-bit OS so PROGRAMFILES should always be correct
if os.path.exists(prog_exe):
return prog_exe
else:
return None
def get_opera_dev_exe():
"""Check for Opera Beta installation and return launcher.exe path as str."""
prog_exe = '{PROGRAMFILES}\\Opera developer\\launcher.exe'.format(**global_vars['Env'])
# Installs as 64-bit on a 64-bit OS so PROGRAMFILES should always be correct
if os.path.exists(prog_exe):
return prog_exe
else:
return None
def get_opera_profile():
"""Find an existing Opera profile and return as a length-1 list of os.DirEntry objects."""
profiles = []
try:
for entry in os.scandir('{APPDATA}\\Opera Software'.format(**global_vars['Env'])):
if entry.is_dir() and entry.name == 'Opera Stable':
return [entry]
except:
pass
return profiles
def get_opera_beta_profile():
"""Find an existing Opera Beta profile and return as a length-1 list of os.DirEntry objects."""
profiles = []
try:
for entry in os.scandir('{APPDATA}\\Opera Software'.format(**global_vars['Env'])):
if entry.is_dir() and entry.name == 'Opera Next':
return [entry]
except:
pass
return profiles
def get_opera_dev_profile():
"""Find an existing Opera Dev profile and return as a length-1 list of os.DirEntry objects."""
profiles = []
try:
for entry in os.scandir('{APPDATA}\\Opera Software'.format(**global_vars['Env'])):
if entry.is_dir() and entry.name == 'Opera Developer':
return [entry]
except:
pass
return profiles
def list_homepages(indent=8, width=32):
"""List current homepages for reference."""
print_standard('{indent}{browser:<{width}}'.format(indent=' '*indent, width=width, browser='Google Chrome...'), end='', flush=True)
print_warning('Currently not possible', timestamp=False)
# Firefox
profiles = get_firefox_profiles()
if len(profiles) > 0:
print_standard('{indent}{browser:<{width}}'.format(indent=' '*indent, width=width, browser='Mozilla Firefox...'))
for profile in profiles:
homepages = []
try:
with open('{path}\\prefs.js'.format(path=profile.path), 'r') as f:
_search = re.search(r'browser\.startup\.homepage", "([^"]*)"', f.read(), re.IGNORECASE)
if _search:
homepages = _search.group(1).split('|')
except FileNotFoundError:
pass
for page in homepages:
print_standard('{indent}{name:<{width}}{page}'.format(indent=' '*(indent+4), width=width-4, name=profile.name, page=page))
# IE
homepage = ''
secondary_homepages = []
key = r'Software\Microsoft\Internet Explorer\Main'
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key) as _key:
homepage = winreg.QueryValueEx(_key, 'Start Page')[0]
try:
secondary_homepages = winreg.QueryValueEx(_key, 'Secondary Start Pages')[0]
except FileNotFoundError:
pass
print_standard('{indent}{browser:<{width}}{page}'.format(indent=' '*indent, width=width, browser='Internet Explorer...', page=homepage))
for page in secondary_homepages:
print_standard('{indent}{page}'.format(indent=' '*(indent+width), page=page))
def reset_google_chrome():
kill_process('chrome.exe')
chrome_exe = get_chrome_exe()
profiles = get_chrome_profiles()
if len(profiles) == 0:
raise NoProfilesError
for profile in profiles:
clean_chromium_profile(profile)
def reset_google_chrome_canary():
kill_process('chrome.exe')
chrome_canary_exe = get_chrome_canary_exe()
profiles = get_chrome_canary_profiles()
if len(profiles) == 0:
raise NoProfilesError
for profile in profiles:
clean_chromium_profile(profile)
def reset_mozilla_firefox():
kill_process('firefox.exe')
profiles = get_firefox_profiles()
if len(profiles) == 0:
create_firefox_default_profiles()
kill_process('firefox.exe')
raise NoProfilesError
else:
for profile in profiles:
clean_firefox_profile(profile)
def reset_opera():
kill_process('opera.exe')
opera_exe = get_opera_exe()
profiles = get_opera_profile()
if len(profiles) == 0:
raise NoProfilesError
# Reset browser (Opera only supports one profile)
clean_chromium_profile(profiles[0])
def reset_opera_beta():
kill_process('opera.exe')
opera_beta_exe = get_opera_beta_exe()
profiles = get_opera_beta_profile()
if len(profiles) == 0:
raise NoProfilesError
# Reset browser (Opera only supports one profile)
clean_chromium_profile(profiles[0])
def reset_opera_dev():
kill_process('opera.exe')
opera_dev_exe = get_opera_dev_exe()
profiles = get_opera_dev_profile()
if len(profiles) == 0:
raise NoProfilesError
# Reset browser (Opera only supports one profile)
clean_chromium_profile(profiles[0])
def set_chrome_as_default():
chrome_exe = get_chrome_exe()
if chrome_exe is None:
raise NotInstalledError
run_program(chrome_exe, ['--make-default-browser'], check=False)
if global_vars['OS']['Version'] in ['10']:
popen_program('ms-settings:defaultapps')
# Cleanup
def cleanup_adwcleaner():
_path = '{SYSTEMDRIVE}\\AdwCleaner'.format(**global_vars['Env'])
if os.path.exists(_path):
os.makedirs('{ClientDir}\\Info'.format(**global_vars), exist_ok=True)
for entry in os.scandir(_path):
if entry.is_file() and re.search(r'*.(log|txt)', entry.name):
_name = re.sub(r'^(.*)\.', '\1_{Date-Time}'.format(**global_vars), entry.name, re.IGNORECASE)
_name = '{ClientDir}\\Info\\{name}'.format(name=_name, **global_vars)
shutil.move(entry.path, non_clobber_rename(_name))
elif entry.name == 'Quarantine':
os.makedirs(global_vars['QuarantineDir'], exist_ok=True)
_name = '{QuarantineDir}\\AdwCleaner_{Date-Time}'.format(**global_vars)
shutil.move(entry.path, non_clobber_rename(_name))
shutil.rmtree(_path)
def cleanup_desktop():
os.makedirs('{ClientDir}\\Info'.format(**global_vars), exist_ok=True)
if os.path.exists('{USERPROFILE}\\Desktop'.format(**global_vars['Env'])):
for entry in os.scandir('{USERPROFILE}\\Desktop'.format(**global_vars['Env'])):
# JRT, RKill, Shortcut cleaner
if re.search(r'^((JRT|RKill).*|sc-cleaner)', entry.name, re.IGNORECASE):
_name = '{ClientDir}\\Info\\{name}'.format(name=entry.name, **global_vars)
shutil.move(entry.path, non_clobber_rename(_name))
run_program('rmdir "{path}"'.format(path='{ClientDir}\\Info'.format(**global_vars)), check=False, shell=True)
def uninstall_eset():
if 'PROGRAMFILES(X86)' in global_vars['Env']:
_path = '{PROGRAMFILES(X86)}\\ESET'.format(**global_vars['Env'])
else:
_path = '{PROGRAMFILES}\\ESET'.format(**global_vars['Env'])
if os.path.exists('{path}\\ESET Online Scanner'.format(path=_path)):
run_program('"{path}\\ESET Online Scanner\\OnlineScannerUninstaller.exe" -s'.format(path=_path))
shutil.rmtree('{path}\\ESET Online Scanner'.format(path=_path))
run_program('rmdir "{path}"'.format(path=_path), check=False)
def uninstall_mbam():
# if 'PROGRAMFILES(X86)' in global_vars['Env']:
# _path = '{PROGRAMFILES(X86)}\\Malwarebytes Anti-Malware'.format(**global_vars['Env'])
# else:
# _path = '{PROGRAMFILES}\\Malwarebytes Anti-Malware'.format(**global_vars['Env'])
# if os.path.exists('{path}'.format(path=_path)):
# print_warning('* Malwarebytes Anti-Malware installed.')
# if ask(' Uninstall?'):
# try:
# run_program('"{path}\\unins000.exe" /SILENT'.format(path=_path))
# run_program('rmdir "{path}"'.format(path=_path), check=False)
# except:
# print_error('ERROR: Failed to uninstall Malwarebytes Anti-Malware.')
pass
def uninstall_sas():
# It is always in programfiles (not x86) ??
_path = '{PROGRAMFILES}\\SUPERAntiSpyware'.format(**global_vars['Env'])
if os.path.exists(_path):
run_program('{path}\\Uninstall.exe'.format(path=_path))
run_program('rmdir "{path}"'.format(path=_path), check=False)
# Config
def config_classicstart():
# User level, not system level
_classicstart = '{PROGRAMFILES}\\Classic Shell\\ClassicStartMenu.exe'.format(**global_vars['Env'])
_skin = '{PROGRAMFILES}\\Classic Shell\\Skins\\Metro-Win10-Black.skin7'.format(**global_vars['Env'])
extract_item('ClassicStartSkin', silent=True)
# Stop Classic Start
run_program([_classicstart, '-exit'], check=False)
# Configure
winreg.CreateKey(winreg.HKEY_CURRENT_USER, r'Software\IvoSoft\ClassicShell\Settings')
winreg.CreateKey(winreg.HKEY_CURRENT_USER, r'Software\IvoSoft\ClassicStartMenu\MRU')
winreg.CreateKey(winreg.HKEY_CURRENT_USER, r'Software\IvoSoft\ClassicStartMenu\Settings')
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\IvoSoft\ClassicStartMenu', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'ShowedStyle2', 0, winreg.REG_DWORD, 1)
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\IvoSoft\ClassicStartMenu\Settings', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'MenuStyle', 0, winreg.REG_SZ, 'Win7')
winreg.SetValueEx(_key, 'RecentPrograms', 0, winreg.REG_SZ, 'Recent')
winreg.SetValueEx(_key, 'SkipMetro', 0, winreg.REG_DWORD, 1)
# Enable Win10 theme if on Win10
if global_vars['OS']['Version'] == '10' and os.path.exists(_skin):
winreg.SetValueEx(_key, 'SkinW7', 0, winreg.REG_SZ, 'Metro-Win10-Black')
winreg.SetValueEx(_key, 'SkinVariationW7', 0, winreg.REG_SZ, '')
# Pin Google Chrome to Start Menu (Classic)
_source = '{BinDir}\\ClassicStartSkin\\Google Chrome.lnk'.format(**global_vars)
_dest_path = '{APPDATA}\\ClassicShell\\Pinned'.format(**global_vars['Env'])
_dest = '{dest_path}\\Google Chrome.lnk'.format(dest_path=_dest_path)
try:
os.makedirs(_dest_path, exist_ok=True)
shutil.copy(_source, _dest)
except:
pass # Meh, it's fine without
# (Re)start Classic Start
run_program([_classicstart, '-exit'], check=False)
sleep(1)
kill_process('ClassicStartMenu.exe')
run_program(_classicstart, check=False)
def config_explorer_system():
# Disable Telemetry
winreg.CreateKeyEx(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection', 0, winreg.KEY_WRITE)
winreg.CreateKeyEx(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Policies\Microsoft\Windows\DataCollection', 0, winreg.KEY_WRITE)
winreg.CreateKeyEx(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection', 0, winreg.KEY_WRITE | winreg.KEY_WOW64_32KEY)
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'AllowTelemetry', 0, winreg.REG_DWORD, 0)
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Policies\Microsoft\Windows\DataCollection', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'AllowTelemetry', 0, winreg.REG_DWORD, 0)
with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection', 0, winreg.KEY_WRITE | winreg.KEY_WOW64_32KEY) as _key:
winreg.SetValueEx(_key, 'AllowTelemetry', 0, winreg.REG_DWORD, 0)
# Disable Wi-Fi Sense
winreg.CreateKeyEx(winreg.HKEY_LOCAL_MACHINE, r'Software\Microsoft\PolicyManager\default\WiFi\AllowWiFiHotSpotReporting', 0, winreg.KEY_WRITE)
winreg.CreateKeyEx(winreg.HKEY_LOCAL_MACHINE, r'Software\Microsoft\PolicyManager\default\WiFi\AllowAutoConnectToWiFiSenseHotspots', 0, winreg.KEY_WRITE)
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'Software\Microsoft\PolicyManager\default\WiFi\AllowWiFiHotSpotReporting', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'Value', 0, winreg.REG_DWORD, 0)
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'Software\Microsoft\PolicyManager\default\WiFi\AllowAutoConnectToWiFiSenseHotspots', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'Value', 0, winreg.REG_DWORD, 0)
# Disable Location Tracking
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Sensor\Overrides\{BFA794E4-F964-4FDB-90F6-51056BFE4B44}', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'SensorPermissionState', 0, winreg.REG_DWORD, 0)
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'System\CurrentControlSet\Services\lfsvc\Service\Configuration', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'Status', 0, winreg.REG_DWORD, 0)
def config_explorer_user():
# Disable Cortana
winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, r'Software\Microsoft\Personalization\Settings', 0, winreg.KEY_WRITE)
winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, r'Software\Microsoft\InputPersonalization', 0, winreg.KEY_WRITE)
winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, r'Software\Microsoft\InputPersonalization\TrainedDataStore', 0, winreg.KEY_WRITE)
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Microsoft\Personalization\Settings', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'AcceptedPrivacyPolicy', 0, winreg.REG_DWORD, 0)
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Microsoft\InputPersonalization', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'RestrictImplicitTextCollection', 0, winreg.REG_DWORD, 1)
winreg.SetValueEx(_key, 'RestrictImplicitInkCollection', 0, winreg.REG_DWORD, 1)
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Microsoft\InputPersonalization\TrainedDataStore', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'HarvestContacts', 0, winreg.REG_DWORD, 1)
# Hide Search button / box
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Microsoft\Windows\CurrentVersion\Search', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'SearchboxTaskbarMode', 0, winreg.REG_DWORD, 0)
# Change default Explorer view to "Computer"
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'LaunchTo', 0, winreg.REG_DWORD, 1)
def update_clock():
# Set Timezone and sync clock
run_program('tzutil /s "Pacific Standard Time"', check=False)
run_program('net stop w32ime', check=False)
run_program('w32tm /config /syncfromflags:manual /manualpeerlist:"us.pool.ntp.org time.nist.gov time.windows.com"', check=False)
run_program('net start w32ime', check=False)
run_program('w32tm /resync /nowait', check=False)
# Disk Management and Data Transfers
def cleanup_transfer():
"""Fix permissions and walk through transfer folder (from the bottom) and remove extraneous items."""
cmd = ['attrib', '-a', '-h', '-r', '-s', global_vars['Data']['Destination']]
run_program(cmd, check=False)
try:
os.rmdir(global_vars['Data']['Destination'])
except:
pass # Should only remove when empty
if os.path.exists(global_vars['Data']['Destination']):
for root, dirs, files in os.walk(global_vars['Data']['Destination'], topdown=False):
for name in dirs:
# Remove empty directories and junction points
try:
os.rmdir(os.path.join(root, name))
except OSError:
pass
for name in files:
# Remove files based on exclusion regex
if REGEX_EXCL_ITEMS.search(name):
try:
os.remove(os.path.join(root, name))
except OSError:
pass
def extract_keys():
"""Extract keys from provided hives and return a dict."""
keys = {}
# Extract keys
extract_item('ProduKey', silent=True)
for hive in find_software_hives():
_cmd = [
global_vars['Tools']['ProduKey'],
'/IEKeys', '0',
'/WindowsKeys', '1',
'/OfficeKeys', '1',
'/ExtractEdition', '1',
'/nosavereg',
'/regfile', hive,
'/scomma', '']
_out = run_program(_cmd)
for line in _out.stdout.decode().splitlines():
# Add key to keys under product only if unique
_tmp = line.split(',')
_product = _tmp[0]
_key = _tmp[2]
if _product not in keys:
keys[_product] = []
if _key not in keys[_product]:
keys[_product].append(_key)
global_vars['Keys'] = keys
def is_valid_wim_image(item):
_valid = bool(item.is_file() and REGEX_WIM_FILE.search(item.name))
if _valid:
extract_item('wimlib', silent=True)
try:
_cmd = [global_vars['Tools']['wimlib-imagex'], 'info', item.path]
run_program(_cmd)
except subprocess.CalledProcessError:
_valid = False
print_log('WARNING: Image "{image}" damaged.'.format(image=item.name))
return _valid
def mount_backup_shares():
"""Mount the backup shares unless labeled as already mounted."""
for server in BACKUP_SERVERS:
# Blindly skip if we mounted earlier
if server['Mounted']:
continue
else:
try:
# Test connection
ping_test(server['IP'])
# Mount
run_program('net use \\\\{IP}\\{Share} /user:{User} {Pass}'.format(**server))
print_info('Mounted {Name}'.format(**server))
server['Mounted'] = True
except subprocess.CalledProcessError:
print_error('Failed to mount \\\\{Name}\\{Share}, {IP} unreachable.'.format(**server))
sleep(1)
except:
print_warning('Failed to mount \\\\{Name}\\{Share} ({IP})'.format(**server))
sleep(1)
def run_fast_copy(items=None, dest=None):
if items is None or dest is None:
raise Exception
elif len(items) == 0:
raise Exception
cmd = [global_vars['Tools']['FastCopy'], *FAST_COPY_ARGS]
if 'LogFile' in global_vars:
cmd.append('/logfile={LogFile}'.format(**global_vars))
cmd.extend(items)
cmd.append('/to={dest}\\'.format(dest=dest))
run_program(cmd)
def run_wimextract(source=None, items=None, dest=None):
if source is None or items is None or dest is None:
raise Exception
elif len(items) == 0:
raise Exception
extract_item('wimlib', silent=True)
# Write files.txt
with open('{TmpDir}\\wim_files.txt'.format(**global_vars), 'w') as f:
# Defaults
for item in items:
f.write('{item}\n'.format(item=item))
sleep(1) # For safety?
# Extract files
cmd = [
global_vars['Tools']['wimlib-imagex'],
'extract',
source, '1',
'@{TmpDir}\\wim_files.txt'.format(**global_vars),
'--dest-dir={dest}\\'.format(dest=dest),
'--no-acls',
'--nullglob']
run_program(cmd)
def save_keys():
key_list = []
if global_vars['Keys']:
for product in sorted(global_vars['Keys']):
key_list.append(product)
for key in sorted(global_vars['Keys'][product]):
key_list.append(' {key}'.format(key=key))
else:
key_list.append('No keys found.')
return key_list
def scan_backup():
if 'Source' not in global_vars['Data']:
raise Exception
backup = global_vars['Data']['Source']
global_vars['Data']['Selected Items'] = []
if backup.is_dir():
# File-Based
print_standard('Scanning File-Based backup: {}'.format(backup.path))
scan_backup_folder(backup)
else:
# Image-Based
if REGEX_WIM_FILE.search(backup.name):
print_standard('Scanning Image-Based backup: {}'.format(backup.path))
selected_items = scan_backup_wim(backup)
else:
print_error('ERROR: Unsupported image: {}'.format(backup.path))
raise GenericError
def scan_backup_folder(backup_folder=None, rel_path=None, interactive=True):
if backup_folder is None:
raise Exception
win_olds = []
dest = '{dest}{rel_path}'.format(
dest = global_vars['Data']['Destination'],
rel_path = '' if rel_path is None else '\\'+rel_path)
# Root items
_items = []
for item in os.scandir(backup_folder.path):
if REGEX_INCL_ROOT_ITEMS.search(item.name):
_items.append(item.path)
elif not REGEX_EXCL_ROOT_ITEMS.search(item.name):
if (not interactive
or ask('Copy: "{rel_path}{name}" ?'.format(name=item.name, rel_path='' if rel_path is None else rel_path+'\\'))):
_items.append(item.path)
if REGEX_WINDOWS_OLD.search(item.name):
win_olds.append(item)
if len(_items) > 0:
global_vars['Data']['Selected Items'].append({
'Message': '{}Root Items...'.format('' if rel_path is None else rel_path+'\\'),
'Items': _items.copy(),
'Destination': dest})
# Fonts
if os.path.exists('{}\\Windows\\Fonts'.format(backup_folder.path)):
global_vars['Data']['Selected Items'].append({
'Message': '{}Fonts...'.format('' if rel_path is None else rel_path+'\\'),
'Items': ['{}\\Windows\\Fonts'.format(backup_folder.path)],
'Destination': '{}\\{}'.format(dest, 'Windows')})
_items.append('{source}\\Windows\\Fonts'.format(source=backup_folder.path))
# Registry
_items = []
if os.path.exists('{}\\Windows\\System32\\config'.format(backup_folder.path)):
_items.append('{}\\Windows\\System32\\config'.format(backup_folder.path))
if os.path.exists('{}\\Windows\\System32\\OEM'.format(backup_folder.path)):
_items.append('{}\\Windows\\System32\\OEM'.format(backup_folder.path))
if len(_items) > 0:
global_vars['Data']['Selected Items'].append({
'Message': '{}Registry...'.format('' if rel_path is None else rel_path+'\\'),
'Items': _items.copy(),
'Destination': '{}\\{}'.format(dest, 'Windows\\System32')})
# Windows.old(s)
for old in win_olds:
scan_backup_folder(old.path, rel_path=old.name, interactive=False)
def scan_backup_wim(backup_file=None, rel_path=None, interactive=True):
if backup_file is None:
raise Exception
if rel_path is None:
rel_path = ''
else:
rel_path = rel_path + '\\'
win_olds = []
# Scan source
extract_item('wimlib', silent=True)
_cmd = [
global_vars['Tools']['wimlib-imagex'], 'dir',
backup_file.path, '1']
try:
_list = run_program(_cmd)
except subprocess.CalledProcessError as err:
print_error('ERROR: Failed to get file list.')
raise
# Root Items
_items = []
root_items = [i.strip() for i in _list.stdout.decode('utf-8', 'ignore').splitlines() if i.count('\\') == 1 and i.strip() != '\\']
if rel_path:
root_items = [i.replace(rel_path, '') for i in root_items if rel_path in i]
for item in root_items:
if REGEX_INCL_ROOT_ITEMS.search(item):
_items.append(item)
elif not REGEX_EXCL_ROOT_ITEMS.search(item):
if (not interactive
or ask('Extract: "{rel_path}{name}" ?'.format(name=item, rel_path=rel_path))):
_items.append('{}{}'.format(rel_path, item))
if REGEX_WINDOWS_OLD.search(item):
win_olds.append(item)
if len(_items) > 0:
global_vars['Data']['Selected Items'].append({
'Message': '{}Root Items...'.format(rel_path),
'Items': _items.copy(),
'Destination': global_vars['Data']['Destination']})
# Fonts
if wim_contains(backup_file.path, '{}Windows\\Fonts'.format(rel_path)):
global_vars['Data']['Selected Items'].append({
'Message': '{}Fonts...'.format(rel_path),
'Items': ['{}\\Windows\\Fonts'.format(rel_path)],
'Destination': global_vars['Data']['Destination']})
# Registry
_items = []
if wim_contains(backup_file.path, '{}Windows\\System32\\config'.format(rel_path)):
_items.append('{}Windows\\System32\\config'.format(rel_path))
if wim_contains(backup_file.path, '{}Windows\\System32\\OEM'.format(rel_path)):
_items.append('{}Windows\\System32\\OEM'.format(rel_path))
if len(_items) > 0:
global_vars['Data']['Selected Items'].append({
'Message': '{}Registry...'.format(rel_path),
'Items': _items.copy(),
'Destination': global_vars['Data']['Destination']})
# Windows.old(s)
for old in win_olds:
scan_backup_wim(backup_file, rel_path=old, interactive=False)
def select_backup():
"""Select backup from those found on the BACKUP_SERVERS for the ticket."""
sources = []
mount_backup_shares()
# Check for ticket folders on servers
for server in BACKUP_SERVERS:
if server['Mounted']:
print_standard('Scanning {server}...'.format(server=server['Name']))
for d in os.scandir('\\\\{IP}\\{Share}'.format(**server)):
if d.is_dir() and re.match('^{}'.format(global_vars['TicketNumber']), d.name):
# Add folder to sources
sources.append({
'Name': '{server:9}| File-Based: [DIR] {ticket}'.format(server=server['Name'], ticket=d.name),
'Server': server,
'Source': d})
# Check for images and subfolders
for ticket_folder in sources.copy():
for item in os.scandir(ticket_folder['Source'].path):
if item.is_dir():
# Add folder to sources
sources.append({
'Name': '{server:9}| File-Based: [DIR] {ticket}\\{folder}'.format(
folder = item.name,
server = ticket_folder['Server']['Name'],
ticket = ticket_folder['Source'].name),
'Server': ticket_folder['Server'],
'Source': item})
# Check for images in folder
for subitem in os.scandir(item.path):
if REGEX_WIM_FILE.search(item.name):
# Add image to sources
try:
size = human_readable_size(item.stat().st_size)
except:
size = ' ? ?' # unknown
sources.append({
'Disabled': bool(not is_valid_wim_image(subitem)),
'Name': '{server:9}| Image-Based: {size:>7} {ticket}\\{folder}\\{image}'.format(
folder = item.name,
image = subitem.name,
server = ticket_folder['Server']['Name'],
size = size,
ticket = ticket_folder['Source'].name),
'Server': ticket_folder['Server'],
'Source': subitem})
elif REGEX_WIM_FILE.search(item.name):
# Add image to sources
try:
size = human_readable_size(item.stat().st_size)
except:
size = ' ? ?' # unknown
sources.append({
'Disabled': bool(not is_valid_wim_image(item)),
'Name': '{server:9}| Image-Based: {size:>7} {ticket}\\{image}'.format(
image = item.name,
server = ticket_folder['Server']['Name'],
size = size,
ticket = ticket_folder['Source'].name),
'Server': ticket_folder['Server'],
'Source': item})
# Build Menu
sources.sort(key=itemgetter('Name'))
actions = [{'Name': 'Quit', 'Letter': 'Q'}]
# Select backup from sources
if len(sources) > 0:
selection = menu_select('Which backup are we using?', sources, actions, disabled_label='DAMAGED')
if selection == 'Q':
umount_backup_shares()
exit_script()
else:
global_vars['Data']['Source'] = sources[int(selection)-1]['Source']
else:
print_error('ERROR: No backups found for ticket: {TicketNumber}.'.format(**global_vars))
umount_backup_shares()
pause("Press Enter to exit...")
exit_script()
def select_destination():
"""Select destination for data 1 transfer."""
disk = select_disk()
path = '{disk}{folder_path}_{Date}'.format(
disk = disk['Disk'].mountpoint,
folder_path = 'WK\\Transfer' if 'fixed' in disk['Disk'].opts else 'WK-Transfer',
**global_vars)
# Avoid merging with existing transfer
path = non_clobber_rename(path)
os.makedirs(path, exist_ok=True)
global_vars['Data']['Destination'] = path
def select_disk():
"""Select disk from attached disks. returns dict."""
actions = [{'Name': 'Quit', 'Letter': 'Q'}]
disks = []
for d in psutil.disk_partitions():
info = {
'Disk': d,
'Name': d.mountpoint}
try:
usage = psutil.disk_usage(d.device)
free = '{free} / {total} available'.format(
free = human_readable_size(usage.free, 2),
total = human_readable_size(usage.total, 2))
except:
# Meh, leaving unsupported destinations out
pass
# free = 'Unknown'
# info['Disabled'] = True
else:
info['Display Name'] = '{disk} ({free})'.format(disk=info['Name'], free=free)
disks.append(info)
selection = menu_select('Which disk are we transferring to?', disks, actions)
if selection == 'Q':
exit_script()
else:
return disks[int(selection)-1]
def transfer_backup():
if 'Source' not in global_vars['Data']:
raise Exception
backup = global_vars['Data']['Source']
if backup.is_dir():
# File-Based
transfer_backup_file_based()
else:
# Image-Based
if REGEX_WIM_FILE.search(backup.name):
transfer_backup_image_based()
else:
print_error('ERROR: Unsupported image: {}'.format(backup.path))
raise GenericError
def transfer_backup_file_based():
if 'Source' not in global_vars['Data'] or 'Selected Items' not in global_vars['Data']:
raise Exception
backup = global_vars['Data']['Source']
selected_items = global_vars['Data']['Selected Items']
# Run FastCopy for each selection "group"
for group in selected_items:
try_and_print(message=group['Message'], function=run_fast_copy, cs='Done', items=group['Items'], dest=group['Destination'])
def transfer_backup_image_based():
if 'Source' not in global_vars['Data'] or 'Selected Items' not in global_vars['Data']:
raise Exception
backup = global_vars['Data']['Source']
selected_items = global_vars['Data']['Selected Items']
# Run wimlib-imagex for each selection "group"
for group in selected_items:
try_and_print(message=group['Message'], function=run_wimextract, cs='Done', source=global_vars['Data']['Source'].path, items=group['Items'], dest=group['Destination'])
def umount_backup_shares():
"""Unnount the backup shares regardless of current status."""
for server in BACKUP_SERVERS:
try:
# Umount
run_program('net use \\\\{IP}\\{Share} /delete'.format(**server))
print_info('Umounted {Name}'.format(**server))
server['Mounted'] = False
except:
print_error('Failed to umount \\\\{Name}\\{Share}.'.format(**server))
sleep(1)
def wim_contains(source_path=None, file_path=None):
if file_path is None or source_path is None:
raise Exception
_cmd = [
global_vars['Tools']['wimlib-imagex'], 'dir',
'{source}'.format(source=source_path), '1',
'--path={}'.format(file_path),
'--one-file-only']
try:
run_program(_cmd)
except subprocess.CalledProcessError:
return False
else:
return True
# Kit Updates
def download_file(out_dir, out_name, source_url):
"""Downloads a file using curl."""
extract_item('curl', silent=True)
_cmd = [
global_vars['Tools']['curl'],
# '-#LSfo', # ProgressBar
'-Lfso',
'{out_dir}/{out_name}'.format(out_dir=out_dir, out_name=out_name),
source_url]
os.makedirs(out_dir, exist_ok=True)
run_program(_cmd, pipe=False)
def resolve_dynamic_url(source_url, regex):
"""Download the "download page" and scan for a url using the regex provided; returns str."""
# Download the "download page"
extract_item('curl', silent=True)
_tmp_file = '{TmpDir}/webpage.tmp'.format(**global_vars)
_cmd = [
global_vars['Tools']['curl'],
'-#LSfo',
_tmp_file,
source_url]
try:
run_program(_cmd)
except:
# "Fail silently as the download_file() function will catch it
return None
# Scan the file for the regex
with open(_tmp_file, 'r') as file:
for line in file:
if re.search(regex, line):
_url = line.strip()
_url = re.sub(r'.*(a |)href="([^"]+)".*', r'\2', _url)
_url = re.sub(r".*(a |)href='([^']+)'.*", r'\2', _url)
break
# Cleanup and return
os.remove(_tmp_file)
return _url
def update_adwcleaner():
_path = global_vars['BinDir']
_name = 'AdwCleaner.exe'
_dl_page = 'http://www.bleepingcomputer.com/download/adwcleaner/dl/125/'
_regex = r'href=.*http(s|)://download\.bleepingcomputer\.com/dl/[a-zA-Z0-9]+/[a-zA-Z0-9]+/windows/security/security-utilities/a/adwcleaner/AdwCleaner\.exe'
_url = resolve_dynamic_url(_dl_page, _regex)
download_file(_path, _name, _url)
def update_eset():
_path = global_vars['BinDir']
_name = 'ESET.exe'
_url = 'http://download.eset.com/special/eos/esetsmartinstaller_enu.exe'
download_file(_path, _name, _url)
def update_jrt():
_path = global_vars['BinDir']
_name = 'JRT.exe'
_url = 'http://downloads.malwarebytes.org/file/jrt'
download_file(_path, _name, _url)
def update_kvrt():
_path = global_vars['BinDir']
_name = 'KVRT.exe'
_url = 'http://devbuilds.kaspersky-labs.com/devbuilds/KVRT/latest/full/KVRT.exe'
download_file(_path, _name, _url)
def update_hitmanpro():
_path = '{BinDir}/HitmanPro'.format(**global_vars)
_name = 'HitmanPro.exe'
_url = 'http://dl.surfright.nl/HitmanPro.exe'
download_file(_path, _name, _url)
_name = 'HitmanPro64.exe'
_url = 'http://dl.surfright.nl/HitmanPro_x64.exe'
download_file(_path, _name, _url)
def update_intel_driver_utility():
_path = '{BinDir}/_Drivers'.format(**global_vars)
_name = 'Intel Driver Update Utility.exe'
_dl_page = 'http://www.intel.com/content/www/us/en/support/detect.html'
_regex = r'a href.*http(s|)://downloadmirror\.intel\.com/[a-zA-Z0-9]+/[a-zA-Z0-9]+/Intel%20Driver%20Update%20Utility%20Installer.exe'
_url = resolve_dynamic_url(_dl_page, _regex)
_url = resolve_dynamic_url(_dl_page, _regex)
download_file(_path, _name, _url)
def update_intel_ssd_toolbox():
_path = '{BinDir}/_Drivers'.format(**global_vars)
_name = 'Intel SSD Toolbox.exe'
_dl_page = 'https://downloadcenter.intel.com/download/26085/Intel-Solid-State-Drive-Toolbox'
_regex = r'href=./downloads/eula/[0-9]+/Intel-Solid-State-Drive-Toolbox.httpDown=https\%3A\%2F\%2Fdownloadmirror\.intel\.com\%2F[0-9]+\%2Feng\%2FIntel\%20SSD\%20Toolbox\%20-\%20v[0-9\.]+.exe'
_url = resolve_dynamic_url(_dl_page, _regex)
_url = re.sub(r'.*httpDown=(.*)', r'\1', _url, flags=re.IGNORECASE)
_url = _url.replace('%3A', ':')
_url = _url.replace('%2F', '/')
download_file(_path, _name, _url)
def update_rkill():
_path = '{BinDir}/RKill'.format(**global_vars)
_name = 'RKill.exe'
_dl_page = 'http://www.bleepingcomputer.com/download/rkill/dl/10/'
_regex = r'href=.*http(s|)://download\.bleepingcomputer\.com/dl/[a-zA-Z0-9]+/[a-zA-Z0-9]+/windows/security/security-utilities/r/rkill/rkill\.exe'
_url = resolve_dynamic_url(_dl_page, _regex)
download_file(_path, _name, _url)
def update_samsung_magician():
print_warning('Disabled.')
#~Broken~# _path = '{BinDir}/_Drivers'.format(**global_vars)
#~Broken~# _name = 'Samsung Magician.zip'
#~Broken~# _dl_page = 'http://www.samsung.com/semiconductor/minisite/ssd/download/tools.html'
#~Broken~# _regex = r'href=./semiconductor/minisite/ssd/downloads/software/Samsung_Magician_Setup_v[0-9]+.zip'
#~Broken~# _url = resolve_dynamic_url(_dl_page, _regex)
#~Broken~# # Convert relative url to absolute
#~Broken~# _url = 'http://www.samsung.com' + _url
#~Broken~# download_file(_path, _name, _url)
#~Broken~# # Extract and replace old copy
#~Broken~# _args = [
#~Broken~# 'e', '"{BinDir}/_Drivers/Samsung Magician.zip"'.format(**global_vars),
#~Broken~# '-aoa', '-bso0', '-bsp0',
#~Broken~# '-o"{BinDir}/_Drivers"'.format(**global_vars)
#~Broken~# ]
#~Broken~# run_program(seven_zip, _args)
#~Broken~# try:
#~Broken~# os.remove('{BinDir}/_Drivers/Samsung Magician.zip'.format(**global_vars))
#~Broken~# #~PoSH~# Move-Item "$bin\_Drivers\Samsung*exe" "$bin\_Drivers\Samsung Magician.exe" $path 2>&1 | Out-Null
#~Broken~# except:
#~Broken~# pass
pass
def update_sysinternalssuite():
_path = '{BinDir}/tmp'.format(**global_vars)
_name = 'SysinternalsSuite.zip'
_url = 'https://download.sysinternals.com/files/SysinternalsSuite.zip'
download_file(_path, _name, _url)
# Extract
_args = [
'e', '"{BinDir}/tmp/SysinternalsSuite.zip"'.format(**global_vars),
'-aoa', '-bso0', '-bsp0',
'-o"{BinDir}/SysinternalsSuite"'.format(**global_vars)]
run_program(seven_zip, _args)
try:
os.remove('{BinDir}/tmp/SysinternalsSuite.zip'.format(**global_vars))
except:
pass
def update_tdsskiller():
_path = global_vars['BinDir']
_name = 'TDSSKiller.exe'
_url = 'http://media.kaspersky.com/utilities/VirusUtilities/EN/tdsskiller.exe'
download_file(_path, _name, _url)
# Installations
def install_adobe_reader():
cmd = [
'{BaseDir}/Installers/Extras/Office/Adobe Reader DC.exe'.format(**global_vars),
'/sAll',
'/msi', '/norestart', '/quiet',
'ALLUSERS=1',
'EULA_ACCEPT=YES']
try_and_print(message='Adobe Reader DC...', function=run_program, cmd=cmd)
def install_chrome_extensions():
winreg.CreateKeyEx(winreg.HKEY_LOCAL_MACHINE, r'Software\Google\Chrome\Extensions', 0, winreg.KEY_WRITE | winreg.KEY_WOW64_32KEY)
winreg.CreateKeyEx(winreg.HKEY_LOCAL_MACHINE, r'Software\Google\Chrome\Extensions\cjpalhdlnbpafiamejdnhcphjbkeiagm', 0, winreg.KEY_WRITE | winreg.KEY_WOW64_32KEY)
with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, r'Software\Google\Chrome\Extensions\cjpalhdlnbpafiamejdnhcphjbkeiagm', 0, winreg.KEY_WRITE | winreg.KEY_WOW64_32KEY) as _key:
winreg.SetValueEx(_key, 'update_url', 0, winreg.REG_SZ, 'https://clients2.google.com/service/update2/crx')
winreg.CreateKeyEx(winreg.HKEY_LOCAL_MACHINE, r'Software\Google\Chrome\Extensions\pgdnlhfefecpicbbihgmbmffkjpaplco', 0, winreg.KEY_WRITE | winreg.KEY_WOW64_32KEY)
with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, r'Software\Google\Chrome\Extensions\pgdnlhfefecpicbbihgmbmffkjpaplco', 0, winreg.KEY_WRITE | winreg.KEY_WOW64_32KEY) as _key:
winreg.SetValueEx(_key, 'update_url', 0, winreg.REG_SZ, 'https://clients2.google.com/service/update2/crx')
def install_classicstart_skin():
if global_vars['OS']['Version'] not in ['8', '10']:
raise UnsupportedOSError
extract_item('ClassicStartSkin', silent=True)
_source = '{BinDir}\\ClassicStartSkin\\Metro-Win10-Black.skin7'.format(**global_vars)
_dest_path = '{PROGRAMFILES}\\Classic Shell\\Skins'.format(**global_vars['Env'])
_dest = '{dest_path}\\Metro-Win10-Black.skin7'.format(dest_path=_dest_path)
os.makedirs(_dest_path, exist_ok=True)
shutil.copy(_source, _dest)
def install_firefox_extensions():
extract_item('FirefoxExtensions', silent=True)
extensions = ['uBlock0@raymondhill.net']
dests = ['{PROGRAMFILES}\\Mozilla Firefox\\distribution\\extensions'.format(**global_vars['Env'])]
if 'PROGRAMFILES(X86)' in global_vars['Env']:
dests.append('{PROGRAMFILES(X86)}\\Mozilla Firefox\\distribution\\extensions'.format(**global_vars['Env']))
for extension in extensions:
_source = '{BinDir}\\FirefoxExtensions\\{extension}'.format(extension=extension, **global_vars)
for dest in dests:
_dest = '{}\\{}'.format(dest, extension)
if not os.path.exists(dest):
shutil.copytree(_source, _dest)
def install_ninite_bundle(mse=False):
if global_vars['OS']['Version'] in ['8', '10']:
# Modern selection
popen_program('{BaseDir}/Installers/Extras/Bundles/Modern.exe'.format(**global_vars))
else:
# Legacy selection
if mse:
popen_program('{BaseDir}/Installers/Extras/Security/Microsoft Security Essentials.exe'.format(**global_vars))
popen_program('{BaseDir}/Installers/Extras/Bundles/Legacy.exe'.format(**global_vars))
def install_vcredists():
extract_item('_vcredists', silent=True)
prev_dir = os.getcwd()
os.chdir('{BinDir}/_vcredists'.format(**global_vars))
for vcr in VCR_REDISTS:
try_and_print(message=vcr['Name'], function=run_program, cmd=vcr['Cmd'])
os.chdir(prev_dir)
# Network
def check_connection():
while True:
result = try_and_print(message='Ping test...', function=ping_test, cs='OK')
if result['CS']:
break
else:
if not ask('ERROR: System appears offline, try again?'):
abort()
def ping_test(addr='google.com'):
"""Attempt to ping addr and if unsuccessful either retry or abort."""
_cmd = ['ping', '-n', '2', addr]
run_program(_cmd)
# OSR / VR
def run_autoruns():
"""Run AutoRuns in the background with VirusTotal checks enabled."""
extract_item('SysinternalsSuite', filter='autoruns*', silent=True)
winreg.CreateKey(winreg.HKEY_CURRENT_USER, r'Software\Sysinternals\AutoRuns')
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Sysinternals\AutoRuns', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'checkvirustotal', 0, winreg.REG_DWORD, 1)
winreg.SetValueEx(_key, 'EulaAccepted', 0, winreg.REG_DWORD, 1)
winreg.SetValueEx(_key, 'shownomicrosoft', 0, winreg.REG_DWORD, 1)
winreg.SetValueEx(_key, 'shownowindows', 0, winreg.REG_DWORD, 1)
winreg.SetValueEx(_key, 'showonlyvirustotal', 0, winreg.REG_DWORD, 1)
winreg.SetValueEx(_key, 'submitvirustotal', 0, winreg.REG_DWORD, 0)
winreg.SetValueEx(_key, 'verifysignatures', 0, winreg.REG_DWORD, 1)
winreg.CreateKey(winreg.HKEY_CURRENT_USER, r'Software\Sysinternals\AutoRuns\SigCheck')
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Sysinternals\AutoRuns\SigCheck', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'EulaAccepted', 0, winreg.REG_DWORD, 1)
winreg.CreateKey(winreg.HKEY_CURRENT_USER, r'Software\Sysinternals\AutoRuns\Streams')
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Sysinternals\AutoRuns\Streams', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'EulaAccepted', 0, winreg.REG_DWORD, 1)
winreg.CreateKey(winreg.HKEY_CURRENT_USER, r'Software\Sysinternals\AutoRuns\VirusTotal')
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Sysinternals\AutoRuns\VirusTotal', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'VirusTotalTermsAccepted', 0, winreg.REG_DWORD, 1)
popen_program(global_vars['Tools']['AutoRuns'], minimized=True)
def run_chkdsk():
"""Run CHKDSK in a "split window" and report errors."""
if global_vars['OS']['Version'] in ['8', '10']:
_cmd = [
'chkdsk',
'{SYSTEMDRIVE}'.format(**global_vars['Env']),
'/scan', '/perf']
else:
_cmd = [
'chkdsk',
'{SYSTEMDRIVE}'.format(**global_vars['Env'])]
_out = run_program(_cmd, check=False)
# retcode == 0: no issues
# retcode == 1: fixed issues (also happens when chkdsk.exe is killed?)
# retcode == 2: issues
if int(_out.returncode) > 0:
# print_error(' ERROR: CHKDSK encountered errors')
raise GenericError
# Save stderr
with open('{LogDir}\\CHKDSK.err'.format(**global_vars), 'a') as f:
for line in _out.stderr.decode().splitlines():
f.write(line.strip() + '\n')
# Save stdout
with open('{LogDir}\\CHKDSK.log'.format(**global_vars), 'a') as f:
for line in _out.stdout.decode().splitlines():
f.write(line.strip() + '\n')
def run_chkdsk_offline():
"""Set filesystem 'dirty bit' to force a chkdsk during next boot."""
_cmd = [
'fsutil', 'dirty', 'set',
'{SYSTEMDRIVE}'.format(**global_vars['Env'])]
_out = run_program(_cmd, check=False)
if int(_out.returncode) > 0:
raise GenericError
def run_chkdsk_spotfix():
"""Run CHKDSK in a "split window" and report errors."""
if global_vars['OS']['Version'] in ['8', '10']:
_cmd = [
'chkdsk',
'{SYSTEMDRIVE}'.format(**global_vars['Env']),
'/scan', '/perf']
else:
raise UnsupportedOSError
_out = run_program(_cmd, check=False)
# retcode == 0: no issues
# retcode == 1: fixed issues (also happens when chkdsk.exe is killed?)
# retcode == 2: issues
if int(_out.returncode) > 0:
# print_error(' ERROR: CHKDSK encountered errors')
raise GenericError
# Save stderr
with open('{LogDir}\\CHKDSK_Spotfix.err'.format(**global_vars), 'a') as f:
for line in _out.stderr.decode().splitlines():
f.write(line.strip() + '\n')
# Save stdout
with open('{LogDir}\\CHKDSK_Spotfix.log'.format(**global_vars), 'a') as f:
for line in _out.stdout.decode().splitlines():
f.write(line.strip() + '\n')
def run_dism_restore_health():
"""Run DISM /ScanHealth, then /CheckHealth, and then report errors."""
if global_vars['OS']['Version'] in ['8', '10']:
# Scan Health
_cmd = [
'DISM', '/Online', '/Cleanup-Image', '/RestoreHealth',
'/LogPath:"{LogDir}\\DISM_RestoreHealth.log"'.format(**global_vars),
'-new_console:n', '-new_console:s33V']
run_program(_cmd, pipe=False, check=False, shell=True)
wait_for_process('dism')
# Now check health
_cmd = [
'DISM', '/Online', '/Cleanup-Image', '/CheckHealth',
'/LogPath:"{LogDir}\\DISM_CheckHealth.log"'.format(**global_vars)]
_result = run_program(_cmd, shell=True).stdout.decode()
# Check result
if re.search(r'No component store corruption detected', _result, re.IGNORECASE):
pass
else:
raise GenericError
else:
raise UnsupportedOSError
def run_dism_scan_health():
"""Run DISM /ScanHealth, then /CheckHealth, and then report errors."""
if global_vars['OS']['Version'] in ['8', '10']:
# Scan Health
_cmd = [
'DISM', '/Online', '/Cleanup-Image', '/ScanHealth',
'/LogPath:"{LogDir}\\DISM_ScanHealth.log"'.format(**global_vars),
'-new_console:n', '-new_console:s33V']
run_program(_cmd, pipe=False, check=False, shell=True)
wait_for_process('dism')
# Now check health
_cmd = [
'DISM', '/Online', '/Cleanup-Image', '/CheckHealth',
'/LogPath:"{LogDir}\\DISM_CheckHealth.log"'.format(**global_vars)]
_result = run_program(_cmd, shell=True).stdout.decode()
# Check result
if re.search(r'No component store corruption detected', _result, re.IGNORECASE):
pass
else:
raise GenericError
else:
raise UnsupportedOSError
def run_hitmanpro():
"""Run HitmanPro in the background."""
extract_item('HitmanPro', silent=True)
_cmd = [
global_vars['Tools']['HitmanPro'],
'/quiet', '/noinstall', '/noupload',
'/log={LogDir}\\hitman.xml'.format(**global_vars)]
popen_program(_cmd)
def run_kvrt():
"""Run KVRT."""
extract_item('KVRT', silent=True)
os.makedirs(global_vars['QuarantineDir'], exist_ok=True)
_cmd = [
global_vars['Tools']['KVRT'],
'-accepteula', '-dontcryptsupportinfo', '-fixednames',
'-d', global_vars['QuarantineDir'],
'-processlevel', '3']
popen_program(_cmd, pipe=False)
def run_process_killer():
"""Kill most running processes skipping those in the whitelist.txt."""
# borrowed from TronScript (reddit.com/r/TronScript) and credit to /u/cuddlychops06
_prev_dir = os.getcwd()
extract_item('ProcessKiller', silent=True)
os.chdir('{BinDir}\\ProcessKiller'.format(**global_vars))
run_program(['ProcessKiller.exe', '/silent'], check=False)
os.chdir(_prev_dir)
def run_rkill():
"""Run RKill and cleanup afterwards."""
extract_item('RKill', silent=True)
_cmd = [
global_vars['Tools']['RKill'],
'-l', '{LogDir}\\RKill.log'.format(**global_vars),
'-new_console:n', '-new_console:s33V']
run_program(_cmd, check=False)
wait_for_process('RKill')
kill_process('notepad.exe')
# RKill cleanup
if os.path.exists('{USERPROFILE}\\Desktop'.format(**global_vars['Env'])):
for item in os.scandir('{USERPROFILE}\\Desktop'.format(**global_vars['Env'])):
if re.search(r'^RKill', item.name, re.IGNORECASE):
_name = re.sub(r'^(.*)\.', '\1_{Date-Time}'.format(**global_vars), item.name, re.IGNORECASE)
_name = '{ClientDir}\\Info\\{name}'.format(name=_name, **global_vars)
shutil.move(item.path, non_clobber_rename(_name))
def run_sfc_scan():
"""Run SFC in a "split window" and report errors."""
_cmd = [
'{SYSTEMROOT}\\System32\\sfc.exe'.format(**global_vars['Env']),
'/scannow']
_out = run_program(_cmd, check=False)
# Save stderr
with open('{LogDir}\\SFC.err'.format(**global_vars), 'a') as f:
for line in _out.stderr.decode('utf-8', 'ignore').splitlines():
f.write(line.strip() + '\n')
# Save stdout
with open('{LogDir}\\SFC.log'.format(**global_vars), 'a') as f:
for line in _out.stdout.decode('utf-8', 'ignore').splitlines():
f.write(line.strip() + '\n')
# Check result
log_text = _out.stdout.decode('utf-8', 'ignore').replace('\0', '')
if re.findall(r'did\s+not\s+find\s+any\s+integrity\s+violations', log_text):
pass
elif re.findall(r'successfully\s+repaired\s+them', log_text):
raise GenericRepair
else:
raise GenericError
def run_tdsskiller():
"""Run TDSSKiller."""
extract_item('TDSSKiller', silent=True)
os.makedirs('{QuarantineDir}\\TDSSKiller'.format(**global_vars), exist_ok=True)
_cmd = [
global_vars['Tools']['TDSSKiller'],
'-l', '{LogDir}\\TDSSKiller.log'.format(**global_vars),
'-qpath', '{QuarantineDir}\\TDSSKiller'.format(**global_vars),
'-accepteula', '-accepteulaksn',
'-dcexact', '-tdlfs']
run_program(_cmd, pipe=False)
# Windows Activation
def activate_windows_with_bios():
"""Attempt to activate Windows with a key stored in the BIOS."""
# Code borrowed from https://github.com/aeruder/get_win8key
#####################################################
#script to query windows 8.x OEM key from PC firmware
#ACPI -> table MSDM -> raw content -> byte offset 56 to end
#ck, 03-Jan-2014 (christian@korneck.de)
#####################################################
bios_key = None
table = b"MSDM"
if acpi.FindAcpiTable(table) is True:
rawtable = acpi.GetAcpiTable(table)
#http://msdn.microsoft.com/library/windows/hardware/hh673514
#byte offset 36 from beginning = Microsoft 'software licensing data structure' / 36 + 20 bytes offset from beginning = Win Key
bios_key = rawtable[56:len(rawtable)].decode("utf-8")
else:
raise Exception('ACPI table {} not found.'.format(str(table)))
if bios_key is None:
raise BIOSKeyNotFoundError
# Install Key
run_program('cscript {SYSTEMROOT}\\System32\\slmgr.vbs /ipk {pkey} //nologo'.format(**global_vars['Env'], pkey=bios_key), check=False)
sleep(5)
# Attempt activation
run_program('cscript {SYSTEMROOT}\\System32\\slmgr.vbs /ato //nologo'.format(**global_vars['Env']), check=False)
sleep(5)
# Check status
if not windows_is_activated():
raise Exception('Activation Failed')
def update_windows_activation_status():
if not re.search(r'(permanent|safe mode)', global_vars['OS']['Activation'], re.IGNORECASE):
_out = run_program('cscript /nologo {SYSTEMROOT}\\System32\\slmgr.vbs /xpr'.format(**global_vars['Env']))
_out = _out.stdout.decode().splitlines()
_out = [l for l in _out if re.match(r'^\s', l)]
if len(_out) > 0:
global_vars['OS']['Activation'] = re.sub(r'^\s+', '', _out[0])
else:
global_vars['OS']['Activation'] = 'Activation status unknown'
def windows_is_activated():
"""Updates activation status, checks if activated, and returns a bool."""
update_windows_activation_status()
return re.search(r'permanent', global_vars['OS']['Activation'], re.IGNORECASE)
# System Info
def backup_file_list():
"""Export current file listing for the system."""
extract_item('Everything', silent=True)
_cmd = [
global_vars['Tools']['Everything'],
'-nodb',
'-create-filelist',
'{LogDir}\\File List.txt'.format(**global_vars),
'{SYSTEMDRIVE}'.format(**global_vars['Env'])]
run_program(_cmd)
def backup_power_plans():
"""Export current power plans."""
os.makedirs('{BackupDir}\\Power Plans'.format(**global_vars), exist_ok=True)
_plans = run_program('powercfg /L')
_plans = _plans.stdout.decode().splitlines()
_plans = [p for p in _plans if re.search(r'^Power Scheme', p)]
for p in _plans:
_guid = re.sub(r'Power Scheme GUID:\s+([0-9a-f\-]+).*', r'\1', p)
_name = re.sub(r'Power Scheme GUID:\s+[0-9a-f\-]+\s+\(([^\)]+)\).*', r'\1', p)
# print(' {name} ({guid})'.format(guid=_guid, name=_name))
_out = '{BackupDir}\\Power Plans\\{name}.pow'.format(name=_name, **global_vars)
if not os.path.exists(_out):
_cmd = ['powercfg', '-export', _out, _guid]
run_program(_cmd, check=False)
def backup_registry():
extract_item('erunt', silent=True)
_args = [
'{LogDir}\\Registry'.format(**global_vars),
'sysreg',
'curuser',
'otherusers',
'/noprogresswindow']
run_program(global_vars['Tools']['ERUNT'], _args)
def compress_info():
path = '{ClientDir}'.format(**global_vars)
file = 'Info_{Date-Time}.7z'.format(**global_vars)
_cmd = [
global_vars['Tools']['SevenZip'],
'a', '-t7z', '-mx=9', '-bso0', '-bse0',
'{path}\\{file}'.format(path=path, file=file),
'{ClientDir}\\Info'.format(**global_vars)]
run_program(_cmd)
def find_software_hives():
"""Search for transferred SW hives and return a list."""
hives = []
search_paths = [global_vars['ClientDir']]
while len(search_paths) > 0:
for item in os.scandir(search_paths.pop(0)):
if item.is_dir() and REGEX_REGISTRY_DIRS.search(item.name):
search_paths.append(item.path)
if item.is_file() and REGEX_SOFTWARE_HIVE.search(item.name):
hives.append(item.path)
return hives
def get_installed_office():
programs = []
with open ('{LogDir}\\Installed Program List (AIDA64).txt'.format(**global_vars), 'r') as f:
for line in sorted(f.readlines()):
if REGEX_OFFICE.search(line):
programs.append(line[4:82].strip())
if len(programs) == 0:
programs = ['No programs found']
return programs
def get_product_keys():
keys = []
with open ('{LogDir}\\Product Keys (ProduKey).txt'.format(**global_vars), 'r') as f:
for line in f.readlines():
if re.search(r'^Product Name', line):
line = re.sub(r'^Product Name\s+:\s+(.*)', r'\1', line.strip())
keys.append(line)
if len(keys) == 0:
keys = ['No product keys found']
return keys
def get_user_data_size_info(all_users=True, indent=8, width=32):
"""Get size of user folders for all users and return a dict of dicts."""
users = {}
TMP_HIVE_PATH = 'HKU\\wk_tmp'
# Extract and configure du
extract_item('SysinternalsSuite', filter='du*', silent=True)
winreg.CreateKey(winreg.HKEY_CURRENT_USER, r'Software\Sysinternals\Du')
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Sysinternals\Du', access=winreg.KEY_WRITE) as _key:
winreg.SetValueEx(_key, 'EulaAccepted', 0, winreg.REG_DWORD, 1)
try:
# Get SIDs
if all_users:
out = run_program('wmic useraccount get sid')
else:
out = run_program('wmic useraccount where name="{USERNAME}" get sid'.format(**global_vars['Env']))
sids = out.stdout.decode().splitlines()
sids = [s.strip() for s in sids if re.search(r'-1\d+$', s.strip())]
# Get Usernames and add to _users
for sid in sids:
try:
out = run_program('wmic useraccount where sid="{sid}" get name'.format(sid=sid))
name = out.stdout.decode().splitlines()[2].strip()
users[name] = {'Extra Folders': {}, 'Shell Folders': {}, 'SID': sid}
except:
# Just skip problem users
pass
except subprocess.CalledProcessError:
# This results in an empty dict being returned, leaving it to the calling section to handle that case
pass
# Use username/SID pairs to check profile folder sizes
for u in users.keys():
try:
# Main Profile path
key = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\{SID}'.format(**users[u])
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key) as _key:
users[u]['ProfileImagePath'] = winreg.QueryValueEx(_key, 'ProfileImagePath')[0]
try:
out = run_program(global_vars['Tools']['Du'], ['-nobanner', '-q', users[u]['ProfileImagePath']])
size = out.stdout.decode().splitlines()[4]
size = re.sub(r'Size:\s+([\d,]+)\sbytes$', r'\1', size)
size = size.replace(',', '')
size = human_readable_size(size)
size_str = '{indent}{folder:<{width}}{size:>6} ({path})'.format(
indent = ' ' * indent,
width = width,
folder = 'Profile',
size = size,
path = users[u]['ProfileImagePath'])
users[u]['ProfileSize'] = size_str
except subprocess.CalledProcessError:
# Failed to get folder size
pass
# Check if user hive is already loaded
unload_hive = False
try:
# This tests if the user hive is already loaded and throws FileNotFoundError if not.
winreg.QueryValue(winreg.HKEY_USERS, users[u]['SID'])
except FileNotFoundError:
# User not logged-in. Loading hive and setting unload_hive so it will be unloaded before the script exits.
try:
_cmd = 'reg load {tmp_path} "{ProfileImagePath}\\NTUSER.DAT"'.format(tmp_path=TMP_HIVE_PATH, **users[u])
run_program(_cmd)
unload_hive = True
except subprocess.CalledProcessError:
# Failed to load user hive
pass
# Get Shell folder sizes
key = r'{SID}\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'.format(**users[u])
try:
with winreg.OpenKey(winreg.HKEY_USERS, key) as _key:
for folder in SHELL_FOLDERS.keys():
for value in SHELL_FOLDERS[folder]:
try:
# Query value and break out of for look if successful
folder_path = winreg.QueryValueEx(_key, value)[0]
try:
# Finally calculate folder size
out = run_program(global_vars['Tools']['Du'], ['-nobanner', '-q', folder_path])
size = out.stdout.decode().splitlines()[4]
size = re.sub(r'Size:\s+([\d,]+)\sbytes$', r'\1', size)
size = size.replace(',', '')
size = human_readable_size(size)
str = '{indent}{folder:<{width}}{size:>6} ({path})'.format(
indent = ' ' * indent,
width = width,
folder = folder,
size = size,
path = folder_path)
users[u]['Shell Folders'][folder] = str
except subprocess.CalledProcessError:
# Failed to get folder size
pass
break
except FileNotFoundError:
# Failed to query value above
pass
except FileNotFoundError:
# Can't read the user hive, skipping this user.
pass
# Extra shell folder check
for folder in SHELL_FOLDERS.keys():
if folder not in users[u]['Shell Folders']:
folder_path = '{ProfileImagePath}\\{folder}'.format(folder=folder, **users[u])
if os.path.exists(folder_path):
try:
out = run_program(global_vars['Tools']['Du'], ['-nobanner', '-q', folder_path])
size = out.stdout.decode().splitlines()[4]
size = re.sub(r'Size:\s+([\d,]+)\sbytes$', r'\1', size)
size = size.replace(',', '')
size = human_readable_size(size)
str = '{indent}{folder:<{width}}{size:>6} ({path})'.format(
indent = ' ' * indent,
width = width,
folder = folder,
size = size,
path = folder_path)
users[u]['Shell Folders'][folder] = str
except subprocess.CalledProcessError:
# Failed to get folder size
pass
# Extra folder sizes
for folder in EXTRA_FOLDERS:
folder_path = '{ProfileImagePath}\\{folder}'.format(folder=folder, **users[u])
if os.path.exists(folder_path):
try:
out = run_program(global_vars['Tools']['Du'], ['-nobanner', '-q', folder_path])
size = size.stdout.decode().splitlines()[4]
size = re.sub(r'Size:\s+([\d,]+)\sbytes$', r'\1', size)
size = size.replace(',', '')
size = human_readable_size(size)
str = '{indent}{folder:<{width}}{size:>6} ({path})'.format(
indent = ' ' * indent,
width = width,
folder = folder,
size = size,
path = folder_path)
users[u]['Extra Folders'][folder] = str
except subprocess.CalledProcessError:
# Failed to get folder size
pass
# Unload user hive (if necessary)
if unload_hive:
_cmd = 'reg unload {tmp_path}'.format(tmp_path=TMP_HIVE_PATH)
run_program(_cmd, check=False)
except FileNotFoundError:
# Can't find the ProfileImagePath, skipping this user.
pass
except:
# Unload the wk_tmp hive no matter what
_cmd = 'reg unload {tmp_path}'.format(tmp_path=TMP_HIVE_PATH)
run_program(_cmd, check=False)
# Done
return users
def run_aida64():
extract_item('AIDA64', silent=True)
# All system info
if not os.path.exists('{LogDir}\\System Information (AIDA64).html'.format(**global_vars)):
_cmd = [
global_vars['Tools']['AIDA64'],
'/R', '{LogDir}\\System Information (AIDA64).html'.format(**global_vars),
'/CUSTOM', '{BinDir}\\AIDA64\\full.rpf'.format(**global_vars),
'/HTML', '/SILENT', '/SAFEST']
run_program(_cmd, check=False)
# Installed Programs
if not os.path.exists('{LogDir}\\Installed Program List (AIDA64).txt'.format(**global_vars)):
_cmd = [
global_vars['Tools']['AIDA64'],
'/R', '{LogDir}\\Installed Program List (AIDA64).txt'.format(**global_vars),
'/CUSTOM', '{BinDir}\\AIDA64\\installed_programs.rpf'.format(**global_vars),
'/TEXT', '/SILENT', '/SAFEST']
run_program(_cmd, check=False)
# Product Keys
if not os.path.exists('{LogDir}\\Product Keys (AIDA64).txt'.format(**global_vars)):
_cmd = [
global_vars['Tools']['AIDA64'],
'/R', '{LogDir}\\Product Keys (AIDA64).txt'.format(**global_vars),
'/CUSTOM', '{BinDir}\\AIDA64\\licenses.rpf'.format(**global_vars),
'/TEXT', '/SILENT', '/SAFEST']
run_program(_cmd, check=False)
def run_bleachbit():
if not os.path.exists('{LogDir}\\BleachBit.log'.format(**global_vars)):
extract_item('BleachBit', silent=True)
_cmd = [global_vars['Tools']['BleachBit'], '--preview', '--preset']
_out = run_program(_cmd, check=False)
# Save stderr
if len(_out.stderr.decode().splitlines()) > 0:
with open('{LogDir}\\BleachBit.err'.format(**global_vars), 'a') as f:
for line in _out.stderr.decode().splitlines():
f.write(line.strip() + '\n')
# Save stdout
with open('{LogDir}\\BleachBit.log'.format(**global_vars), 'a') as f:
for line in _out.stdout.decode().splitlines():
f.write(line.strip() + '\n')
def run_hwinfo_sensors():
_path = '{BinDir}\\HWiNFO'.format(**global_vars)
for bit in [32, 64]:
# Configure
_source = '{path}\\general.ini'.format(path=_path)
_dest = '{path}\\HWiNFO{bit}.ini'.format(bit=bit, path=_path)
shutil.copy(_source, _dest)
with open(_dest, 'a') as f:
f.write('SensorsOnly=1\n')
f.write('SummaryOnly=0\n')
popen_program(global_vars['Tools']['HWiNFO'])
def run_produkey():
extract_item('ProduKey', silent=True)
if not os.path.exists('{LogDir}\\Product Keys (ProduKey).txt'.format(**global_vars)):
# Clear current configuration
for config in ['ProduKey.cfg', 'ProduKey64.cfg']:
try:
if os.path.exists('{BinDir}\\ProduKey\\{config}'.format(config=config, **global_vars)):
os.remove('{BinDir}\\ProduKey\\{config}'.format(config=config, **global_vars))
except:
pass
_cmd = [
global_vars['Tools']['ProduKey'], '/nosavereg',
'/stext', '{LogDir}\\Product Keys (ProduKey).txt'.format(**global_vars)]
run_program(_cmd, check=False)
def run_xmplay():
extract_item('XMPlay', silent=True)
popen_program([global_vars['Tools']['XMPlay'], '{BinDir}\\XMPlay\\music.7z'.format(**global_vars)])
def show_disk_usage(disk=None):
if disk is None:
raise Exception
print_standard(disk.device.replace('\\', ' '), end='', flush=True, timestamp=False)
try:
usage = psutil.disk_usage(disk.device)
display_string = '{percent:>5.2f}% Free ({free} / {total})'.format(
percent = 100 - usage.percent,
free = human_readable_size(usage.free, 2),
total = human_readable_size(usage.total, 2))
if usage.percent > 85:
print_error(display_string, timestamp=False)
elif usage.percent > 75:
print_warning(display_string, timestamp=False)
else:
print_standard(display_string, timestamp=False)
except:
print_warning('Unknown', timestamp=False)
def show_free_space():
"""Show free space info for all fixed disks."""
message = 'Free Space:'
for disk in psutil.disk_partitions():
try:
if 'fixed' in disk.opts:
try_and_print(disk=disk, message=message, function=show_disk_usage, ns='Unknown', silent_function=False)
message = ''
except:
pass
def show_installed_ram():
mem = psutil.virtual_memory()
if mem.total > 5905580032:
# > 5.5 Gb so 6Gb or greater
print_standard(human_readable_size(mem.total).strip())
elif mem.total > 3758096384:
# > 3.5 Gb so 4Gb or greater
print_warning(human_readable_size(mem.total).strip())
else:
print_error(human_readable_size(mem.total).strip())
def show_os_activation():
act_str = global_vars['OS']['Activation']
if re.search(r'permanent', act_str, re.IGNORECASE):
print_standard(act_str, timestamp=False)
elif re.search(r'unavailable', act_str, re.IGNORECASE):
print_warning(act_str, timestamp=False)
else:
print_error(act_str, timestamp=False)
def show_os_name():
os_name = global_vars['OS']['DisplayName']
if global_vars['OS']['Arch'] == 32:
# Show all 32-bit installs as an error message
print_error(os_name, timestamp=False)
else:
if re.search(r'(unrecognized|very outdated)', os_name, re.IGNORECASE):
print_error(os_name, timestamp=False)
elif re.search(r'outdated', os_name, re.IGNORECASE):
print_warning(os_name, timestamp=False)
else:
print_standard(os_name, timestamp=False)
def show_temp_files_size():
# Temp file size
size = None
with open('{LogDir}\\BleachBit.log'.format(**global_vars), 'r') as f:
for line in f.readlines():
if re.search(r'^disk space to be recovered:', line, re.IGNORECASE):
size = re.sub(r'.*: ', '', line.strip())
size = re.sub(r'(\w)iB$', r' \1b', size)
if size is None:
print_warning(size, timestamp=False)
else:
print_standard(size, timestamp=False)
def show_user_data_summary(all_users=True, indent=8, width=32):
users = get_user_data_size_info(all_users)
for user in sorted(users.keys()):
print_success(' User: {user}'.format(user=user))
print_standard(users[user].get('ProfileSize', 'Unknown'))
print_standard('{}{}'.format(' '*indent, '-'*(width+6)))
for folder in sorted(users[user]['Shell Folders']):
print_standard(users[user]['Shell Folders'][folder])
for folder in sorted(users[user]['Extra Folders']):
print_standard(users[user]['Shell Folders'][folder])
def upload_info():
path = '{ClientDir}'.format(**global_vars)
file = 'Info_{Date-Time}.7z'.format(**global_vars)
upload_data(path, file)
if __name__ == '__main__':
print("This file is not meant to be called directly.")