From c6526dcfce4366f3204800e9aa379e0c13e594f8 Mon Sep 17 00:00:00 2001 From: z0z0z Date: Tue, 30 Apr 2019 03:59:36 +0000 Subject: [PATCH] Create installcab.py --- RE2/installcab.py | 308 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 RE2/installcab.py diff --git a/RE2/installcab.py b/RE2/installcab.py new file mode 100644 index 0000000..452a43e --- /dev/null +++ b/RE2/installcab.py @@ -0,0 +1,308 @@ +#! /usr/bin/env python2 +import os +import sys +import subprocess +import tempfile +import shutil + +import xml.etree.ElementTree +import re + +tmpdir = None + +####################################### +# Utils + +def cleanup(): + if not tmpdir: + return + if options['nocleanup']: + print("Not cleaning up. Find the files at %s" % tmpdir) + else: + shutil.rmtree(tmpdir) + +def bad_exit(text): + print(text) + cleanup() + sys.exit(1) + +def print_debug(text): + if options['debug']: + print(text) + +####################################### +# Architecture helpers + +arch_map = { + "amd64": "win64", + "x86": "win32", + "wow64": "wow64" +} + +dest_map = { + "win64:win32": "Syswow64", + "win64:win64": "System32", + "win64:wow64": "System32", + "win32:win32": "System32" +} + +def check_wineprefix_arch(prefix_path): + if not os.path.exists(prefix_path): + bad_exit("Wineprefix path does not exist! (%s)" % prefix_path) + system_reg_file = os.path.join(prefix_path, 'system.reg') + with open(system_reg_file) as f: + for line in f.readlines(): + if line.startswith("#arch=win32"): + return 'win32' + elif line.startswith("#arch=win64"): + return 'win64' + bad_exit("Could not determine wineprefix arch!") + +def get_system32_realdir(arch): + return dest_map[winearch+':'+arch] + +def get_dll_destdir(dll_path): + arch = check_dll_arch(dll_path) + if arch == 'win32' and winearch == 'win64': + dest_dir = syswow64_path + elif arch == 'win64' and winearch == 'win32': + bad_exit("Invalid win64 assembly for win32 wineprefix!") + else: + dest_dir = system32_path + return dest_dir + +def get_winebin(arch): + if (arch == 'win64' or arch == 'wow64') and winearch == 'win32': + bad_exit("Invalid win64 assembly for win32 wineprefix!") + elif arch == 'win32' or arch == 'wow64': + winebin = 'wine' + else: + winebin = 'wine64' + return winebin + +def check_dll_arch(dll_path): + out = subprocess.check_output(['file', dll_path]) + if 'x86-64' in out: + return 'win64' + else: + return 'win32' + +####################################### +# Manifest processing + +def replace_variables(value, arch): + if "$(" in value: + value = value.replace("$(runtime.help)", "C:\\windows\\help") + value = value.replace("$(runtime.inf)", "C:\\windows\\inf") + value = value.replace("$(runtime.wbem)", "C:\\windows\\wbem") + value = value.replace("$(runtime.windows)", "C:\\windows") + value = value.replace("$(runtime.ProgramFiles)", "C:\\windows\\Program Files") + value = value.replace("$(runtime.programFiles)", "C:\\windows\\Program Files") + value = value.replace("$(runtime.programFilesX86)", "C:\\windows\\Program Files (x86)") + value = value.replace("$(runtime.system32)", "C:\\windows\\%s" % get_system32_realdir(arch)) + value = value.replace("$(runtime.drivers)", "C:\\windows\\%s\\drivers" % get_system32_realdir(arch)) + value = value.replace("\\", "\\\\") + return value + +def process_value(rv, arch): + attrs = rv.attrib + name = attrs["name"] + value = attrs["value"] + value_type = attrs["valueType"] + if not name.strip(): + name = "@" + else: + name = "\"%s\"" % name + name = replace_variables(name, arch) + if value_type == 'REG_BINARY': + value = re.findall('..',value) + value = 'hex:'+",".join(value) + elif value_type == 'REG_DWORD': + value = "dword:%s" % value.replace("0x", "") + elif value_type == 'REG_QWORD': + value = "qword:%s" % value.replace("0x", "") + elif value_type == 'REG_NONE': + value = None + elif value_type == 'REG_EXPAND_SZ': + # not sure if we should replace this ones at this point: + # caps can vary in the pattern + value = value.replace("%SystemRoot%", "C:\\windows") + value = value.replace("%ProgramFiles%", "C:\\windows\\Program Files") + value = value.replace("%WinDir%", "C:\\windows") + value = value.replace("%ResourceDir%", "C:\\windows") + value = value.replace("%Public%", "C:\\users\\Public") + value = value.replace("%LocalAppData%", "C:\\windows\\Public\\Local Settings\\Application Data") + value = value.replace("%AllUsersProfile%", "C:\\windows") + value = value.replace("%UserProfile%", "C:\\windows") + value = value.replace("%ProgramData%", "C:\\ProgramData") + value = "\"%s\"" % value + elif value_type == 'REG_SZ': + value = "\"%s\"" % value + else: + print("warning unkown type: %s" % value_type) + value = "\"%s\"" % value + if value: + value = replace_variables(value, arch) + if options["stripdllpath"]: + if '.dll' in value: + value = value.lower().replace("c:\\\\windows\\\\system32\\\\", "") + value = value.lower().replace("c:\\\\windows\\\\syswow64\\\\", "") + return name, value + +def process_key(key): + key = key.strip("\\") + key = key.replace("HKEY_CLASSES_ROOT", "HKEY_LOCAL_MACHINE\\Software\\Classes") + return key + +def parse_manifest_arch(elmt): + registry_keys = elmt.findall("{urn:schemas-microsoft-com:asm.v3}assemblyIdentity") + if not len(registry_keys): + bad_exit("Can't find processor architecture") + arch = registry_keys[0].attrib["processorArchitecture"] + if not arch in arch_map: + bad_exit("Unknown processor architecture %s" % arch) + arch = arch_map[arch] + if (arch == 'win64' or arch == 'wow64') and winearch == 'win32': + bad_exit("Invalid 64 bit assembly for 32 bit system!") + return arch + +def process_manifest(file_name): + out = "" + elmt = xml.etree.ElementTree.parse(file_name).getroot() + arch = parse_manifest_arch(elmt) + registry_keys = elmt.findall("{urn:schemas-microsoft-com:asm.v3}registryKeys") + if len(registry_keys): + for registry_key in registry_keys[0].getchildren(): + key = process_key(registry_key.attrib["keyName"]) + out += "[%s]\n" % key + for rv in registry_key.findall("{urn:schemas-microsoft-com:asm.v3}registryValue"): + name, value = process_value(rv, arch) + if not value is None: + out += "%s=%s\n"%(name,value) + out += "\n" + return (out, arch) + + +####################################### +# Installer Processing + +def extract_from_installer(orig_file, dest_dir, component): + filter_str = "*%s*" % component + print("%s" % component.strip("_")) + cmd = ["cabextract", "-F", filter_str, "-d", dest_dir, orig_file] + subprocess.check_output(cmd) + output_files = [os.path.join(r,file) for r,d,f in os.walk(dest_dir) for file in f] + return output_files + +def load_manifest(file_path): + file_name = os.path.basename(file_path) + reg_data, arch = process_manifest(file_path) + print("- %s (%s)" % (file_name, arch)) + return reg_data, arch + +def register_dll(dll_path): + if not options["register"]: + return + arch = check_dll_arch(dll_path) + winebin = get_winebin(arch) + cmd = ["regsvr32", os.path.basename(dll_path)] + subprocess.call(cmd) + +def install_dll(dll_path): + if options["nodll"]: + return + dest_dir = get_dll_destdir(dll_path) + file_name = os.path.basename(dll_path) + print("- %s -> %s" % (file_name, dest_dir)) + shutil.copy(dll_path, dest_dir) + register_dll(os.path.join(dest_dir, file_name)) + +def install_regfile(path, reg_file, arch): + if options["noreg"]: + return + winebin = get_winebin(arch) + print_debug(" install reg %s with %s" % (reg_file, winebin)) + cmd = [winebin, "regedit", os.path.join(path, reg_file)] + subprocess.call(cmd) + +def process_files(output_files): + reg_files = [] + for file_path in output_files: + if file_path.endswith(".manifest"): + out = "Windows Registry Editor Version 5.00\n\n" + outdata, arch = load_manifest(file_path) + if outdata: + out += outdata + print_debug(" %s assembly" % arch) + with open(os.path.join(tmpdir, file_path+".reg"), "w") as f: + f.write(out) + reg_files.append((file_path, arch)) + + for file_path in output_files: + if file_path.endswith(".dll"): + install_dll(file_path) + + for file_path, arch in reg_files: + install_regfile(tmpdir, file_path+".reg", arch) + + +####################################### +# Command line options + +options = {'register': False, 'nocleanup': False, 'nodll': False, 'noreg': False, 'stripdllpath': False, 'debug': False} + +def parse_command_line_opts(options): + app_argv = list(sys.argv) + for opt_key in options.keys(): + opt_command = "--%s" % opt_key + if opt_command in app_argv: + options[opt_key] = True + app_argv.remove(opt_command) + return app_argv + + +####################################### +# Main + +if __name__ == '__main__': + app_argv = parse_command_line_opts(options) + # sanity checks + if len(app_argv) < 3: + print("usage:") + print(" installcab.py [options] cabfile component [wineprefix_path]") + print("") + print("example:") + print(" installcab.py ~/.cache/winetricks/win7sp1/windows6.1-KB976932-X86.exe wmvdecod") + print("") + print("options:") + print(" --nocleanup: do not perform cleanup") + print(" --noreg: do not import registry entries") + print(" --nodll: do not install dlls into system dir") + print(" --register: register dlls with regsrv32") + print(" --stripdllpath: strip full path for dlls in registry so wine can find them anywhere") + sys.exit(0) + + if len(app_argv) < 4 and not "WINEPREFIX" in os.environ: + bad_exit("You need to set WINEPREFIX for this to work!") + + # setup + if len(app_argv) < 4: + wineprefix = os.environ["WINEPREFIX"] + else: + wineprefix = app_argv[3] + + winearch = check_wineprefix_arch(wineprefix) + + system32_path = os.path.join(wineprefix, 'drive_c', 'windows', 'system32') + syswow64_path = os.path.join(wineprefix, 'drive_c', 'windows', 'syswow64') + + tmpdir = tempfile.mkdtemp() + cabfile = app_argv[1] + component = app_argv[2] + + # processing + output_files = extract_from_installer(cabfile, tmpdir, component) + process_files(output_files) + + # clean up + cleanup()