Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var FS = map[string][]byte{ "/requirements.txt": []byte(`forensicstore>=0.17.0,<0.18.0 `), "/scripts/elementary-hotfixes.py": []byte(`#!/usr/bin/env python # Copyright (c) 2019 Siemens AG # # Permission is hereby granted, free of charge, to any person obtaining a copy of # this software and associated documentation files (the "Software"), to deal in # the Software without restriction, including without limitation the rights to # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of # the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Author(s): Demian Kellermann """ This plugin parses different registry entries for installed Hotfixes (patches) to the Windows system as well as to other software components """ import json import logging import re import struct import sys from collections import defaultdict from datetime import datetime import forensicstore import storeutil LOGGER = logging.getLogger(__name__) HOTFIX_PATHS_INSTALLER = [ 'hkey_local_machine\\software\\microsoft\\windows\\currentversion\\component based servicing\\packages\\', ] HOTFIX_PATHS_ADDITIONAL = [ 'hkey_local_machine\\software\\wow6432node\\microsoft\\updates\\', 'hkey_local_machine\\software\\microsoft\\updates\\', ] KB_REGEX = re.compile(r'KB\d+') def _analyze_installer(obj): entries = [] installer_entries = defaultdict(set) hotfix_infos = {v["name"].lower(): v["data"] for v in obj["values"]} if hotfix_infos.get('InstallClient') != 'WindowsUpdateAgent': return [] hotfix = KB_REGEX.search(obj["key"].split('\\')[-1]) if not hotfix: # some entries do not have the KB number in the title, but something like "RollupFix", check # the InstallLocation value in this case location = hotfix_infos.get('InstallLocation') if location: hotfix = KB_REGEX.search(location) if not hotfix: LOGGER.info("Non KB entry for WindowsUpdateAgent found: %s", obj["key"]) return [] install_high = hotfix_infos.get('InstallTimeHigh') install_low = hotfix_infos.get('InstallTimeLow') if install_high and install_low: timestamp = filetime_to_timestamp(filetime_join(install_high, install_low)) else: timestamp = '' installer_entries[hotfix.group(0)].add(timestamp) for hotfix in installer_entries: entries.append({ 'Hotfix': hotfix, 'Installed': sorted(installer_entries[hotfix])[0] if installer_entries[hotfix] else '-', 'Source': 'Component Based Servicing', "type": "hotfix" }) return entries def _analyze_additional(key): hotfix = key["key"].split('\\')[-1] product = key["key"].split('\\')[-2] return [{ 'Hotfix': hotfix, 'Installed': key["modified_time"], 'Source': 'Microsoft Updates', 'Component': product, "type": "hotfix" }] def transform(obj): if any(map(lambda path: obj["key"].lower().startswith(path), HOTFIX_PATHS_INSTALLER)): return _analyze_installer(obj) if any(map(lambda path: obj["key"].lower().startswith(path), HOTFIX_PATHS_ADDITIONAL)): return _analyze_additional(obj) return [] def filetime_join(upper, lower): """ :param upper: upper part of the number :param lower: lower part of the number """ return struct.unpack('Q', struct.pack('ii', lower, upper))[0] def filetime_to_timestamp(filetime_64): """ The FILETIME timestamp is a 64-bit integer that contains the number of 100th nano seconds since 1601-01-01 00:00:00. The number is usually saved in the registry using two DWORD["values"] :return: string of UTC time """ # pylint: disable=invalid-name HUNDREDS_OF_NANOSECONDS_IN_A_SECOND = 10000000 UNIXEPOCH_AS_FILETIME = 116444736000000000 datetime_stamp = datetime.utcfromtimestamp( (filetime_64 - UNIXEPOCH_AS_FILETIME) / HUNDREDS_OF_NANOSECONDS_IN_A_SECOND) return datetime_stamp.isoformat() def main(url): print(json.dumps({"header": ["Hotfix", "Installed", "Source", "Component"]})) LOGGER.debug("search for hotfixes") store = forensicstore.open(url) hklmsw = "HKEY_LOCAL_MACHINE\\SOFTWARE\\" conditions = [ {'key': hklmsw + "Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\Packages\\%"}, {'key': hklmsw + "WOW6432Node\\Microsoft\\Updates\\%\\%"}, {'key': hklmsw + "Microsoft\\Updates\\%\\%"} ] for item in store.select(conditions): for result in transform(item): print(json.dumps(result)) store.close() if __name__ == '__main__': logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) parser = storeutil.ScriptArgumentParser( 'hotfixes', description='Process windows hotfixes', store_arg=True, filter_arg=False, ) args, _ = parser.parse_known_args(sys.argv[1:]) main(args.forensicstore) `), "/scripts/elementary-hotfixes.py.info": []byte(`{"Use": "hotfixes <forensicstore>", "Short": "Process windows hotfixes"} `), "/scripts/elementary-networking.py": []byte(`#!/usr/bin/env python # Copyright (c) 2019 Siemens AG # # Permission is hereby granted, free of charge, to any person obtaining a copy of # this software and associated documentation files (the "Software"), to deal in # the Software without restriction, including without limitation the rights to # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of # the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Author(s): Demian Kellermann import json import sys import forensicstore import storeutil NAMES_KEY = r"\control\network\{4d36e972-e325-11ce-bfc1-08002be10318}" INTERFACE_KEY = r'\services\tcpip\parameters\interfaces' def transform(objs): """ This plugin parses network configuration from the SYSTEM hive and correlates it with other keys to get the network interface name and the last configuration """ names_keys = [] interface_keys = [] for obj in objs: key = obj["key"].lower() if NAMES_KEY in key: names_keys.append(obj) elif INTERFACE_KEY in key: interface_keys.append(obj) results = [] for interface_key in interface_keys: result = {} interface_id = interface_key["key"].split('\\')[-1] interface_infos = {} # create a map from values if "values" in interface_key: # return interface_key["values"] interface_infos = { v["name"].lower(): v["data"] if "data" in v else "" for v in interface_key["values"] } missing_value = '(not specified)' result['type'] = "known_network" result['GUID'] = interface_id result['DHCP'] = 'yes' if interface_infos.get( 'enabledhcp', missing_value) == 1 else 'no' result['IPs'] = interface_infos.get('ipaddress', missing_value) result['SubNetMask'] = interface_infos.get('subnetmask', missing_value) result['NameServer'] = interface_infos.get('nameserver', missing_value) result['IP Key Changed'] = interface_key['modified_time'] # search names key name_key = None for key in names_keys: if interface_id.lower() in key["key"].lower(): name_key = key if name_key is not None: # create a map from values name_infos = { v["name"].lower(): v["data"] if "data" in v else "" for v in name_key["values"] } result['Network Key Changed'] = name_key["modified_time"] result['Friendly Name'] = name_infos["name"] else: result['Network Key Changed'] = missing_value result['Friendly Name'] = missing_value results.append(result) return results def main(url): print(json.dumps({ "header": ["GUID", "DHCP", "IPs", "SubNetMask", "NameServer", "IP Key Changed", "Network Key Changed", "Friendly Name"]})) store = forensicstore.open(url) conditions = [ {'key': r"HKEY_LOCAL_MACHINE\SYSTEM\%ControlSet%\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\%"}, {'key': r"HKEY_LOCAL_MACHINE\SYSTEM\%ControlSet%\Services\Tcpip\Parameters\Interfaces\%"} ] items = store.select(conditions) for result in transform(items): print(json.dumps(result)) store.close() if __name__ == '__main__': parser = storeutil.ScriptArgumentParser( 'networking', description='Process networking artifacts', store_arg=True, filter_arg=False, ) args, _ = parser.parse_known_args(sys.argv[1:]) main(args.forensicstore) `), "/scripts/elementary-networking.py.info": []byte(`{"Use": "networking <forensicstore>", "Short": "Process windows network interfaces"} `), "/scripts/elementary-runkeys.py": []byte(`#!/usr/bin/env python # Copyright (c) 2019 Siemens AG # # Permission is hereby granted, free of charge, to any person obtaining a copy of # this software and associated documentation files (the "Software"), to deal in # the Software without restriction, including without limitation the rights to # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of # the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Author(s): Jonas Plum import json import sys import forensicstore import storeutil def transform(items): results = [] for item in items: if "values" in item: for value in item["values"]: results.append({ "Key": item["key"], "Command": value["data"], "Name": value["name"], "SID": item["key"].split("\\")[1], "type": "runkey", }) return results def main(url): print(json.dumps({"header": ["Name", "Command", "SID", "Key"]})) store = forensicstore.open(url) hklmsw = "HKEY_LOCAL_MACHINE\\Software\\" hkusw = "HKEY_USERS\\%\\Software\\" conditions = [ {'key': hklmsw + r"Microsoft\Windows\CurrentVersion\Policies\Explorer\Run"}, {'key': hklmsw + r"Microsoft\Windows\CurrentVersion\Run"}, {'key': hklmsw + r"Microsoft\Windows\CurrentVersion\RunOnce"}, {'key': hklmsw + r"Microsoft\Windows\CurrentVersion\RunOnce\Setup"}, {'key': hklmsw + r"Microsoft\Windows\CurrentVersion\RunOnceEx"}, {'key': hklmsw + r"Wow6432Node\Microsoft\Windows\CurrentVersion\Run"}, {'key': hklmsw + r"Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce"}, {'key': hklmsw + r"Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce\Setup"}, {'key': hklmsw + r"Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnceEx"}, {'key': hklmsw + r"Wow6432Node\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run"}, {'key': hkusw + r"Microsoft\Windows\CurrentVersion\Policies\Explorer\Run"}, {'key': hkusw + r"Microsoft\Windows\CurrentVersion\Run"}, {'key': hkusw + r"Microsoft\Windows\CurrentVersion\RunOnce"}, {'key': hkusw + r"Microsoft\Windows\CurrentVersion\RunOnce\Setup"}, {'key': hkusw + r"Microsoft\Windows\CurrentVersion\RunOnceEx"}, {'key': hkusw + r"Wow6432Node\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run"}, {'key': hkusw + r"Wow6432Node\Microsoft\Windows\CurrentVersion\Run"}, {'key': hkusw + r"Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce"}, {'key': hkusw + r"Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce\Setup"}, {'key': hkusw + r"Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnceEx"} ] items = store.select(conditions) for result in transform(items): print(json.dumps(result)) store.close() if __name__ == '__main__': parser = storeutil.ScriptArgumentParser( 'run-keys', description='Process windows run keys', store_arg=True, filter_arg=False, ) args, _ = parser.parse_known_args(sys.argv[1:]) main(args.forensicstore) `), "/scripts/elementary-runkeys.py.info": []byte(`{"Use": "run-keys <forensicstore>", "Short": "Process windows run keys"} `), "/scripts/elementary-services.py": { 0x23, 0x21, 0x2f, 0x75, 0x73, 0x72, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x65, 0x6e, 0x76, 0x20, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x0a, 0x23, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x39, 0x20, 0x53, 0x69, 0x65, 0x6d, 0x65, 0x6e, 0x73, 0x20, 0x41, 0x47, 0x0a, 0x23, 0x0a, 0x23, 0x20, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x68, 0x65, 0x72, 0x65, 0x62, 0x79, 0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x2c, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x6f, 0x62, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x23, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x20, 0x28, 0x74, 0x68, 0x65, 0x20, 0x22, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x22, 0x29, 0x2c, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x0a, 0x23, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x0a, 0x23, 0x20, 0x75, 0x73, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x2c, 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x2c, 0x20, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x2c, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x2c, 0x20, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x73, 0x65, 0x6c, 0x6c, 0x20, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x0a, 0x23, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x68, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x66, 0x75, 0x72, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x6f, 0x20, 0x73, 0x6f, 0x2c, 0x0a, 0x23, 0x20, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x0a, 0x23, 0x0a, 0x23, 0x20, 0x54, 0x68, 0x65, 0x20, 0x61, 0x62, 0x6f, 0x76, 0x65, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x20, 0x73, 0x68, 0x61, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x6c, 0x6c, 0x0a, 0x23, 0x20, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x62, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2e, 0x0a, 0x23, 0x0a, 0x23, 0x20, 0x54, 0x48, 0x45, 0x20, 0x53, 0x4f, 0x46, 0x54, 0x57, 0x41, 0x52, 0x45, 0x20, 0x49, 0x53, 0x20, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x44, 0x20, 0x22, 0x41, 0x53, 0x20, 0x49, 0x53, 0x22, 0x2c, 0x20, 0x57, 0x49, 0x54, 0x48, 0x4f, 0x55, 0x54, 0x20, 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59, 0x20, 0x4f, 0x46, 0x20, 0x41, 0x4e, 0x59, 0x20, 0x4b, 0x49, 0x4e, 0x44, 0x2c, 0x20, 0x45, 0x58, 0x50, 0x52, 0x45, 0x53, 0x53, 0x20, 0x4f, 0x52, 0x0a, 0x23, 0x20, 0x49, 0x4d, 0x50, 0x4c, 0x49, 0x45, 0x44, 0x2c, 0x20, 0x49, 0x4e, 0x43, 0x4c, 0x55, 0x44, 0x49, 0x4e, 0x47, 0x20, 0x42, 0x55, 0x54, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x45, 0x44, 0x20, 0x54, 0x4f, 0x20, 0x54, 0x48, 0x45, 0x20, 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x49, 0x45, 0x53, 0x20, 0x4f, 0x46, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41, 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x2c, 0x20, 0x46, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x0a, 0x23, 0x20, 0x46, 0x4f, 0x52, 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55, 0x52, 0x50, 0x4f, 0x53, 0x45, 0x20, 0x41, 0x4e, 0x44, 0x20, 0x4e, 0x4f, 0x4e, 0x49, 0x4e, 0x46, 0x52, 0x49, 0x4e, 0x47, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x2e, 0x20, 0x49, 0x4e, 0x20, 0x4e, 0x4f, 0x20, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x20, 0x53, 0x48, 0x41, 0x4c, 0x4c, 0x20, 0x54, 0x48, 0x45, 0x20, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x53, 0x20, 0x4f, 0x52, 0x0a, 0x23, 0x20, 0x43, 0x4f, 0x50, 0x59, 0x52, 0x49, 0x47, 0x48, 0x54, 0x20, 0x48, 0x4f, 0x4c, 0x44, 0x45, 0x52, 0x53, 0x20, 0x42, 0x45, 0x20, 0x4c, 0x49, 0x41, 0x42, 0x4c, 0x45, 0x20, 0x46, 0x4f, 0x52, 0x20, 0x41, 0x4e, 0x59, 0x20, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x2c, 0x20, 0x44, 0x41, 0x4d, 0x41, 0x47, 0x45, 0x53, 0x20, 0x4f, 0x52, 0x20, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x20, 0x4c, 0x49, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x2c, 0x20, 0x57, 0x48, 0x45, 0x54, 0x48, 0x45, 0x52, 0x0a, 0x23, 0x20, 0x49, 0x4e, 0x20, 0x41, 0x4e, 0x20, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x20, 0x4f, 0x46, 0x20, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x54, 0x2c, 0x20, 0x54, 0x4f, 0x52, 0x54, 0x20, 0x4f, 0x52, 0x20, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x57, 0x49, 0x53, 0x45, 0x2c, 0x20, 0x41, 0x52, 0x49, 0x53, 0x49, 0x4e, 0x47, 0x20, 0x46, 0x52, 0x4f, 0x4d, 0x2c, 0x20, 0x4f, 0x55, 0x54, 0x20, 0x4f, 0x46, 0x20, 0x4f, 0x52, 0x20, 0x49, 0x4e, 0x0a, 0x23, 0x20, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x20, 0x57, 0x49, 0x54, 0x48, 0x20, 0x54, 0x48, 0x45, 0x20, 0x53, 0x4f, 0x46, 0x54, 0x57, 0x41, 0x52, 0x45, 0x20, 0x4f, 0x52, 0x20, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x20, 0x4f, 0x52, 0x20, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x20, 0x44, 0x45, 0x41, 0x4c, 0x49, 0x4e, 0x47, 0x53, 0x20, 0x49, 0x4e, 0x20, 0x54, 0x48, 0x45, 0x20, 0x53, 0x4f, 0x46, 0x54, 0x57, 0x41, 0x52, 0x45, 0x2e, 0x0a, 0x23, 0x0a, 0x23, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x28, 0x73, 0x29, 0x3a, 0x20, 0x44, 0x65, 0x6d, 0x69, 0x61, 0x6e, 0x20, 0x4b, 0x65, 0x6c, 0x6c, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x6e, 0x0a, 0x0a, 0x22, 0x22, 0x22, 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x20, 0x22, 0x22, 0x22, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x6a, 0x73, 0x6f, 0x6e, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x73, 0x79, 0x73, 0x0a, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x65, 0x6e, 0x73, 0x69, 0x63, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x75, 0x74, 0x69, 0x6c, 0x0a, 0x0a, 0x0a, 0x64, 0x65, 0x66, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x28, 0x6f, 0x62, 0x6a, 0x73, 0x29, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x20, 0x68, 0x69, 0x76, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x73, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x20, 0x70, 0x79, 0x6c, 0x69, 0x6e, 0x74, 0x3a, 0x20, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x3d, 0x74, 0x6f, 0x6f, 0x2d, 0x6d, 0x61, 0x6e, 0x79, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x73, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x22, 0x6b, 0x65, 0x79, 0x22, 0x5d, 0x2e, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x2e, 0x65, 0x6e, 0x64, 0x73, 0x77, 0x69, 0x74, 0x68, 0x28, 0x27, 0x5c, 0x5c, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x27, 0x29, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x5b, 0x6b, 0x65, 0x79, 0x2e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x28, 0x27, 0x5c, 0x5c, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x27, 0x2c, 0x20, 0x27, 0x27, 0x29, 0x5d, 0x20, 0x3d, 0x20, 0x6f, 0x62, 0x6a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x28, 0x27, 0x5c, 0x5c, 0x27, 0x29, 0x5b, 0x2d, 0x32, 0x5d, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, 0x20, 0x73, 0x6b, 0x69, 0x70, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x73, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x28, 0x6f, 0x62, 0x6a, 0x5b, 0x22, 0x6b, 0x65, 0x79, 0x22, 0x5d, 0x2e, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x28, 0x29, 0x2c, 0x20, 0x4e, 0x6f, 0x6e, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x76, 0x5b, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x2e, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x28, 0x29, 0x3a, 0x20, 0x76, 0x5b, 0x22, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x5b, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x5d, 0x20, 0x69, 0x66, 0x20, 0x22, 0x64, 0x61, 0x74, 0x61, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x5b, 0x22, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x27, 0x27, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x76, 0x5b, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x2e, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x28, 0x29, 0x3a, 0x20, 0x76, 0x5b, 0x22, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x5d, 0x20, 0x69, 0x66, 0x20, 0x22, 0x64, 0x61, 0x74, 0x61, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x74, 0x28, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x28, 0x27, 0x73, 0x74, 0x61, 0x72, 0x74, 0x27, 0x2c, 0x20, 0x30, 0x78, 0x34, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x28, 0x27, 0x74, 0x79, 0x70, 0x65, 0x27, 0x2c, 0x20, 0x4e, 0x6f, 0x6e, 0x65, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x4e, 0x61, 0x6d, 0x65, 0x27, 0x3a, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x22, 0x6b, 0x65, 0x79, 0x22, 0x5d, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x28, 0x22, 0x5c, 0x5c, 0x22, 0x29, 0x5b, 0x2d, 0x31, 0x5d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x3a, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x28, 0x27, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x2c, 0x20, 0x27, 0x27, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x27, 0x3a, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x28, 0x27, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x6e, 0x61, 0x6d, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x27, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x27, 0x3a, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x28, 0x27, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x27, 0x2c, 0x20, 0x27, 0x27, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x27, 0x3a, 0x20, 0x53, 0x54, 0x41, 0x52, 0x54, 0x55, 0x50, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x53, 0x2e, 0x67, 0x65, 0x74, 0x28, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x79, 0x70, 0x65, 0x27, 0x3a, 0x20, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x28, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x61, 0x74, 0x68, 0x27, 0x3a, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x28, 0x27, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x70, 0x61, 0x74, 0x68, 0x27, 0x2c, 0x20, 0x27, 0x27, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x4c, 0x4c, 0x27, 0x3a, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x28, 0x27, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x64, 0x6c, 0x6c, 0x27, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x27, 0x3a, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x22, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x5d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x27, 0x3a, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x27, 0x3a, 0x20, 0x6f, 0x62, 0x6a, 0x5b, 0x22, 0x6b, 0x65, 0x79, 0x22, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x28, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x0a, 0x0a, 0x0a, 0x53, 0x54, 0x41, 0x52, 0x54, 0x55, 0x50, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x53, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x2d, 0x64, 0x65, 0x2f, 0x64, 0x6f, 0x74, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x6d, 0x6f, 0x64, 0x65, 0x3f, 0x76, 0x69, 0x65, 0x77, 0x3d, 0x6e, 0x65, 0x74, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x2d, 0x34, 0x2e, 0x37, 0x2e, 0x32, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x30, 0x3a, 0x20, 0x22, 0x42, 0x6f, 0x6f, 0x74, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x31, 0x3a, 0x20, 0x22, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x32, 0x3a, 0x20, 0x22, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x33, 0x3a, 0x20, 0x22, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x34, 0x3a, 0x20, 0x22, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x0a, 0x7d, 0x0a, 0x0a, 0x23, 0x20, 0x45, 0x6e, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x57, 0x68, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x2c, 0x20, 0x61, 0x20, 0x44, 0x57, 0x4f, 0x52, 0x44, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x0a, 0x23, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x48, 0x4b, 0x45, 0x59, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5c, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5c, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x53, 0x65, 0x74, 0x5c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x5c, 0x3c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3e, 0x2e, 0x20, 0x49, 0x74, 0x27, 0x73, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x20, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x41, 0x75, 0x74, 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x0a, 0x23, 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x73, 0x65, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x31, 0x2c, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x32, 0x20, 0xe2, 0x80, 0x94, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x53, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x2d, 0x64, 0x65, 0x2f, 0x64, 0x6f, 0x74, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x74, 0x79, 0x70, 0x65, 0x3f, 0x76, 0x69, 0x65, 0x77, 0x3d, 0x6e, 0x65, 0x74, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x2d, 0x34, 0x2e, 0x37, 0x2e, 0x32, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x31, 0x3a, 0x20, 0x22, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x32, 0x3a, 0x20, 0x22, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x34, 0x3a, 0x20, 0x22, 0x41, 0x64, 0x61, 0x70, 0x74, 0x65, 0x72, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x38, 0x3a, 0x20, 0x22, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x31, 0x30, 0x3a, 0x20, 0x22, 0x57, 0x69, 0x6e, 0x33, 0x32, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x32, 0x30, 0x3a, 0x20, 0x22, 0x57, 0x69, 0x6e, 0x33, 0x32, 0x53, 0x68, 0x61, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x31, 0x30, 0x30, 0x3a, 0x20, 0x22, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x22, 0x2c, 0x0a, 0x7d, 0x0a, 0x0a, 0x0a, 0x64, 0x65, 0x66, 0x20, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x28, 0x6d, 0x61, 0x73, 0x6b, 0x29, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x74, 0x28, 0x6d, 0x61, 0x73, 0x6b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x62, 0x69, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x53, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x62, 0x69, 0x74, 0x20, 0x26, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x28, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x53, 0x5b, 0x62, 0x69, 0x74, 0x5d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x27, 0x2c, 0x20, 0x27, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x74, 0x79, 0x70, 0x65, 0x73, 0x29, 0x0a, 0x0a, 0x0a, 0x64, 0x65, 0x66, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x75, 0x72, 0x6c, 0x29, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x28, 0x6a, 0x73, 0x6f, 0x6e, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x73, 0x28, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0x3a, 0x20, 0x5b, 0x27, 0x4e, 0x61, 0x6d, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x2c, 0x20, 0x27, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x27, 0x2c, 0x20, 0x27, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x79, 0x70, 0x65, 0x27, 0x2c, 0x20, 0x27, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x61, 0x74, 0x68, 0x27, 0x2c, 0x20, 0x27, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x4c, 0x4c, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x27, 0x2c, 0x20, 0x27, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x27, 0x2c, 0x20, 0x27, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x27, 0x5d, 0x7d, 0x29, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x6f, 0x72, 0x65, 0x6e, 0x73, 0x69, 0x63, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x75, 0x72, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x5b, 0x7b, 0x27, 0x6b, 0x65, 0x79, 0x27, 0x3a, 0x20, 0x22, 0x48, 0x4b, 0x45, 0x59, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x48, 0x49, 0x4e, 0x45, 0x5c, 0x5c, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5c, 0x5c, 0x25, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x53, 0x65, 0x74, 0x25, 0x5c, 0x5c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x5c, 0x5c, 0x25, 0x22, 0x7d, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x28, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x29, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x28, 0x6a, 0x73, 0x6f, 0x6e, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x73, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x29, 0x0a, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x5f, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x5f, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x5f, 0x27, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x73, 0x65, 0x72, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x27, 0x2c, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x27, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x27, 0x2c, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x72, 0x67, 0x3d, 0x54, 0x72, 0x75, 0x65, 0x2c, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x3d, 0x46, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x72, 0x67, 0x73, 0x2c, 0x20, 0x5f, 0x20, 0x3d, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x28, 0x73, 0x79, 0x73, 0x2e, 0x61, 0x72, 0x67, 0x76, 0x5b, 0x31, 0x3a, 0x5d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x61, 0x72, 0x67, 0x73, 0x2e, 0x66, 0x6f, 0x72, 0x65, 0x6e, 0x73, 0x69, 0x63, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x29, 0x0a, }, "/scripts/elementary-services.py.info": []byte(`{"Use": "services <forensicstore>", "Short": "Process windows services"} `), "/scripts/elementary-software.py": []byte(`#!/usr/bin/env python # Copyright (c) 2019 Siemens AG # # Permission is hereby granted, free of charge, to any person obtaining a copy of # this software and associated documentation files (the "Software"), to deal in # the Software without restriction, including without limitation the rights to # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of # the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Author(s): Demian Kellermann """ This plugin parses the Windows Uninstaller registry keys for a list of installed software """ import json import sys import forensicstore import storeutil def transform(obj): uninstall_entry = { "Name": "", "Key Timestamp": obj.get("modified", ""), "Version": "", "Publisher": "", "InstallDate": "", "Source": "", "Location": "", "Uninstall": "", "Key": obj["key"] } if "values" not in obj: return [] uninstall_infos = {v["name"].lower(): v["data"] if "data" in v else "" for v in obj["values"]} uninstall_entry['Name'] = uninstall_infos.get("displayname", '') uninstall_entry['Version'] = uninstall_infos.get("displayversion", '') uninstall_entry['Publisher'] = uninstall_infos.get("publisher", '') if uninstall_infos.get('installdate', None) is not None: strnum = str(uninstall_infos.get('installdate')) uninstall_entry['InstallDate'] = '{}-{}-{}'.format( strnum[0:4], strnum[4:6], strnum[6:8]) else: uninstall_entry['InstallDate'] = '' uninstall_entry['Source'] = uninstall_infos.get('installsource', '') uninstall_entry['Location'] = uninstall_infos.get( 'installlocation', '') uninstall_entry['Uninstall'] = uninstall_infos.get( 'uninstallstring', '') uninstall_entry["type"] = "uninstall_entry" return [uninstall_entry] def main(url): print(json.dumps({ "header": ["Name", "Version", "Publisher", "InstallDate", "Source", "Location", "Uninstall", "Key", "Key Timestamp"]})) store = forensicstore.open(url) conditions = [{ 'key': "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%" }, { 'key': "HKEY_USERS\\%\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%" }] for item in store.select(conditions): results = transform(item) for result in results: print(json.dumps(result)) store.close() if __name__ == '__main__': parser = storeutil.ScriptArgumentParser( 'software', description='Process uninstall entries', store_arg=True, filter_arg=False, ) args, _ = parser.parse_known_args(sys.argv[1:]) main(args.forensicstore) `), "/scripts/elementary-software.py.info": []byte(`{"Use": "software <forensicstore>", "Short": "Process uninstall entries"} `), "/scripts/elementary-usb.py": []byte(`#!/usr/bin/env python # Copyright (c) 2020 Siemens AG # # Permission is hereby granted, free of charge, to any person obtaining a copy of # this software and associated documentation files (the "Software"), to deal in # the Software without restriction, including without limitation the rights to # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of # the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Author(s): Jonas Plum, Korbinian Karl import json import logging import sys import forensicstore import storeutil LOGGER = logging.getLogger(__name__) class USBForensicStoreExtractor: # pylint: disable=fixme,too-few-public-methods def __init__(self, store): self.artifact_names = ["WindowsUSBDeviceInformations", "WindowsUSBUserMountedDevices", "WindowsUSBVolumeAndDriveMapping"] self.forensicstore = store # self.filter = store_filter def get_usb_usage_data(self): """ Collects all kinds of data about usb devices and their usage on a given forensicstore. :return: A list containing dicts about used usb devices """ system_mounted_usb_info = self._get_system_usb_data() usb_user_mounted_devices = self._get_user_usb_data(system_mounted_usb_info) return self._get_all_usb_data(usb_user_mounted_devices) def _get_system_usb_data(self): system_mounted_usb_info = {} usb_system_mounted_devices = list(self.forensicstore.select([{"artifact": "WindowsUSBVolumeAndDriveMapping"}])) if not usb_system_mounted_devices: return {} usb_system_mounted_devices = [device.get('values') for device in usb_system_mounted_devices].pop() # Collects some information about the system mounted usb devices for mapping in usb_system_mounted_devices: splitted_values = mapping.get("name").split("\\").pop() if splitted_values.startswith("Volume"): volume_guid = splitted_values.replace("Volume", '') try: device_details = bytes.fromhex(mapping.get("data")).decode("utf16") if device_details and device_details.startswith("_??_USBSTOR#Disk"): cleaned_details = device_details.split("&") vendor_name = cleaned_details[1] product_name = cleaned_details[2] usb_revision = cleaned_details[3].split("#")[0] usb_serial_number = cleaned_details[3].split("#")[1] system_mounted_usb_info.update({volume_guid: {"vendor_name": vendor_name.replace("Ven_", ""), "product_name": product_name.replace("Prod_", ""), "usb_revision": usb_revision.replace("Rev_", ""), "usb_uid": usb_serial_number, }}) except UnicodeError: pass return system_mounted_usb_info def _get_user_usb_data(self, system_mounted_usb_info: dict): usb_user_mounted_data = self.forensicstore.select([{"artifact": "WindowsUSBUserMountedDevices"}]) # Categorises found system usb usage to user usb usage. usb_user_mounted_devices = [] for device in usb_user_mounted_data: splitted_reg_key = device.get("key").split("\\") rear_reg_key = splitted_reg_key[-1] if rear_reg_key.startswith("{") and rear_reg_key in system_mounted_usb_info: usb_dict = system_mounted_usb_info[rear_reg_key] usb_dict.update({"volume_guid": rear_reg_key.replace("{", "").replace("}", "")}) user_sid = splitted_reg_key[1] usb_dict.update({"user_sid": user_sid}) usb_user_mounted_devices.append(usb_dict) return usb_user_mounted_devices def _get_all_usb_data(self, usb_user_mounted_devices: list): usb_device_information = self.forensicstore.select([{"artifact": "WindowsUSBDeviceInformations"}]) # Combines the gathered information to create a dictionary of actual used usb devices and some meta data. # Also keeps track about non mounted usb devices. mounted_device_ids = [device.get("usb_uid") for device in usb_user_mounted_devices] for device in usb_device_information: splitted_reg_key = device.get("key").split("/") # TODO: Can probably done better through a regex. if len(splitted_reg_key) == 7: device_id = splitted_reg_key[-1].split("&")[0] if device_id in mounted_device_ids: for user_mounted_device in usb_user_mounted_devices: if device_id == user_mounted_device.get("usb_uid"): friendly_name = [value.get("data") for value in device.get("values") if value.get("name") == "FriendlyName"].pop() user_mounted_device.update({"friendly_name": friendly_name}) user_mounted_device.update(self._get_first_insert_timestamps(device_id)) else: usb_revision = splitted_reg_key[5].split("&")[3].replace("Rev_", "") device_dict = {"vendor_name": None, "product_name": None, "usb_revision": usb_revision, "usb_uid": device_id, "volume_guid": None, "user_sid": None} device_dict.update(self._get_first_insert_timestamps(device_id)) for value in device.get("values"): if value.get("name") == "FriendlyName": device_dict.update({"friendly_name": value.get("data")}) usb_user_mounted_devices.append(device_dict) return usb_user_mounted_devices def _get_first_insert_timestamps(self, device_id): items = self.forensicstore.select([{"name": "setupapi.dev.log"}]) # fsf = self.forensicstore.remote_fs.open("WindowsDeviceSetup/setupapi.dev.log", mode='rb') for item in items: if "export_path" not in item: continue fsf = self.forensicstore.fs.open(item["export_path"], mode='rt') inital_timestamp = {"first_insert": None} if device_id: # Checks each line for the first insert timestamp in setupapi.dev.log for line in fsf: try: log_line = line.decode("utf-8") if "Device Install (Hardware initiated)" in log_line and device_id in log_line: splitted_log_line = next(fsf).decode("UTF-8").split(' ') date = splitted_log_line.pop().replace("\r\n", "") time = splitted_log_line.pop() inital_timestamp = {"first_insert": date + " " + time} break except UnicodeDecodeError: pass return inital_timestamp @staticmethod def _get_all_insert_timestamps(): # TODO get all insert timestamps via the last change timestamp of the usb UID. # fs = self.forensicstore return [] def main(url): print(json.dumps({ "header": ["vendor_name", "product_name", "usb_revision", "usb_uid", "volume_guid", "user_sid"], })) LOGGER.debug("process usb") store = forensicstore.open(url) usb_usage_data = USBForensicStoreExtractor(store).get_usb_usage_data() for result in usb_usage_data: result["type"] = "usb-device" print(json.dumps(result)) store.close() if __name__ == '__main__': logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) parser = storeutil.ScriptArgumentParser( 'usb', description='Process usb artifacts', store_arg=True, filter_arg=False, ) args, _ = parser.parse_known_args(sys.argv[1:]) main(args.forensicstore) `), "/scripts/elementary-usb.py.info": []byte(`{ "Use": "usb <forensicstore>", "Short": "Process windows usb artifacts" } `), "/scripts/storeutil.py": []byte(`# Copyright (c) 2020 Siemens AG # # Permission is hereby granted, free of charge, to any person obtaining a copy of # this software and associated documentation files (the "Software"), to deal in # the Software without restriction, including without limitation the rights to # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of # the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Author(s): Jonas Plum import argparse def merge_conditions(list_a, list_b): if list_a is None: return list_b if list_b is None: return list_a list_c = [] for item_a in list_a: for item_b in list_b: list_c.append({**item_a, **item_b}) return list_c class DictListAction(argparse.Action): # pylint: disable=too-few-public-methods def __call__(self, parser, namespace, values, option_string=None): flag = {} for kv in values.split(","): key, value = kv.split("=") flag[key] = value if hasattr(namespace, self.dest): flags = getattr(namespace, self.dest) if flags is not None: flags.append(flag) setattr(namespace, self.dest, flags) return setattr(namespace, self.dest, [flag]) class ScriptArgumentParser(argparse.ArgumentParser): def __init__(self, subcommand, store_arg, filter_arg, *args, **kwargs): super().__init__(*args, **kwargs) self.subcommand = subcommand if store_arg: self.add_argument('forensicstore', type=str, help='the processed forensicstore') if filter_arg: self.add_argument( "--filter", dest="filter", action=DictListAction, metavar="type=file,name=System.evtx...", help="filter processed items") `), }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.